diff --git a/.changeset/supported-delivery-types.md b/.changeset/supported-delivery-types.md new file mode 100644 index 0000000000..2819467507 --- /dev/null +++ b/.changeset/supported-delivery-types.md @@ -0,0 +1,5 @@ +--- +"adcontextprotocol": minor +--- + +Add `media_buy.supported_delivery_types` to `get_adcp_capabilities`. A seller declares whether it sells `guaranteed` inventory, `non_guaranteed` inventory, or both, mirroring `supported_pricing_models`, so buyers can filter sellers before calling `get_products`. Like every capability declaration, it is a commitment: a declaring seller returns only products with a listed `delivery_type`. When absent, buyers treat the seller's delivery types as unknown. diff --git a/docs/building/by-layer/L1/security.mdx b/docs/building/by-layer/L1/security.mdx index 845373870f..7f57b75f60 100644 --- a/docs/building/by-layer/L1/security.mdx +++ b/docs/building/by-layer/L1/security.mdx @@ -2557,7 +2557,7 @@ schema-valid. The annotations are registered as a 4.0 tightening in the [4.0 compatibility checks](/docs/reference/migration/v4-compatibility#capabilities-stay-within-their-structural-bounds). **Worst case.** With the 4.0 bounds applied, the largest schema-valid -capabilities document is 99,101,026 bytes (about 95 MiB), counting only the +capabilities document is 99,101,090 bytes (about 95 MiB), counting only the members the schema declares. `scripts/capabilities-structural-bounds.cjs` computes it and `tests/capabilities-structural-bounds.test.cjs` pins it, so the figure moves only when a declared member or limit changes. The calculation assumes every declared member is diff --git a/docs/protocol/capabilities-explorer.mdx b/docs/protocol/capabilities-explorer.mdx index cc5e7ee0db..6fcf6141fc 100644 --- a/docs/protocol/capabilities-explorer.mdx +++ b/docs/protocol/capabilities-explorer.mdx @@ -79,6 +79,7 @@ The largest domain. Sub-namespaces are where most media-buying flags belong. - **`portfolio`** — portfolio-management capabilities. - **`reporting_delivery_methods`** — how reporting is delivered. - **`supported_pricing_models`** — array (CPM, CPC, CPCV, CPP, revenue share, flat rate, etc.). +- **`supported_delivery_types`** — array of `guaranteed` and/or `non_guaranteed` (3.3). When declared, every product carries a listed `delivery_type`. [Propose a flag for `media_buy.features`](https://github.com/adcontextprotocol/adcp/issues/new?title=Add+media_buy+feature:+%3Cflag%3E&body=Schema+location:+%60.media_buy.features%60%0A%0A%23%23+Proposal%0A%0A...%0A%0A%23%23+Why+a+feature+flag+and+not+a+new+task%0A%0A...%0A%0A%23%23+Conformance+probe%0A%0AHow+can+a+buyer+verify+this+capability+is+actually+honored?+...&labels=rfc,capabilities) diff --git a/docs/reference/migration/3-2-to-3-3.mdx b/docs/reference/migration/3-2-to-3-3.mdx index 909a55dff3..92dca2fb11 100644 --- a/docs/reference/migration/3-2-to-3-3.mdx +++ b/docs/reference/migration/3-2-to-3-3.mdx @@ -704,6 +704,7 @@ The field is stable in 3.3, not experimental. It completes the basis vocabulary ### Catalogs, signing, and extensions +- **Delivery types.** Declare `media_buy.supported_delivery_types` with the delivery types you sell, and return only products whose `delivery_type` is listed. Buyers treat an absent declaration as unknown, not as an empty set. - **Catalog ingestion.** Declare `media_buy.features.catalog_ingestion` with `accepted_catalog_types`, `ingestion_modes`, and `item_status_reporting` (required), plus `supported_feed_formats` whenever `feed_url` is listed. Declaring it requires `catalog_management: true`. Reject an unlisted catalog type, mode, or feed format, or an explicit `content_id_type` outside a declared list, with `UNSUPPORTED_FEATURE` before mutation, including on `dry_run`; inline items beyond `max_inline_items_per_request` fail with `INVALID_REQUEST`. Buyers treat an absent declaration as unknown, not unrestricted. See [`sync_catalogs`](/docs/media-buy/task-reference/sync_catalogs#ingestion-compatibility). - **`request_signing.operation_sources`.** List `mcp_tools_call` and `a2a_invocation_skill` only for the sources your verifier implements and passes vectors for, and list every source you support: an array without `a2a_invocation_skill` tells peers you do not enforce over A2A. See [above](#a2a-operation-resolution-hardening-becomes-must). - **`trust.json` (experimental).** `trust/v1/trust.json` and `trust/v1/trust-acknowledgements.json` are published under `identity.trust_json` (#8067). An agent is identified by the canonical origin of its URL, one agent per origin, and the record carries no keys (#8121). In 3.3 its reader is the experimental Web Bot Auth profile, which derives the record from the agent's origin, and grants are read at `sync_accounts`. Publishers declare nothing. See [trust.json](/docs/reference/trust-json) and the [agent identity migration guide](/docs/reference/migration/agent-identity-3-3). diff --git a/docs/reference/whats-new-in-3-3.mdx b/docs/reference/whats-new-in-3-3.mdx index 115ecdd537..425c862e3e 100644 --- a/docs/reference/whats-new-in-3-3.mdx +++ b/docs/reference/whats-new-in-3-3.mdx @@ -204,6 +204,7 @@ Two more guarantees loosen here, and the response-size default a third ([below]( - **Signal readiness (experimental).** `get_signals` results can carry `signal_status` (`processing`, `ready`, `failed`) and the `source_audience_id` a custom segment was built from. Feature id `signals.signal_lifecycle`. See [Custom segments built from a seed audience](/docs/signals/tasks/get_signals#custom-segments-built-from-a-seed-audience). - **Event source archive.** A seller that declares `media_buy.event_source_archive` accepts `archived` on `sync_event_sources` items to archive or restore a source without losing its configuration. See [Archiving an event source](/docs/media-buy/task-reference/sync_event_sources#archiving-an-event-source). - **Creative and brand.** `list_creatives` echoes creative-level `provenance`; brand.json gains a brand-wide `prohibited_terms` list; rights constraints can carry advisory `channels[]` and `excluded_channels[]` (experimental, `brand.rights_lifecycle`); and the HTML-markup legacy formats project to `display_tag` inline markup instead of `html5`. +- **Delivery types.** `media_buy.supported_delivery_types` declares whether a seller sells `guaranteed` inventory, `non_guaranteed` inventory, or both, so buyers can skip sellers that don't sell what they're buying before calling `get_products`. A declaring seller returns only products with a listed `delivery_type`. - **The `ext.adcp` namespace.** `ext.adcp` is a registered, AdCP-owned extension namespace. Its first member is an optional `opportunity` binding for cooperating 3.2 `get_products` integrations. See [AdCP opportunity extension](/docs/building/by-layer/L2/context-sessions#adcp-opportunity-extension). ## Bounded agent responses diff --git a/static/schemas/source/protocol/get-adcp-capabilities-response.json b/static/schemas/source/protocol/get-adcp-capabilities-response.json index 92111770e1..f58002827d 100644 --- a/static/schemas/source/protocol/get-adcp-capabilities-response.json +++ b/static/schemas/source/protocol/get-adcp-capabilities-response.json @@ -1775,6 +1775,16 @@ "minItems": 1, "uniqueItems": true }, + "supported_delivery_types": { + "x-added-in": "3.3.0", + "type": "array", + "description": "Delivery types this seller sells across its product portfolio: guaranteed (reserved inventory) and/or non_guaranteed. Buyers can use this for pre-flight filtering before querying individual products. Like every capability declaration, it is a commitment: the products this seller returns carry a delivery_type listed here. When absent, the seller has not declared its delivery types, and buyers treat them as unknown rather than as an empty set. Values come from the product delivery_type enum (guaranteed, non_guaranteed); this is unrelated to the display_tag format parameter of the same name, which lists creative serving modes such as inline_markup.", + "items": { + "$ref": "/schemas/enums/delivery-type.json" + }, + "minItems": 1, + "uniqueItems": true + }, "buying_modes": { "type": "array", "description": "Buying modes this seller supports on get_products. 'brief' (semantic discovery driven by the brief) is universally supported and implicit. 'wholesale' (raw wholesale product feed enumeration — caller omits brief and the seller returns the full priced product feed, paginated) is opt-in and SHOULD be declared explicitly so buyers can probe before issuing wholesale calls. 'refine' lets buyers iterate on prior products/proposals and is also the vehicle for finalizing draft proposals when the seller returns them. Sellers MAY declare ['brief', 'wholesale'] to signal wholesale support; absent declaration is treated as ['brief'] for wholesale-feed probing purposes and sellers MAY return INVALID_REQUEST for wholesale calls they do not support. Symmetric with signals.discovery_modes.", diff --git a/tests/capabilities-structural-bounds.test.cjs b/tests/capabilities-structural-bounds.test.cjs index 7dc70ee4e0..e0829aea5c 100644 --- a/tests/capabilities-structural-bounds.test.cjs +++ b/tests/capabilities-structural-bounds.test.cjs @@ -27,7 +27,7 @@ const read = (...p) => fs.readFileSync(path.join(ROOT, ...p), 'utf8'); const security = read('docs/building/by-layer/L1/security.mdx'); // The documented worst case. Update security.mdx with this number. -const DOCUMENTED_WORST_CASE_BYTES = 99_101_026; +const DOCUMENTED_WORST_CASE_BYTES = 99_101_090; const DOCUMENTED_OPEN_POSITIONS = 190; const rows = bounds.coverage();