Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MIGRATION_v9_to_v10.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ the SDK-relevant inventory is [docs/adcp-3.3-sdk-inventory.md](docs/adcp-3.3-sdk
| Permissive adagents.json resolution requires the fetched domain | `resolve_properties_for_agent(doc, agent_url, mode="permissive")` raises `ValueError` without `publisher_domain=`. Pass the domain the document was fetched for: a bare entry's top-level properties are then kept only when every identifier is a `type: "domain"` under that domain, so a file on another host can no longer claim a publisher's site, app bundle or DOOH venue through a bare entry. Properties identified by bundle, store or venue IDs, and properties whose `publisher_domain` names another domain, are dropped from the bare fallback. `allow_any_domain=True` restores the SDK 9 result. Typed selectors and strict mode are unchanged. See [docs/adagents-discovery.md](docs/adagents-discovery.md) for `properties_for_publisher_domain` and `verify_cross_domain_properties`. |
| Generated enums name each value once | The `_1` alias members codegen emitted when an `anyOf` arm repeated an enum value (`System.zip_1`, `Field17.ad_request_url_1`, 19 enums on 10.0.0b4) are gone; each value has one member, the first-declared one. Code that referenced an alias name (`System.zip_1`) must use the real member (`System.zip`); `System("zip")` and `member.value` are unchanged. |
| `list_account_changes` pages are checked and projected per caller | AdCP 3.3 requires non-empty `changed_paths` on every `adcp` / `seller_operator` change record (`[""]` for a whole-resource create, delete or purge) and lets the originating request's `idempotency_key` reach only the principal that sent it. A handler on 3.3 or later that returns such a record without `changed_paths` now fails the call with `VALIDATION_ERROR` (`field` `/changes/N/changed_paths`), whatever the validation mode; and every record loses `idempotency_key` unless it is an `adcp` record built with `adcp.account_change_feed.attribute_account_change(record, originator)` whose originator is the caller (`ToolContext.tenant_id` + `caller_identity`). Persist the originator when the mutation commits and attribute records when serving the page. `WebhookSender.prepare_account_change_recorded` / `send_account_change_recorded` also accept the change record itself (with `subscriber_id=`), refuse one without `changed_paths`, and never carry its `idempotency_key`. |
| `get_adcp_capabilities` reports `adcp.implementation` | `serve()` (and `adcp.decisioning.serve()`) now adds AdCP 3.3 `adcp.implementation.sdks[]` to capabilities served at 3.3 or later: one entry `{"name": "adcp", "version": <installed adcp version>, "components": [...]}`, listing only components the server provably wires agent-wide (`transport` always; `types` for Decisioning platforms; `schema_validation` with strict request and response validation and no `2.5` in `supported_versions`; `authentication` with `auth=BearerTokenAuth(...)` and no widened discovery set; `idempotency` when every advertised mutating task is `@IdempotencyStore.wrap`ped; `webhooks` / `signing` only when the SDK owns and signs every outbound webhook; `lifecycle` never automatically). A 3.0 to 3.2 caller never sees it. Tests that compare a whole 3.3 capabilities response need the new key. Opt out with `serve(report_implementation=False)` (or `ServeConfig(report_implementation=False)`); add components or other SDKs with `ImplementationReport(extra_components=[...], extra_sdks=[...])`; or declare the block yourself (`Adcp(implementation=...)` on `DecisioningCapabilities`, `capabilities_response(implementation=...)`, or in your own handler's response), which the SDK never overwrites. `adcp.server.adcp_sdk_entry()` builds this SDK's entry for a hand-written block. `DecisioningCapabilities.adcp.implementation` was previously dropped from the projection; it is now emitted. |
4 changes: 2 additions & 2 deletions docs/adcp-3.3-sdk-inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ it adopted the bundle, and what is left to the codegen/wire-fidelity (2) and
| Discovery never provisions | `get_products`, `list_products`, `get_signals`, `request_proposals`, `refine_proposals`, `decline_proposals` MUST NOT create or activate an account; unresolved `account` → `ACCOUNT_NOT_FOUND`; `list_accounts` treats `account` as a filter | Storyboard `media_buy_seller/unprovisioned_account_reference` (advisory until runner 14.1.0). | done: discovery tasks never provision (`ResolveContext.is_provisioning`, #1288) and a supplied unresolved reference is `ACCOUNT_NOT_FOUND`; confirm with the storyboard: (3) |
| `account-change.json` `idempotency_key`; `changed_paths` required for `adcp` / `seller_operator` origins | `AccountChange.idempotency_key` | Echoed to the sending principal only. | generated; `list_account_changes` dispatch refuses a page with an `adcp` / `seller_operator` record lacking non-empty `changed_paths` (`VALIDATION_ERROR` at `/changes/N/changed_paths`, any validation mode, handlers on 3.3 or later) and strips `idempotency_key` from every record except an `adcp` record attributed (`attribute_account_change`, off the wire) to the calling principal; `WebhookSender.prepare_account_change_recorded` accepts the record, checks it, and never carries its key; helpers in `adcp.account_change_feed`: done (3), #1647 |
| `account.agent_onboarding`, `get_principal` `relationship`, `identity.trust_json`, `identity-relying-party` | `core/agent-onboarding.json`, `-tier.json`, `-requirement.json`, `core/buyer-onboarding-posture.json`, `core/principal-relationship.json`, `trust/v1/trust.json`, `trust/v1/trust-acknowledgements.json`; enums `agent-onboarding-mode`, `agent-onboarding-requirement-kind` (x-extensible), `principal-relationship-status`; `PrincipalChangedWebhook.Reason` `relationship_changed`, `relationship_expiring`; `get_principal` `Result.errors` | Experimental. The identity preview (`_identity_preview.py`, vendored `_identity_schemas/`, `generate_identity_preview.py`) is retired: `adcp.types.experimental_identity` binds the generated 3.3 types, `AgentOnboardingRequirement.kind` stays open (`enum | str`), and `adcp.signing.trust` validates against the bundle through `get_named_validator`. | done (1) |
| `adcp.implementation.sdks[]` | capabilities `Adcp.implementation` (`Implementation`, `Sdk`, `Component`) | Vendors move data out of `ext.adcp`. | generated; advertise this SDK: (3), #1646 |
| `adcp.implementation.sdks[]` | capabilities `Adcp.implementation` (`Implementation`, `Sdk`, `Component`) | Vendors move data out of `ext.adcp`. | generated; `serve()` self-reports `{"name": "adcp", "version": <installed>, "components": [...]}` on responses served at 3.3+, with components derived from what the server wires agent-wide (`adcp.server.implementation`); a declared block wins, `serve(report_implementation=False)` opts out: done (3), #1646 |
| `security_profiles` | `GetAdcpCapabilitiesResponse.security_profiles` (`adcp-prod-security-3.3`, experimental) | | generated |
| `request_signing.profiles`, `operation_sources`; `webhook_signing.profiles` | capabilities `RequestSigning.profiles` (`adcp/request-signing/v1`, `web-bot-auth`), `operation_sources` (`mcp_tools_call`, `a2a_invocation_skill`) | `required_for` enforced over A2A with `operation_sources` as the advertisement. | A2A hardening enforcement: done (3), #1659 (33 vectors pass); advertising `operation_sources`: (3), #1637 |
| `ext.adcp` reserved | `extensions/adcp.json` `AdcpOwnedExtensions` | Reserved namespace for the working group. | generated |
Expand All @@ -129,7 +129,7 @@ Every new declaration is optional; 3.2 behaviour stands when undeclared.

| Path | Added | SDK status |
| --- | --- | --- |
| `adcp.implementation` | `Implementation` (`sdks[]` of `Sdk` / `Component`) | generated |
| `adcp.implementation` | `Implementation` (`sdks[]` of `Sdk` / `Component`) | generated; exported as `Implementation`, `Sdk`, `ImplementationComponent` from `adcp.decisioning.capabilities`; projected from `DecisioningCapabilities.adcp.implementation` and `capabilities_response(implementation=...)`; otherwise `serve()` self-reports it: done (3), #1646 |
| `security_profiles` | `SecurityProfile[]` | generated |
| `account` | `agent_onboarding`, `stable_account_id` | generated |
| `creative` | `stable_creative_ids`, `supports_assignment_landing_page`, `supports_assignment_merge`, `supports_assignment_trackers`, `broadcast_loudness` | generated |
Expand Down
6 changes: 6 additions & 0 deletions src/adcp/decisioning/capabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ class HelloSeller(DecisioningPlatform):
IdempotencySupported,
IdempotencyUnsupported,
Identity,
Implementation,
ImplementationComponent,
KeyOrigins,
KeywordTargets,
LaterProposals,
Expand All @@ -82,6 +84,7 @@ class HelloSeller(DecisioningPlatform):
OpenOpportunities,
Portfolio,
RequestSigning,
Sdk,
SiCapabilities,
Signals,
SignalsFeatures,
Expand Down Expand Up @@ -147,6 +150,8 @@ class HelloSeller(DecisioningPlatform):
"IdempotencySupported",
"IdempotencyUnsupported",
"Identity",
"Implementation",
"ImplementationComponent",
"KeyOrigins",
"KeywordTargets",
"LaterProposals",
Expand All @@ -163,6 +168,7 @@ class HelloSeller(DecisioningPlatform):
"PreviewRenderingOrigin",
"PreviewRoute",
"RequestSigning",
"Sdk",
"SiCapabilities",
"Signals",
"SignalsFeatures",
Expand Down
3 changes: 3 additions & 0 deletions src/adcp/decisioning/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2263,6 +2263,9 @@ async def get_adcp_capabilities(
supported_versions=adcp.get("supported_versions"),
build_version=adcp.get("build_version"),
idempotency=adcp.get("idempotency") or {"supported": False},
# A declared AdCP 3.3 ``adcp.implementation`` wins over the
# self-report ``serve()`` adds (adcp.server.implementation).
implementation=adcp.get("implementation"),
)

from adcp.server.version_policy import project_supported_versions
Expand Down
3 changes: 3 additions & 0 deletions src/adcp/server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ async def get_products(params, context=None):
valid_actions_for_status,
)
from adcp.server.idempotency import IdempotencyStore, MemoryBackend
from adcp.server.implementation import ImplementationReport, adcp_sdk_entry
from adcp.server.mcp_sessions import MCPSessionStats, get_mcp_session_stats
from adcp.server.mcp_tools import (
DISCOVERY_METHODS,
Expand Down Expand Up @@ -278,6 +279,8 @@ async def get_products(params, context=None):
"ResponseEnhancer",
"SchemaMode",
"ServeConfig",
"ImplementationReport",
"adcp_sdk_entry",
"create_mcp_tools",
"create_mcp_server",
"get_tools_for_handler",
Expand Down
Loading
Loading