diff --git a/docs/validation-contract.md b/docs/validation-contract.md index 738c8331e..792bb4ca4 100644 --- a/docs/validation-contract.md +++ b/docs/validation-contract.md @@ -43,6 +43,100 @@ directly. One consequence for subclasses: a `field_serializer` that substitutes a default for `None` no longer runs for a `None` value on a marked field, because `exclude_if` drops the field before the serializer is reached. +## Wire round trip + +AdCP binds bytes to several objects. `core/audience-evidence.json` defines +`content_digest` as SHA-256 over the RFC 8785 (JCS) form of the entry *as +declared*; `core/canonical-proposal.json` binds `terms_digest` to +`commercial_terms` the same way; `core/rights-constraint.json`, +`core/creative-representation-set.json`, `media-buy/acceptance-policy-profile.json`, +the attestation schemas and the governance check carry their own; a `context` +is "echoed unchanged". A model that changes a value between parse and dump +breaks every one of those contracts without failing anything, and until 9.1 +the SDK had no invariant saying it must not. + +### Root cause + +The generator maps each JSON Schema `format` to the pydantic type that +*parses* it best — `AnyUrl`, `AwareDatetime`, `UUID`, `EmailStr` — and those +types re-serialize the value they parsed rather than the text they were given: +a bare origin gains a trailing slash, `+00:00` becomes `Z`, `.000` is dropped, +a UUID is lower-cased, an email's domain is lower-cased. The runtime then adds +rewrites of its own: `model_dump` emits every field, so a schema `default` the +generator wrote into the field appears in the output though the document +omitted it; `exclude_none` (the default) drops an explicit `null` the schema +admits; `by_alias` is off, so `$schema` dumps as `field_schema`; and +`extra='ignore'` drops a property the pinned model does not declare. Each of +these is reasonable for an application type and wrong for a document whose +bytes are an identity. + +Four earlier fixes were point patches of this one gap: #1384 typed one field +(`agent_url`) as a wire string because an option ID hashed it; #1449 reported +that dumping a validated `ProductFormatDeclaration` "adds every branch default +to the published format"; #1587 made nested models leave unset fields off from +any parent; #1575 kept a `ContextObject`'s null keys. #1610 found the next +instance in `AudienceEvidence`. + +### The invariant + +**A value the model did not change is dumped as it was received.** Two +mechanisms carry it, and a conformance suite measures it: + +- **Wire-preserving scalar types.** `scripts/post_generate_fixes.py` + (`preserve_wire_text_on_normalizing_fields`) points every generated + `format: date-time`, `uri`, `uuid` and `email` field at + `adcp.types._wire` — `WireDatetime`, `WireAnyUrl`, `WireUuid`, `WireEmail`. + Each validates exactly as the pydantic type did, keeps the attribute API + (`.year`, `.tzinfo`, `.host`, `.hex`), compares and hashes as the same + value, advertises the same JSON Schema, and in JSON mode serializes the text + it was parsed from — when that text passes the strict wire check the bundled + validator applies (`adcp._formats`: RFC 3339 for a date-time, RFC 3986 + syntax with an agreeing host for a URI, the hyphenated 8-4-4-4-12 form for a + UUID, a trimmed address for an email). A string pydantic's lenient parser + accepts but the schema rejects (`"2026-10-01 00:00:00+00:00"`, + `"https:x.example"`, `"{...}"`) is normalized, as it always was. A + `datetime`, `AnyUrl` or `UUID` *object* passed to a constructor has no wire + text and serializes as before: RFC 3339 with `Z` for UTC and `±HH:MM` + otherwise, fractional seconds only when non-zero; pydantic's normalized URL + string; a lower-case hyphenated UUID. Every such field is covered, not only the + digest-covered ones: a shared class (`AttestationReference`, `Provenance`, + `Targeting`) sits inside digested and undigested objects alike, so a + per-subtree rule would have to split classes. The rule covers the generated + AdCP schema types (`adcp.types.domains`); `adcp.types.registry`, the + OpenAPI-generated registry client, keeps pydantic's `AnyUrl` and + `AwareDatetime` — no registry object is digest-covered. +- **An explicit "as received" dump.** `AdCPBaseModel.model_dump_as_received()` + is `model_dump(mode="json", exclude_unset=True, exclude_none=False, + by_alias=True)`: it leaves off every field the document did not set (where + a generated default would otherwise appear), keeps an explicit `null` the + schema admits, and writes wire names. Recompute a digest over this dump, not + the default one. For a model built in Python it emits exactly the fields the + caller set. + +`tests/conformance/test_wire_round_trip.py` runs every bundle schema that has +a model — the bundle's `examples`, a synthesized document, the differential +fixtures, a checked-in corpus for the digest-covered schemas, and variants of +each exercising every form a normalizing parser rewrites — through +`model_validate_json(doc).model_dump(mode="json")` and compares the result to +the input under JCS. A rewritten URL, datetime, UUID, email, value or shape +fails anywhere. The digest- and echo-covered schemas must round-trip with no +difference of any class under `model_dump_as_received`. Under the default +dump the suite names the classes that remain, each with the issue that tracks +it, and the list only shrinks: + +| class | what happens | where it stands | +| --- | --- | --- | +| `default_emitted` | the dump materializes a schema default the document omitted | #1613; `model_dump_as_received` recovers the bytes. Two digest-covered schemas (`canonical-proposal`, `commercial-terms`) are affected under the default dump and are listed in the suite. | +| `field_dropped` | `extra='ignore'` drops an unknown or sibling-arm property | #1614; not recoverable by any dump. No digest-covered schema is affected at pin 3.2.4. | +| `alias_emitted` | `model_dump()` writes `pass_` for `pass`, `field_schema` for `$schema` | #1615; `model_dump_as_received` writes the wire name. | +| `null_dropped` | `exclude_none` drops an admitted explicit `null` | #1615; `model_dump_as_received` keeps it. | + +Why the default dump was not changed in 9.x: leaving unset defaults off every +`model_dump()` is wire-equivalent for a schema-applying reader but visible to +any code that indexes a dumped dict (`payload["sandbox"]`), including a +counterparty that does not apply schema defaults. That is a 10.0 decision, +written up in #1613. + ## Wire-boundary behavior - SDK clients validate requests before sending and responses after receiving, diff --git a/scripts/post_generate_fixes.py b/scripts/post_generate_fixes.py index ca322f248..9f5f43d56 100644 --- a/scripts/post_generate_fixes.py +++ b/scripts/post_generate_fixes.py @@ -25,9 +25,11 @@ import ast import hashlib import importlib.util +import io import json import re import sys +import tokenize from collections.abc import Mapping, Sequence from copy import deepcopy from pathlib import Path @@ -1007,6 +1009,97 @@ def preserve_format_reference_agent_url_wire_string() -> None: print(" core/format_id.py: agent_url keeps the wire string (WireUrl)") +#: The pydantic type the generator renders for each normalizing ``format`` and +#: the wire-preserving type from ``adcp.types._wire`` that replaces it. Each +#: replacement validates identically, keeps the attribute API (a ``datetime``, +#: an ``AnyUrl``, a ``UUID``, a ``str``) and advertises the same JSON Schema; +#: the only change is that JSON-mode serialization writes the text the wire +#: delivered instead of the parser's normalized form. +_WIRE_PRESERVING_TYPES: dict[tuple[str, str], str] = { + ("pydantic", "AwareDatetime"): "WireDatetime", + ("pydantic", "AnyUrl"): "WireAnyUrl", + ("pydantic", "EmailStr"): "WireEmail", + ("uuid", "UUID"): "WireUuid", +} + + +def _rename_code_names(source: str, renames: Mapping[str, str]) -> str: + """Rename identifier tokens, leaving strings and comments untouched. + + A description such as "Use a fresh UUID v4 for each request" must keep + its wording; only a ``NAME`` token the tokenizer reports is a reference. + """ + lines = source.splitlines(keepends=True) + edits: list[tuple[int, int, int, str]] = [] + previous = "" + for token in tokenize.generate_tokens(io.StringIO(source).readline): + # ``uuid.UUID`` is an attribute, not a reference to the imported name. + if token.type == tokenize.NAME and token.string in renames and previous != ".": + (row, col), (_, end_col) = token.start, token.end + edits.append((row - 1, col, end_col, renames[token.string])) + if token.type not in (tokenize.NL, tokenize.NEWLINE, tokenize.COMMENT): + previous = token.string + for row, col, end_col, replacement in reversed(edits): + line = lines[row] + lines[row] = line[:col] + replacement + line[end_col:] + return "".join(lines) + + +def preserve_wire_text_on_normalizing_fields() -> None: + """Point every normalizing scalar field at its wire-preserving type. + + ``format: date-time``, ``format: uri``, ``format: uuid`` and + ``format: email`` fields are rendered ``AwareDatetime``, ``AnyUrl``, + ``UUID`` and ``EmailStr``, each of which re-serializes the value it parsed: + ``https://measurement.example`` comes back with a trailing slash, + ``2026-10-01T00:00:00.000+00:00`` as ``2026-10-01T00:00:00Z``, an + upper-case UUID lower-cased, an email with its domain lower-cased. AdCP + binds bytes to several of these objects -- ``content_digest`` is SHA-256 + over the JCS form of an entry's declared fields -- so a parsed-then-dumped + entry stopped matching its own digest (#1610). The types in + ``adcp.types._wire`` validate the same way and serialize the text they + were parsed from; a value built in Python serializes as before. + + Every field is pointed there, not only the digest-covered ones: a shared + class (``AttestationReference``, ``Provenance``) sits inside digested and + undigested objects alike, so a per-subtree rule would have to split + classes, and the invariant worth having is that the SDK never rewrites a + wire value it did not change. + """ + renames = {name: wire for (_module, name), wire in _WIRE_PRESERVING_TYPES.items()} + modified = 0 + for path in sorted(OUTPUT_DIR.rglob("*.py")): + source = path.read_text() + if not any(re.search(rf"\b{name}\b", source) for name in renames): + continue + tree = ast.parse(source) + referenced = { + node.id for node in ast.walk(tree) if isinstance(node, ast.Name) and node.id in renames + } + if not referenced: + continue + for (module, name), wire in _WIRE_PRESERVING_TYPES.items(): + if name in referenced: + source = drop_from_imports(source, module, name) + source = _rename_code_names(source, {name: renames[name] for name in referenced}) + wires = sorted({renames[name] for name in referenced}) + if re.search(r"^from adcp\.types\.base import ", source, re.MULTILINE): + source = add_to_import(source, "adcp.types.base", *wires) + else: + anchor = _import_pattern("pydantic").search(source) + if anchor is None: + anchor = re.search(r"^from |^import ", source, re.MULTILINE) + assert anchor is not None, path + source = ( + source[: anchor.start()] + + f"from adcp.types.base import {', '.join(wires)}\n" + + source[anchor.start() :] + ) + path.write_text(source) + modified += 1 + print(f" Pointed normalizing fields at wire-preserving types in {modified} file(s)") + + def point_integer_fields_at_the_schema_integer_type() -> None: """Rewrite the generator's ``StrictInt`` marker to ``SchemaInt``. @@ -8360,6 +8453,9 @@ def main(argv: list[str] | None = None) -> int: annotate_registry_track_verdict, preserve_geo_place_system_map_keys, preserve_format_reference_agent_url_wire_string, + # After every pass that adds a validator typed ``AnyUrl`` and before + # the import cleanups, which would read the names this pass drops. + preserve_wire_text_on_normalizing_fields, insert_task_message_markers, point_integer_fields_at_the_schema_integer_type, remove_imports_shadowed_by_a_local_class, diff --git a/scripts/post_generation_manifest.json b/scripts/post_generation_manifest.json index 3fdf39234..ec9a571ce 100644 --- a/scripts/post_generation_manifest.json +++ b/scripts/post_generation_manifest.json @@ -1,6 +1,7 @@ { "_comment": "Effect manifest for scripts/post_generate_fixes.py. ``true`` records a fix that changes the generated tree; a string records a fix that changes nothing on the pinned schema set, and states why. Regenerate with `python scripts/post_generate_fixes.py --update-manifest`.", "fixes": { + "point_omittable_fields_at_no_explicit_null": true, "add_model_validator_to_product": "No-op since #213 added an explicit discriminator to publisher_properties: Pydantic validates the union, and the body only prints.", "fix_preview_render_self_reference": "datamodel-code-generator 0.64.0 emits PreviewRender1..3 unqualified in creative/preview_render.py; the module-qualified form this fix rewrites appears nowhere.", "fix_brand_manifest_references": "Its target core/promoted_offerings.py is not generated \u2014 brand.json is in GENERATED_SCHEMA_EXCLUDE_FILES \u2014 and the unaliased brand_manifest.BrandManifest reference appears nowhere in the tree.", @@ -69,9 +70,9 @@ "annotate_registry_track_verdict": true, "preserve_geo_place_system_map_keys": true, "preserve_format_reference_agent_url_wire_string": true, + "preserve_wire_text_on_normalizing_fields": true, "insert_task_message_markers": true, "point_integer_fields_at_the_schema_integer_type": true, - "point_omittable_fields_at_no_explicit_null": true, "remove_imports_shadowed_by_a_local_class": true, "remove_unused_pydantic_field_imports": true, "emit_open_asset_unions": true, diff --git a/src/adcp/_formats.py b/src/adcp/_formats.py new file mode 100644 index 000000000..9c910f8a8 --- /dev/null +++ b/src/adcp/_formats.py @@ -0,0 +1,122 @@ +"""Strict wire-format checks shared by schema validation and the wire types. + +The bundled JSON Schema validator and the generated models must agree on +which strings are a ``format: date-time`` or a ``format: uri`` +(``adcp.validation.schema_loader``), and the wire-preserving scalar types +(``adcp.types._wire``) keep a string's spelling only when it passes the same +check -- a string pydantic's lenient parser accepts but the schema rejects +(``"2026-10-01 00:00:00+00:00"``, ``"https:x.example"``) is normalized +instead, as it always was. This module is a leaf: it imports nothing from +``adcp.types`` or ``adcp.validation``, so both can import it. +""" + +from __future__ import annotations + +import re +from datetime import date +from urllib.parse import quote + +__all__ = [ + "RFC3339_DATE_TIME", + "UUID_TEXT", + "is_plain_uri", + "is_rfc3339_date_time", + "is_uri_syntax", + "is_uuid_text", + "uri_host", +] + +RFC3339_DATE_TIME = re.compile( + r"^\d{4}-\d{2}-\d{2}[Tt]" + r"(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d" + r"(?:\.\d+)?" + r"(?:[Zz]|[+-](?:[01]\d|2[0-3]):[0-5]\d)$", + re.ASCII, +) + +#: The hyphenated 8-4-4-4-12 form, either case: the only spelling +#: ``format: uuid`` admits. Braces, ``urn:uuid:`` and the bare 32 hex digits +#: are accepted by ``uuid.UUID`` and normalized. +UUID_TEXT = re.compile( + r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" +) + +# scheme "://" [userinfo "@"] host [":" port] -- enough to read the host a +# URI's own text names, for comparison with what the URL parser reports. +_URI_AUTHORITY = re.compile(r"^[A-Za-z][A-Za-z0-9+.\-]*://(?:[^/?#@]*@)?(\[[^\]]*\]|[^/?#:]*)") + +# A strict SUBSET of RFC 3986: scheme, a plain ASCII reg-name host, an +# optional port, and path/query/fragment made of unreserved, sub-delimiter +# and well-formed percent-escape characters only. No userinfo, IP literal, +# non-ASCII or stray ``%``, so anything it matches is a valid URI whose host +# every parser reads the same way; everything else takes the full check. +_PCHAR = r"(?:[A-Za-z0-9._~!$&'()*+,;=:@/-]|%[0-9A-Fa-f]{2})" +_PLAIN_URI = re.compile( + r"^[A-Za-z][A-Za-z0-9+.\-]*://[A-Za-z0-9.\-]+(?::[0-9]*)?" + rf"(?:/{_PCHAR}*)?(?:\?(?:{_PCHAR}|\?)*)?(?:#(?:{_PCHAR}|\?)*)?$", + re.ASCII, +) + + +def is_rfc3339_date_time(text: str) -> bool: + """Whether ``text`` is an RFC 3339 date-time. + + The grammar checks the time and offset ranges; only the calendar is + checked here, because Python 3.10's parser accepts three or six + fractional digits where RFC 3339 permits any positive number, and the + check must neither coerce nor truncate the value. + """ + if RFC3339_DATE_TIME.fullmatch(text) is None: + return False + try: + date.fromisoformat(text[:10]) + except ValueError: + return False + return True + + +def is_uri_syntax(text: str) -> bool: + """Whether ``text`` is RFC 3986 URI syntax, with non-ASCII escaped first. + + Whitespace, C1 controls and a backslash fail here: pydantic's ``AnyUrl`` + trims or rewrites them, which is exactly what must not be hidden. Only + ASCII is checked as written, so a malformed percent escape stays visible; + ``AnyUrl`` still checks internationalized hosts. + """ + from rfc3986_validator import validate_rfc3986 + + if any(char.isspace() or 0x7F <= ord(char) <= 0x9F for char in text): + return False + try: + syntax = ( + text + if text.isascii() + else "".join(char if char.isascii() else quote(char, safe="") for char in text) + ) + except UnicodeEncodeError: + return False + return bool(validate_rfc3986(syntax)) + + +def is_plain_uri(text: str) -> bool: + """Whether ``text`` is a URI the fast subset grammar accepts. + + True implies :func:`is_uri_syntax` is true and that the host the text + names is the host a URL parser reports (lower-cased). The common case, + checked in a fraction of the full RFC 3986 validator's time. + """ + # fullmatch: ``$`` alone would also accept a single trailing newline. + return _PLAIN_URI.fullmatch(text) is not None + + +def is_uuid_text(text: str) -> bool: + """Whether ``text`` is the hyphenated 8-4-4-4-12 UUID spelling.""" + return UUID_TEXT.fullmatch(text) is not None + + +def uri_host(text: str) -> str | None: + """The host ``text`` names in its authority, lower-cased, or ``None``.""" + match = _URI_AUTHORITY.match(text) + if match is None: + return None + return match.group(1).lower() diff --git a/src/adcp/reporting/adjustment_evidence.py b/src/adcp/reporting/adjustment_evidence.py index 406e9262e..fc63a80f9 100644 --- a/src/adcp/reporting/adjustment_evidence.py +++ b/src/adcp/reporting/adjustment_evidence.py @@ -32,6 +32,7 @@ ReportingObligation, ReportingRevision, ) +from adcp.types._wire import rfc3339 from adcp.validation.schema_loader import get_named_validator __all__ = [ @@ -463,7 +464,10 @@ def build_reporting_adjustment_receipt( moment = aware_utc(observed_at) if _instant(moment) < _instant(adjustment.created_at, raw["created_at"]): _fail("INVALID_RECEIPT") - payload["observed_at"] = moment.isoformat() + # The receipt's own spelling, written here: a parsed datetime keeps the + # text it was given (#1610), so ``isoformat()``'s ``+00:00`` would be + # emitted as such; the receipt has always carried ``Z``. + payload["observed_at"] = rfc3339(moment) if failures: payload["rejection_codes"] = sorted(failures) if current_receipt is not None: diff --git a/src/adcp/types/__init__.py b/src/adcp/types/__init__.py index 98b67e3f6..e18489287 100644 --- a/src/adcp/types/__init__.py +++ b/src/adcp/types/__init__.py @@ -83,6 +83,11 @@ class name, or a numbered variant has no semantic alias — import it by its "AcceptancePolicyProfileIds", "AcceptancePolicyRequirement", "AcceptancePolicyRule", + "AudienceEvidence", + "AudienceEvidencePin", + "AudienceEvidenceSelection", + "CanonicalAudienceEvidence", + "CanonicalAudienceEvidenceSelection", "CanonicalMediaBuyAction", "CanonicalMediaBuyActionMode", "CanonicalMediaBuyActionName", @@ -1290,6 +1295,9 @@ def __dir__() -> list[str]: AssetVariant, AssignedPackage, Assignments, + AudienceEvidence, + AudienceEvidencePin, + AudienceEvidenceSelection, AudienceSource, AudienceTooSmallDetails, AudioContent, @@ -1333,6 +1341,8 @@ def __dir__() -> list[str]: CalibrateContentResponse1, CalibrateContentSuccessResponse, CanonicalAssetSource, + CanonicalAudienceEvidence, + CanonicalAudienceEvidenceSelection, CanonicalCompositionModel, CanonicalFormatAgentPlacement, CanonicalFormatBase, diff --git a/src/adcp/types/_eager.py b/src/adcp/types/_eager.py index 9e854ca10..c34626a1d 100644 --- a/src/adcp/types/_eager.py +++ b/src/adcp/types/_eager.py @@ -95,6 +95,9 @@ AssetContentType, AssignedPackage, Assignments, + AudienceEvidence, + AudienceEvidencePin, + AudienceEvidenceSelection, AudienceSource, AudienceTooSmallDetails, Authentication, @@ -112,6 +115,8 @@ ByPackageItem, CalibrateContentRequest, CalibrateContentResponse, + CanonicalAudienceEvidence, + CanonicalAudienceEvidenceSelection, CanonicalMediaBuyAction, CanonicalMediaBuyActionMode, CanonicalMediaBuyActionName, @@ -1152,6 +1157,11 @@ def __init__(self, *args: object, **kwargs: object) -> None: "AcceptancePolicyProfileIds", "AcceptancePolicyRequirement", "AcceptancePolicyRule", + "AudienceEvidence", + "AudienceEvidencePin", + "AudienceEvidenceSelection", + "CanonicalAudienceEvidence", + "CanonicalAudienceEvidenceSelection", "CanonicalMediaBuyAction", "CanonicalMediaBuyActionMode", "CanonicalMediaBuyActionName", diff --git a/src/adcp/types/_wire.py b/src/adcp/types/_wire.py new file mode 100644 index 000000000..d57a232e7 --- /dev/null +++ b/src/adcp/types/_wire.py @@ -0,0 +1,384 @@ +"""Scalar types that keep the wire text they were parsed from. + +AdCP binds bytes to several objects: ``content_digest`` is SHA-256 over the +RFC 8785 (JCS) form of an entry's declared fields, a ``context`` is "echoed +unchanged", and a buyer verifies a seller's digest over what it received. A +model that parses ``https://measurement.example`` into pydantic's ``AnyUrl`` +re-serializes it as ``https://measurement.example/``; one that parses +``2026-10-01T00:00:00.000+00:00`` into ``AwareDatetime`` writes +``2026-10-01T00:00:00Z``. Either rewrite changes the digested bytes, so a +parsed-then-dumped entry no longer matches its own digest (#1610). + +The types here validate exactly as the pydantic types the generator would +have used, expose the same attribute API (``.year``, ``.tzinfo``, ``.host``, +``.hex``), compare and hash as the same values, and differ in one thing: in +JSON mode they serialize the text the wire delivered -- when that text passes +the strict wire check the bundled schema validator applies +(:mod:`adcp._formats`). A string pydantic's lenient parser accepts but the +schema rejects (``"2026-10-01 00:00:00+00:00"``, ``"https:x.example"``, a +braced UUID, ``"Bob "``) is normalized exactly as before, so +nothing invalid is ever echoed. A ``datetime``, ``AnyUrl`` or ``UUID`` +*object* has no wire text and serializes in the pydantic form it always did: + +* :class:`WireDatetime`: RFC 3339 with ``Z`` for UTC and ``±HH:MM`` otherwise, + fractional seconds written only when non-zero (six digits); +* :class:`WireAnyUrl`: pydantic's normalized URL string; +* :class:`WireUuid`: lower-case hyphenated. + +``scripts/post_generate_fixes.py`` points every generated ``format: date-time``, +``format: uri``, ``format: uuid`` and ``format: email`` field here +(``preserve_wire_text_on_normalizing_fields``). The published JSON Schema is +unchanged: each type advertises the same ``type``/``format`` pair the pydantic +type did. ``adcp.types.base.WireUrl`` (#1384) is the ``str``-typed sibling for +a URL that was already a plain string to adopters. + +To a type checker the names re-exported from ``adcp.types.base`` are the +pydantic types they replace (``datetime``, ``AnyUrl``, ``UUID``), so nothing +an adopter wrote against the generated annotations changes its verdict; the +``.wire`` property is a runtime attribute, and :func:`wire_text` is the typed +way to read it. +""" + +from __future__ import annotations + +import uuid +from datetime import datetime +from typing import Annotated, Any + +from pydantic import AnyUrl, EmailStr, GetCoreSchemaHandler, StrictStr, TypeAdapter +from pydantic.functional_validators import AfterValidator +from pydantic.json_schema import WithJsonSchema +from pydantic_core import CoreSchema, SchemaSerializer, core_schema +from typing_extensions import Self + +from adcp import _formats +from adcp._deferred_adapters import deferred_adapter + +__all__ = ["WireAnyUrl", "WireDatetime", "WireEmail", "WireUuid", "rfc3339", "wire_text"] + + +def rfc3339(value: datetime) -> str: + """The RFC 3339 text pydantic writes for an aware ``datetime``. + + ``Z`` for a zero offset, ``±HH:MM`` otherwise; fractional seconds only + when the microsecond is non-zero, and then all six digits. This is the + form every generated model has always emitted for a datetime built in + Python, and it stays the form for one. + """ + text = value.isoformat() + if text.endswith("+00:00"): + return text[:-6] + "Z" + return text + + +class WireDatetime(datetime): + """An aware ``datetime`` that remembers the RFC 3339 text it was parsed from. + + A ``datetime`` in every respect -- attributes, arithmetic, comparison, + hashing, ``isinstance`` -- plus :attr:`wire`, the text it serializes to in + JSON mode. A value parsed from strictly valid text serializes to that + text; one built in Python, parsed from a lenient spelling, or derived by + ``replace()``, ``astimezone()`` or arithmetic, serializes in + :func:`rfc3339` form. + + The slot is left unset on a value that carries no wire text: ``datetime`` + builds derived values and unpickles without calling ``__new__``, and a + constructor override costs every parse three times as much. + """ + + __slots__ = ("_wire",) + + _wire: str + + @classmethod + def from_datetime(cls, value: datetime, wire: str | None = None) -> Self: + """``value`` as a :class:`WireDatetime` carrying ``wire`` as its text.""" + # The pickle state (packed bytes plus tzinfo) is the cheapest way + # through ``datetime.__new__``: one C call instead of nine arguments. + self = cls(*datetime.__reduce_ex__(value, 2)[1]) + if wire is not None: + self._wire = wire + return self + + @property + def wire(self) -> str: + """The text this value serializes to in JSON mode.""" + wire = getattr(self, "_wire", None) + return wire if wire is not None else rfc3339(self) + + def __reduce_ex__(self, protocol: Any) -> Any: + # ``datetime`` pickles as ``(cls, (bytes[, tzinfo]))``; ``copy`` and + # ``pickle`` both go through here, so the wire text travels with the + # value through ``deepcopy`` and ``model_copy(deep=True)``. + reduced = datetime.__reduce_ex__(self, protocol) + return (_rebuild_datetime, (reduced, getattr(self, "_wire", None))) + + @classmethod + def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> CoreSchema: + inner = core_schema.datetime_schema(tz_constraint="aware") + is_strict = _formats.is_rfc3339_date_time + + def validate(value: Any, nested: core_schema.ValidatorFunctionWrapHandler) -> Any: + if isinstance(value, cls): + return value + parsed = nested(value) + if isinstance(value, str) and is_strict(value): + return cls.from_datetime(parsed, value) + return cls.from_datetime(parsed) + + return core_schema.no_info_wrap_validator_function( + validate, inner, serialization=_DATETIME_SERIALIZATION + ) + + +def _rebuild_datetime(reduced: Any, wire: str | None) -> WireDatetime: + cls, args = reduced[0], reduced[1] + value: WireDatetime = cls(*args) + if wire is not None: + value._wire = wire + return value + + +def _serialize_datetime(value: Any) -> str: + wire = getattr(value, "_wire", None) + return wire if wire is not None else rfc3339(value) + + +# JSON mode only: a python-mode dump returns the value itself, with no +# Python call per value, exactly as pydantic serializes a datetime. +_DATETIME_SERIALIZATION = core_schema.plain_serializer_function_ser_schema( + _serialize_datetime, when_used="json" +) + +# ``AdCPBaseModel.model_dump`` passes ``serialize_as_any=True``, under which +# pydantic-core serializes a value by its runtime type rather than the field's +# schema -- and infers "datetime" for a ``datetime`` subclass, which would +# write the normalized form. A ``__pydantic_serializer__`` on the class is what +# the inference consults first (``AnyUrl`` carries one for the same reason). +WireDatetime.__pydantic_serializer__ = SchemaSerializer( # type: ignore[attr-defined] + core_schema.any_schema(serialization=_DATETIME_SERIALIZATION) +) + + +class WireAnyUrl(AnyUrl): + """An ``AnyUrl`` that remembers the text it was parsed from. + + Validated by the same parser as ``AnyUrl`` and exposing the same parsed + view (``.scheme``, ``.host``, ``.path``, ...). ``str()`` and JSON-mode + serialization return the text as received when it is strict RFC 3986 + syntax naming the same host the parser reports; otherwise, and for a + value built from an ``AnyUrl`` or by :meth:`build`, pydantic's normalized + form -- so ``str(url)`` can never hand a client a host ``.host`` did not + name. Compares equal to any ``AnyUrl`` for the same parsed URL, so + ``model.url == AnyUrl("https://x.example/")`` keeps holding. + """ + + __slots__ = ("_wire",) + + _wire: str + + def __init__(self, url: Any) -> None: + super().__init__(url) + if isinstance(url, str): + _keep_url_text(self, url) + + @property + def wire(self) -> str: + """The text this value serializes to in JSON mode.""" + wire = getattr(self, "_wire", None) + return wire if wire is not None else str(self._url) + + def __str__(self) -> str: + return self.wire + + def __repr__(self) -> str: + return f"{type(self).__name__}({self.wire!r})" + + def __eq__(self, other: Any) -> bool: + return isinstance(other, AnyUrl) and self._url == other._url + + def __hash__(self) -> int: + return hash(self._url) + + def __deepcopy__(self, memo: dict[int, Any]) -> Self: + clone = type(self).__new__(type(self)) + clone._url = self._url + wire = getattr(self, "_wire", None) + if wire is not None: + clone._wire = wire + return clone + + def __reduce__(self) -> Any: + return (_rebuild_url, (type(self), str(self._url), getattr(self, "_wire", None))) + + @classmethod + def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> CoreSchema: + # One Python layer, not ``AnyUrl``'s wrap plus another: the core URL + # parser does the work and the instance is assembled here. + inner = core_schema.url_schema() + + def validate(value: Any, nested: core_schema.ValidatorFunctionWrapHandler) -> Any: + if isinstance(value, cls): + return value + if isinstance(value, AnyUrl): + value = str(value) + instance = cls.__new__(cls) + instance._url = nested(value) + if isinstance(value, str): + _keep_url_text(instance, value) + return instance + + return core_schema.no_info_wrap_validator_function( + validate, inner, serialization=_URL_SERIALIZATION + ) + + +def _serialize_url(value: Any, info: core_schema.SerializationInfo) -> Any: + return str(value) if info.mode == "json" else value + + +# ``always``, as ``AnyUrl``'s own serializer is: the core URL serializer the +# python-mode fallback would use expects the Rust URL, not the instance. +_URL_SERIALIZATION = core_schema.plain_serializer_function_ser_schema( + _serialize_url, info_arg=True, when_used="always" +) + + +def _keep_url_text(instance: WireAnyUrl, text: str) -> None: + """Attach ``text`` to ``instance`` when it is strict syntax for the parsed host. + + The fast grammar (:func:`adcp._formats.is_plain_uri`) settles the common + case and guarantees the host agrees; anything else takes the full RFC 3986 + check and an explicit host comparison, so ``https://good.example\\@evil.example`` + (a backslash the WHATWG parser reads as a slash) falls back to the + normalized text whose host is the one ``.host`` reports. + """ + if not (_formats.is_plain_uri(text) or _formats.is_uri_syntax(text)): + return + # Compare hosts on every path: the WHATWG parser rewrites numeric hosts + # (``2130706433``, ``0x7f.1``, ``127.1`` all become ``127.0.0.1``), so text + # that is valid syntax can still name a host other than ``.host``. + named = _formats.uri_host(text) + if named is None: + return + url = instance._url + if named == (url.host or "").lower() or named == (url.unicode_host() or "").lower(): + instance._wire = text + + +def _rebuild_url(cls: type[WireAnyUrl], parsed: str, wire: str | None) -> WireAnyUrl: + value = cls.__new__(cls) + value._url = AnyUrl(parsed)._url + if wire is not None: + value._wire = wire + return value + + +class WireUuid(uuid.UUID): + """A ``UUID`` that remembers the text it was parsed from. + + ``uuid.UUID`` serializes lower-case; RFC 4122 reads hex case-insensitively + and the bundled ``format: uuid`` validator accepts either case of the + hyphenated 8-4-4-4-12 form, which is the only spelling kept. A braced, + ``urn:uuid:`` or unhyphenated input ``uuid.UUID`` accepts is normalized + as before, and so is a value built in Python. + """ + + __slots__ = ("_wire",) + + _wire: str + + @property + def wire(self) -> str: + """The text this value serializes to in JSON mode.""" + wire = getattr(self, "_wire", None) + return wire if wire is not None else str(uuid.UUID(int=self.int)) + + def __reduce__(self) -> Any: + return (_rebuild_uuid, (type(self), self.int, getattr(self, "_wire", None))) + + def __deepcopy__(self, memo: dict[int, Any]) -> Self: + return self + + @classmethod + def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> CoreSchema: + inner = core_schema.uuid_schema() + is_strict = _formats.is_uuid_text + + def validate(value: Any, nested: core_schema.ValidatorFunctionWrapHandler) -> Any: + if isinstance(value, cls): + return value + parsed = nested(value) + instance = cls(int=parsed.int) + if isinstance(value, str) and is_strict(value): + object.__setattr__(instance, "_wire", value) + return instance + + return core_schema.no_info_wrap_validator_function( + validate, inner, serialization=_UUID_SERIALIZATION + ) + + +def _rebuild_uuid(cls: type[WireUuid], value: int, wire: str | None) -> WireUuid: + instance = cls(int=value) + if wire is not None: + object.__setattr__(instance, "_wire", wire) + return instance + + +def _serialize_uuid(value: Any) -> str: + wire = getattr(value, "_wire", None) + return wire if wire is not None else str(value) + + +_UUID_SERIALIZATION = core_schema.plain_serializer_function_ser_schema( + _serialize_uuid, when_used="json" +) + +# See the note on ``WireDatetime.__pydantic_serializer__``. +WireUuid.__pydantic_serializer__ = SchemaSerializer( # type: ignore[attr-defined] + core_schema.any_schema(serialization=_UUID_SERIALIZATION) +) + + +def wire_text(value: datetime | AnyUrl | uuid.UUID) -> str: + """The text ``value`` serializes to in JSON mode. + + The text as received for a value parsed from strictly valid text; the + deterministic form the module docstring lists otherwise. + """ + if isinstance(value, (WireDatetime, WireAnyUrl, WireUuid)): + return value.wire + if isinstance(value, datetime): + return rfc3339(value) + return str(value) + + +def _email_as_received(value: str) -> str: + """``value`` when it is the trimmed address ``EmailStr`` normalizes it to. + + ``EmailStr`` lower-cases the domain, which rewrites a digested + ``provider.data_subject_contestation.email``; the address as written is + kept when it differs only by case. A display-name form + (``"Bob "``) or surrounding whitespace is not an address + the schema admits, and comes back as the normalized address, as before. + """ + normalized: str = _email_adapter().validate_python(value) + if value.strip() == value and value.lower() == normalized.lower(): + return value + return normalized + + +@deferred_adapter +def _email_adapter() -> TypeAdapter[EmailStr]: + return TypeAdapter(EmailStr) + + +#: A ``format: email`` string, validated as ``EmailStr`` and carried as the +#: ``str`` the wire delivered when that is the address itself. ``EmailStr`` is +#: itself a ``str`` to adopters, so nothing about the attribute changes. +WireEmail = Annotated[ + StrictStr, + AfterValidator(_email_as_received), + WithJsonSchema({"type": "string", "format": "email"}), +] diff --git a/src/adcp/types/base.py b/src/adcp/types/base.py index d24462e91..089bc7221 100644 --- a/src/adcp/types/base.py +++ b/src/adcp/types/base.py @@ -36,6 +36,30 @@ from adcp.types._geo_place_keys import GeoPlaceSystemKey as GeoPlaceSystemKey from adcp.types._scalar import as_json_schema_integer +# Re-exported for the generated modules, beside ``WireUrl`` (#1384): the +# wire-preserving types ``scripts/post_generate_fixes.py`` points format fields +# at. To a type checker each IS the pydantic type it replaces -- a generated +# ``last_updated`` reads as ``datetime``, a ``url`` as ``AnyUrl`` -- so an +# adopter's ``assert_type``, assignment or constructor call type-checks exactly +# as it did; at runtime the value is the subclass that remembers its wire text. +# ``wire_text`` is the typed way to read that text. +from adcp.types._wire import WireEmail as WireEmail +from adcp.types._wire import wire_text as wire_text + +if TYPE_CHECKING: + import uuid as _uuid + from typing import TypeAlias + + from pydantic import AwareDatetime as _AwareDatetime + + WireDatetime: TypeAlias = _AwareDatetime + WireAnyUrl: TypeAlias = AnyUrl + WireUuid: TypeAlias = _uuid.UUID +else: + from adcp.types._wire import WireAnyUrl as WireAnyUrl + from adcp.types._wire import WireDatetime as WireDatetime + from adcp.types._wire import WireUuid as WireUuid + if TYPE_CHECKING: from typing_extensions import Self @@ -306,7 +330,10 @@ def _url_adapter() -> TypeAdapter[AnyUrl]: #: A ``format: uri`` string whose bytes are part of an identity. Validated as a #: URL, carried as the ``str`` the wire delivered. ``scripts/post_generate_fixes.py`` -#: points the format-reference ``agent_url`` here. +#: points the format-reference ``agent_url`` here. Its siblings for a field that +#: keeps the parsed type -- :class:`WireAnyUrl`, :class:`WireDatetime`, +#: :class:`WireUuid` -- and the ``str``-typed :data:`WireEmail` live in +#: :mod:`adcp.types._wire` and are re-exported here for the generated modules. WireUrl = Annotated[ StrictStr, AfterValidator(_require_absolute_url), @@ -609,6 +636,40 @@ def model_dump(self, **kwargs: Any) -> dict[str, Any]: _build_deferred_serializers(self, set()) return super().model_dump(**kwargs) + def model_dump_as_received(self, **kwargs: Any) -> dict[str, Any]: + """The document this model was parsed from, as JSON values. + + ``model_dump(mode="json")`` emits every field, so a property the wire + omitted comes back carrying the schema default the generator wrote + into the field (``sandbox: false``, ``timezone: "inventory_local"``), + and an explicit ``null`` the schema admits is dropped by + ``exclude_none``. A reader treats those as the same document; a + digest does not. ``core/canonical-proposal.json`` binds + ``terms_digest`` to the JCS form of ``commercial_terms`` *as declared*, + and a buyer recomputing it over a default dump gets a mismatch whenever + the seller left a defaulted property out (#1613). + + This dump leaves off every field the document did not set + (``exclude_unset``), keeps an explicit ``null`` on a property whose + schema admits one (``exclude_none=False``; a property the schema + refuses ``null`` on is left off by its own :data:`NoExplicitNull` + exclusion either way), and writes wire names (``by_alias``). With the + wire-preserving scalar types (:mod:`adcp.types._wire`), a schema-valid + document that reached the model round-trips byte-for-byte under RFC + 8785 -- except for a property the model never held, which + ``extra='ignore'`` dropped on input (#1614). + ``tests/conformance/test_wire_round_trip.py`` holds every digest- and + echo-covered schema to that, with no allowlist. + + For a model built in Python the result is exactly the fields the + caller set, in the deterministic forms the wire types document. + """ + kwargs.setdefault("mode", "json") + kwargs.setdefault("exclude_unset", True) + kwargs.setdefault("exclude_none", False) + kwargs.setdefault("by_alias", True) + return self.model_dump(**kwargs) + def model_dump_json(self, **kwargs: Any) -> str: if "exclude_none" not in kwargs: kwargs["exclude_none"] = True diff --git a/src/adcp/types/canonical_creative.py b/src/adcp/types/canonical_creative.py index 7bde84963..8e3fe787a 100644 --- a/src/adcp/types/canonical_creative.py +++ b/src/adcp/types/canonical_creative.py @@ -327,7 +327,14 @@ def _has_boundary_serialization_metadata(annotation: Any) -> bool: return False if hasattr(annotation, "__get_pydantic_core_schema__"): is_adcp_model = isinstance(annotation, type) and issubclass(annotation, AdCPBaseModel) - if not is_adcp_model and getattr(annotation, "__module__", "").split(".")[0] != "pydantic": + module = getattr(annotation, "__module__", "") + # The wire-preserving scalars (#1610) serialize to themselves in + # python mode, so a stored value is what the default dump reads. + if ( + not is_adcp_model + and module.split(".")[0] != "pydantic" + and module != "adcp.types._wire" + ): return True return any(_has_boundary_serialization_metadata(item) for item in get_args(annotation)) diff --git a/src/adcp/types/domains/a2ui/si_catalog.py b/src/adcp/types/domains/a2ui/si_catalog.py index 455b52dde..5788eee08 100644 --- a/src/adcp/types/domains/a2ui/si_catalog.py +++ b/src/adcp/types/domains/a2ui/si_catalog.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: a2ui/si_catalog.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import Field, StrictBool from . import bound_value @@ -205,7 +205,7 @@ class IntegrationAction(AdCPBaseModel): class Apps(AdCPBaseModel): appId: Annotated[str | None, NoExplicitNull] = None deepLink: Annotated[str | None, NoExplicitNull] = None - url: Annotated[AnyUrl | None, NoExplicitNull] = None + url: Annotated[WireAnyUrl | None, NoExplicitNull] = None class AppHandoff(AdCPBaseModel): diff --git a/src/adcp/types/domains/a2ui/user_action.py b/src/adcp/types/domains/a2ui/user_action.py index 00494a516..51e969836 100644 --- a/src/adcp/types/domains/a2ui/user_action.py +++ b/src/adcp/types/domains/a2ui/user_action.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: a2ui/user_action.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field class Action(AdCPBaseModel): @@ -32,5 +32,5 @@ class A2UiUserAction(AdCPBaseModel): componentId: Annotated[str, Field(description='ID of the component that triggered the action')] action: Annotated[Action, Field(description='The action that was triggered')] timestamp: Annotated[ - AwareDatetime | None, Field(description='When the action occurred'), NoExplicitNull + WireDatetime | None, Field(description='When the action occurred'), NoExplicitNull ] = None diff --git a/src/adcp/types/domains/aao/agent_publishers.py b/src/adcp/types/domains/aao/agent_publishers.py index a3c6b9485..69a0a53b1 100644 --- a/src/adcp/types/domains/aao/agent_publishers.py +++ b/src/adcp/types/domains/aao/agent_publishers.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: aao/agent_publishers.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictBool class DiscoveryMethod(StrEnum): @@ -82,7 +82,7 @@ class PublisherEntry(AdCPBaseModel): ), ] last_verified_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description="When the directory last fetched and validated this publisher's adagents.json. Distinct from the envelope's `directory_indexed_at` — `last_verified_at` is per-publisher freshness, the envelope value is the most recent refresh in the result set." ), @@ -94,13 +94,13 @@ class AaoDirectoryAgentPublishersInverseLookupResponse(AdCPBaseModel): extra='forbid', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonicalized echo of the agent_url that was looked up. Lowercase host, default port stripped, trailing slash on path component normalized — matches the canonicalization the SDK applies in `verify_agent_authorization`.' ), ] directory_indexed_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="When the directory last completed a refresh of any publisher in this result. Provenance anchor for the consumer's own cache. NULL on empty pages — there's no per-publisher anchor to report; consumers SHOULD treat a null value as 'no freshness assertion for this response' and not advance their own cache freshness." ), diff --git a/src/adcp/types/domains/account/list_account_changes_response.py b/src/adcp/types/domains/account/list_account_changes_response.py index 5024f54e0..430bdc9c4 100644 --- a/src/adcp/types/domains/account/list_account_changes_response.py +++ b/src/adcp/types/domains/account/list_account_changes_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: account/list_account_changes_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,9 +9,9 @@ from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool, model_validator +from pydantic import ConfigDict, Field, StrictBool, model_validator from ..core import account_change from ..core import context as context_1 @@ -56,18 +56,18 @@ class SourceCoverageItem(AdCPBaseModel): ), ] coverage_start: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Earliest upstream time covered by this source after connection or retention limits.' ), NoExplicitNull, ] = None observed_through: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Latest upstream point the seller has successfully observed.'), NoExplicitNull, ] = None - last_successful_sync_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + last_successful_sync_at: Annotated[WireDatetime | None, NoExplicitNull] = None stale_after_seconds: Annotated[ SchemaInt | None, Field( @@ -114,14 +114,14 @@ class ListAccountChangesResponse(AdcpResponse, AdcpVersionEnvelope, ProtocolEnve NoExplicitNull, ] = None available_since: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Oldest time for which the seller currently retains change records for this account and caller. The capability guarantees at least 90 days after adoption; pre-adoption history is not fabricated.' ), NoExplicitNull, ] = None generated_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Seller time when this page and its source-coverage watermarks were generated.' ), diff --git a/src/adcp/types/domains/account/report_usage_request.py b/src/adcp/types/domains/account/report_usage_request.py index 4d4569273..b630ed996 100644 --- a/src/adcp/types/domains/account/report_usage_request.py +++ b/src/adcp/types/domains/account/report_usage_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: account/report_usage_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import account_ref from ..core import context as context_1 @@ -132,7 +132,7 @@ class UsageItem(AdCPBaseModel): NoExplicitNull, ] = None finalized_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="ISO 8601 timestamp at which the reporter considered these numbers final. Present only when `final: true`. Anchors any deadline declared in the buy's `measurement_terms.billing_measurement.finalization_deadline_hours`." ), diff --git a/src/adcp/types/domains/account/sync_accounts_response.py b/src/adcp/types/domains/account/sync_accounts_response.py index 3431a8f9c..c886070c0 100644 --- a/src/adcp/types/domains/account/sync_accounts_response.py +++ b/src/adcp/types/domains/account/sync_accounts_response.py @@ -8,7 +8,8 @@ from typing import Annotated, Any, Literal, TypeAlias -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StringConstraints +from adcp.types.base import WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StringConstraints from ..core.version_envelope import AdcpVersionEnvelope from ..core import account_authorization as account_authorization_1 @@ -31,9 +32,9 @@ class Setup(AdcpVersionEnvelope): model_config = ConfigDict(extra='allow') - url: AnyUrl | None = None + url: WireAnyUrl | None = None message: str - expires_at: AwareDatetime | None = None + expires_at: WireDatetime | None = None class CreditLimit(AdcpVersionEnvelope): diff --git a/src/adcp/types/domains/account/sync_governance_request.py b/src/adcp/types/domains/account/sync_governance_request.py index 06909f781..3ad942b3b 100644 --- a/src/adcp/types/domains/account/sync_governance_request.py +++ b/src/adcp/types/domains/account/sync_governance_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: account/sync_governance_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..core import account_ref from ..core import context as context_1 @@ -38,7 +38,7 @@ class GovernanceAgent(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - url: Annotated[AnyUrl, Field(description='Governance agent endpoint URL. Must use HTTPS.')] + url: Annotated[WireAnyUrl, Field(description='Governance agent endpoint URL. Must use HTTPS.')] authentication: Annotated[ Authentication, Field(description='Authentication the seller presents when calling this governance agent.'), diff --git a/src/adcp/types/domains/account/sync_governance_response.py b/src/adcp/types/domains/account/sync_governance_response.py index fd533d4bd..bba5947d8 100644 --- a/src/adcp/types/domains/account/sync_governance_response.py +++ b/src/adcp/types/domains/account/sync_governance_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: account/sync_governance_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -11,8 +11,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..core import account_ref from ..core import context as context_1 @@ -31,7 +31,7 @@ class GovernanceAgent(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - url: Annotated[AnyUrl, Field(description='Governance agent endpoint URL.')] + url: Annotated[WireAnyUrl, Field(description='Governance agent endpoint URL.')] class SyncGovernanceResponse(AdcpResponse, ResponseArmDispatchMixin, AdcpVersionEnvelope, ProtocolEnvelope): diff --git a/src/adcp/types/domains/adagents.py b/src/adcp/types/domains/adagents.py index 4bd2d200b..ae05a6294 100644 --- a/src/adcp/types/domains/adagents.py +++ b/src/adcp/types/domains/adagents.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: adagents.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime, WireEmail from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, AwareDatetime, ConfigDict, EmailStr, Field, RootModel, StrictBool, model_validator +from pydantic import ConfigDict, Field, RootModel, StrictBool, model_validator from .core import ( collection, @@ -35,13 +35,13 @@ class AdcpAgentsAuthorization1(AdCPBaseModel): NoExplicitNull, ] = None authoritative_location: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="HTTPS URL of the authoritative adagents.json file. When present, this file is a reference and the authoritative location contains the actual agent authorization data. Because one deploy can change authorization across every publisher in the network, validators MUST cap response size, refuse redirects on the fetch, enforce short timeouts, and serve the previously cached file on transient 5xx. Two-tier size cap: pointer files served at `/.well-known/adagents.json` use the general 5 MB SSRF cap; dereferenced authoritative files (this URL's response, after the indirection) use a recommended 20 MB cap because the origin has explicitly opted in to fanning out across a publisher network. See docs/governance/property/managed-networks#security-considerations." ), ] last_updated: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='ISO 8601 timestamp indicating when this reference was last updated'), NoExplicitNull, ] = None @@ -84,7 +84,7 @@ class Contact(AdCPBaseModel): ), ] email: Annotated[ - EmailStr | None, + WireEmail | None, Field( description='Contact email for questions or issues with this authorization file', max_length=255, @@ -119,7 +119,7 @@ class Contact(AdCPBaseModel): NoExplicitNull, ] = None privacy_policy_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="URL to the entity's privacy policy. Used for consumer consent flows when interacting with this sales agent." ), @@ -146,7 +146,7 @@ class RevokedPublisherDomain(AdCPBaseModel): ), ] revoked_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when this publisher was revoked. Validators MAY use this to order revocations against their own cached state.' ), @@ -204,7 +204,7 @@ class PropertyFeature(AdCPBaseModel): extra='allow', ) url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="The agent's API endpoint URL. Callers comparing this URL against a feature-provider registry MUST canonicalize both sides per the AdCP URL canonicalization rules, not byte-equality. See docs/reference/url-canonicalization." ), @@ -313,12 +313,12 @@ class AuthorizedAgents1(AuthorizedAgentBaseFields): NoExplicitNull, ] = None effective_from: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Optional start time for this authorization window.'), NoExplicitNull, ] = None effective_until: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Optional end time for this authorization window.'), NoExplicitNull, ] = None @@ -386,12 +386,12 @@ class AuthorizedAgents2(AuthorizedAgentBaseFields): NoExplicitNull, ] = None effective_from: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Optional start time for this authorization window.'), NoExplicitNull, ] = None effective_until: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Optional end time for this authorization window.'), NoExplicitNull, ] = None @@ -461,12 +461,12 @@ class AuthorizedAgents4(AuthorizedAgentBaseFields): NoExplicitNull, ] = None effective_from: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Optional start time for this authorization window.'), NoExplicitNull, ] = None effective_until: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Optional end time for this authorization window.'), NoExplicitNull, ] = None @@ -690,12 +690,12 @@ class AuthorizedAgents3(AuthorizedAgentBaseFields): NoExplicitNull, ] = None effective_from: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Optional start time for this authorization window.'), NoExplicitNull, ] = None effective_until: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Optional end time for this authorization window.'), NoExplicitNull, ] = None @@ -864,7 +864,7 @@ class AdcpAgentsAuthorization27(AdCPBaseModel): NoExplicitNull, ] = None superseded_by: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional pointer indicating this adagents.json file has been superseded by another adagents.json at a different URL. Used by the AAO community-mirror lifecycle: when a platform (e.g., Meta) adopts AdCP and publishes its own adagents.json at `/.well-known/adagents.json`, the AAO mirror file at `creative.adcontextprotocol.org/translated//adagents.json` sets `superseded_by` to the platform-hosted URL. Buyer SDKs encountering a file with `superseded_by` SHOULD short-circuit and re-fetch from the named URL rather than serving stale content from the mirror. The mirror SHOULD continue serving with `superseded_by` set for ≥1 minor release after platform adoption so buyer caches keyed on the mirror URL get an explicit migration signal rather than a silent break.' ), @@ -891,7 +891,7 @@ class AdcpAgentsAuthorization27(AdCPBaseModel): ), ] last_updated: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='ISO 8601 timestamp indicating when this file was last updated'), NoExplicitNull, ] = None @@ -988,7 +988,7 @@ class AdcpAgentsAuthorization29(AdcpAgentsAuthorization22, AdcpAgentsAuthorizati NoExplicitNull, ] = None superseded_by: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional pointer indicating this adagents.json file has been superseded by another adagents.json at a different URL. Used by the AAO community-mirror lifecycle: when a platform (e.g., Meta) adopts AdCP and publishes its own adagents.json at `/.well-known/adagents.json`, the AAO mirror file at `creative.adcontextprotocol.org/translated//adagents.json` sets `superseded_by` to the platform-hosted URL. Buyer SDKs encountering a file with `superseded_by` SHOULD short-circuit and re-fetch from the named URL rather than serving stale content from the mirror. The mirror SHOULD continue serving with `superseded_by` set for ≥1 minor release after platform adoption so buyer caches keyed on the mirror URL get an explicit migration signal rather than a silent break.' ), @@ -1015,7 +1015,7 @@ class AdcpAgentsAuthorization29(AdcpAgentsAuthorization22, AdcpAgentsAuthorizati ), ] last_updated: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='ISO 8601 timestamp indicating when this file was last updated'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/brand/acquire_rights_response.py b/src/adcp/types/domains/brand/acquire_rights_response.py index c49b22355..42977929f 100644 --- a/src/adcp/types/domains/brand/acquire_rights_response.py +++ b/src/adcp/types/domains/brand/acquire_rights_response.py @@ -8,7 +8,8 @@ from typing import Annotated, Any, Literal, TypeAlias -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import WireAnyUrl +from pydantic import ConfigDict, Field from ..core.version_envelope import AdcpVersionEnvelope from . import rights_terms as rights_terms_1 @@ -36,7 +37,7 @@ class AcquireRightsResponse1(AdcpResponse, AdcpVersionEnvelope, ProtocolEnvelope restrictions: list[str] | None = None disclosure: Disclosure | None = None approval_webhook: push_notification_config_1.PushNotificationConfig | None = None - usage_reporting_url: AnyUrl | None = None + usage_reporting_url: WireAnyUrl | None = None rights_constraint: Any context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None diff --git a/src/adcp/types/domains/brand/creative_approval_request.py b/src/adcp/types/domains/brand/creative_approval_request.py index b7468825c..72adb4656 100644 --- a/src/adcp/types/domains/brand/creative_approval_request.py +++ b/src/adcp/types/domains/brand/creative_approval_request.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: brand/creative_approval_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Any -from adcp.types.base import NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..core import context as context_1 from ..core import ext as ext_1 @@ -28,7 +28,7 @@ class CreativeApprovalRequest(AdcpVersionEnvelope): NoExplicitNull, ] = None creative_url: Annotated[ - AnyUrl, Field(description='URL where the creative asset can be retrieved for review') + WireAnyUrl, Field(description='URL where the creative asset can be retrieved for review') ] creative_format: Annotated[ format_id.FormatReferenceStructuredObject | None, diff --git a/src/adcp/types/domains/brand/creative_approval_response.py b/src/adcp/types/domains/brand/creative_approval_response.py index f0da03818..e6c4c97d0 100644 --- a/src/adcp/types/domains/brand/creative_approval_response.py +++ b/src/adcp/types/domains/brand/creative_approval_response.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: brand/creative_approval_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from adcp.types.base import NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field from ..core import context as context_1 from ..core import error @@ -30,8 +30,8 @@ class CreativeApprovalResponse1(AdcpVersionEnvelope, ProtocolEnvelope): creative_id: Annotated[ str | None, Field(description="Echo of the buyer's creative identifier"), NoExplicitNull ] = None - creative_url: Annotated[AnyUrl | None, NoExplicitNull] = None - approved_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + creative_url: Annotated[WireAnyUrl | None, NoExplicitNull] = None + approved_at: Annotated[WireDatetime | None, NoExplicitNull] = None conditions: Annotated[ list[str] | None, Field(description="Conditions on the approval (e.g., 'approved for NL market only')"), @@ -50,7 +50,7 @@ class CreativeApprovalResponse2(AdcpVersionEnvelope, ProtocolEnvelope): creative_id: Annotated[ str | None, Field(description="Echo of the buyer's creative identifier"), NoExplicitNull ] = None - creative_url: Annotated[AnyUrl | None, NoExplicitNull] = None + creative_url: Annotated[WireAnyUrl | None, NoExplicitNull] = None reason: Annotated[str, Field(description='Why the creative was rejected')] suggestions: Annotated[ list[str] | None, @@ -74,14 +74,14 @@ class CreativeApprovalResponse3(AdcpVersionEnvelope, ProtocolEnvelope): creative_id: Annotated[ str | None, Field(description="Echo of the buyer's creative identifier"), NoExplicitNull ] = None - creative_url: Annotated[AnyUrl | None, NoExplicitNull] = None + creative_url: Annotated[WireAnyUrl | None, NoExplicitNull] = None estimated_response_time: Annotated[ str | None, Field(description="Expected time for review (e.g., '24h', '2 business days')"), NoExplicitNull, ] = None status_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='URL to poll for updated approval status. GET this URL to receive a creative-approval-response. Poll at reasonable intervals (suggested: every 5 minutes, back off after 1 hour to every 30 minutes). Stop polling after estimated_response_time has elapsed and the approval_status is still pending_review.' ), diff --git a/src/adcp/types/domains/brand/get_brand_identity_response.py b/src/adcp/types/domains/brand/get_brand_identity_response.py index 0af6a4ff8..1b81fc70c 100644 --- a/src/adcp/types/domains/brand/get_brand_identity_response.py +++ b/src/adcp/types/domains/brand/get_brand_identity_response.py @@ -8,7 +8,8 @@ from typing import Annotated, Any, Literal, TypeAlias -from pydantic import AnyUrl, ConfigDict, Field, StringConstraints +from adcp.types.base import WireAnyUrl +from pydantic import ConfigDict, Field, StringConstraints from ..core.version_envelope import AdcpVersionEnvelope from ..core import context as context_1 @@ -27,7 +28,7 @@ class House(AdcpVersionEnvelope): class Logo(AdcpVersionEnvelope): model_config = ConfigDict(extra='allow') - url: AnyUrl + url: WireAnyUrl orientation: Literal['square', 'horizontal', 'vertical', 'stacked'] | None = None background: Literal['dark-bg', 'light-bg', 'transparent-bg'] | None = None variant: Literal['primary', 'secondary', 'icon', 'wordmark', 'full-lockup'] | None = None @@ -88,7 +89,7 @@ class Asset(AdcpVersionEnvelope): model_config = ConfigDict(extra='allow') asset_id: str asset_type: asset_content_type_1.AssetContentType - url: AnyUrl + url: WireAnyUrl tags: list[str] | None = None name: str | None = None description: str | None = None diff --git a/src/adcp/types/domains/brand/get_rights_response.py b/src/adcp/types/domains/brand/get_rights_response.py index 65bc299fc..ed9d193aa 100644 --- a/src/adcp/types/domains/brand/get_rights_response.py +++ b/src/adcp/types/domains/brand/get_rights_response.py @@ -8,7 +8,8 @@ from typing import Annotated, TypeAlias -from pydantic import AnyUrl, ConfigDict, Field, StringConstraints +from adcp.types.base import WireAnyUrl +from pydantic import ConfigDict, Field, StringConstraints from ..core.version_envelope import AdcpVersionEnvelope from . import rights_pricing_option as rights_pricing_option_1 @@ -28,7 +29,7 @@ class ExclusivityStatus(AdcpVersionEnvelope): class PreviewAsset(AdcpVersionEnvelope): model_config = ConfigDict(extra='allow') - url: AnyUrl + url: WireAnyUrl usage: str | None = None diff --git a/src/adcp/types/domains/brand/revocation_notification.py b/src/adcp/types/domains/brand/revocation_notification.py index e501e9023..75656bf59 100644 --- a/src/adcp/types/domains/brand/revocation_notification.py +++ b/src/adcp/types/domains/brand/revocation_notification.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: brand/revocation_notification.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..core import context as context_1 from ..core import ext as ext_1 @@ -31,7 +31,7 @@ class RevocationNotification(AdCPBaseModel): brand_id: Annotated[str, Field(description='Brand identifier of the rights subject')] reason: Annotated[str, Field(description='Human-readable reason for revocation')] effective_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='When the revocation takes effect. Immediate revocations use current time. Grace periods use a future time. The buyer must stop serving creative using these rights by this time.' ), diff --git a/src/adcp/types/domains/brand/search_brands_response.py b/src/adcp/types/domains/brand/search_brands_response.py index c17283dbe..85de39e87 100644 --- a/src/adcp/types/domains/brand/search_brands_response.py +++ b/src/adcp/types/domains/brand/search_brands_response.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: brand/search_brands_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, model_validator +from pydantic import ConfigDict, Field, model_validator from ..core import context as context_1 from ..core import error @@ -77,7 +77,7 @@ class Logo(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - url: Annotated[AnyUrl, Field(description='URL to the logo asset')] + url: Annotated[WireAnyUrl, Field(description='URL to the logo asset')] orientation: Annotated[ Orientation | None, Field(description='Logo aspect ratio orientation'), NoExplicitNull ] = None @@ -142,7 +142,7 @@ class SearchBrandResult(AdCPBaseModel): NoExplicitNull, ] = None relationship_verified_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the mutual-assertion edge was last confirmed by both sides. Present only when relationship_trust is mutual.' ), diff --git a/src/adcp/types/domains/brand/update_rights_response.py b/src/adcp/types/domains/brand/update_rights_response.py index f4dad3910..ed6214eaa 100644 --- a/src/adcp/types/domains/brand/update_rights_response.py +++ b/src/adcp/types/domains/brand/update_rights_response.py @@ -8,7 +8,8 @@ from typing import Annotated, Any, TypeAlias -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import WireDatetime +from pydantic import ConfigDict, Field from ..core.version_envelope import AdcpVersionEnvelope from . import rights_terms as rights_terms_1 @@ -28,7 +29,7 @@ class UpdateRightsResponse1(AdcpResponse, AdcpVersionEnvelope, ProtocolEnvelope) ) rights_constraint: Any | None = None paused: bool | None = None - implementation_date: AwareDatetime | None = None + implementation_date: WireDatetime | None = None context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None diff --git a/src/adcp/types/domains/brand/verify_brand_claim_response.py b/src/adcp/types/domains/brand/verify_brand_claim_response.py index 1037943f6..676476bbc 100644 --- a/src/adcp/types/domains/brand/verify_brand_claim_response.py +++ b/src/adcp/types/domains/brand/verify_brand_claim_response.py @@ -8,8 +8,8 @@ from adcp.types._str_enum import StrEnum from typing import Any, Annotated, Literal -from adcp.types.base import AdCPBaseModel -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, WireAnyUrl +from pydantic import ConfigDict, Field from ..core import context as context_1 from ..core import error as error_1 @@ -58,7 +58,7 @@ class VerifyBrandClaimPayload(AdCPBaseModel): ), ] agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical URL of the responding brand agent entry whose response-signing key verifies this envelope.' ), diff --git a/src/adcp/types/domains/brand/verify_brand_claims_request.py b/src/adcp/types/domains/brand/verify_brand_claims_request.py index 160679181..baacba516 100644 --- a/src/adcp/types/domains/brand/verify_brand_claims_request.py +++ b/src/adcp/types/domains/brand/verify_brand_claims_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: brand/verify_brand_claims_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,9 +9,9 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field from ..core.version_envelope import AdcpVersionEnvelope from ..enums import property_type @@ -37,7 +37,7 @@ class Claim(AdCPBaseModel): NoExplicitNull, ] = None observed_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description="When the caller observed the leaf's claim."), NoExplicitNull, ] = None @@ -67,7 +67,7 @@ class Claim1(AdCPBaseModel): Field(description='Optional context — what the claimant published or said.'), NoExplicitNull, ] = None - observed_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + observed_at: Annotated[WireDatetime | None, NoExplicitNull] = None class ClaimEntry2(AdCPBaseModel): diff --git a/src/adcp/types/domains/brand/verify_brand_claims_response.py b/src/adcp/types/domains/brand/verify_brand_claims_response.py index 31e8b68e2..f81beb053 100644 --- a/src/adcp/types/domains/brand/verify_brand_claims_response.py +++ b/src/adcp/types/domains/brand/verify_brand_claims_response.py @@ -8,8 +8,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel -from pydantic import AnyUrl, ConfigDict, Field, RootModel +from adcp.types.base import AdCPBaseModel, WireAnyUrl +from pydantic import ConfigDict, Field, RootModel from ..core import context as context_1 from ..core import error as error_1 @@ -103,7 +103,7 @@ class VerifyBrandClaimsPayload(AdCPBaseModel): ), ] agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical URL of the responding brand agent entry whose response-signing key verifies this envelope.' ), diff --git a/src/adcp/types/domains/brand_discovery.py b/src/adcp/types/domains/brand_discovery.py index 6a61e1ab3..10a964dc3 100644 --- a/src/adcp/types/domains/brand_discovery.py +++ b/src/adcp/types/domains/brand_discovery.py @@ -1,19 +1,16 @@ # generated by datamodel-codegen: # filename: _brand_discovery.json -# timestamp: 2026-10-21T11:05:59+00:00 +# timestamp: 2026-10-25T19:32:57+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Dict, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime, WireEmail from adcp.types._scalar import ScalarFloat, ScalarInt, ScalarStr from pydantic import ( - AnyUrl, - AwareDatetime, ConfigDict, - EmailStr, Field, RootModel, StrictBool, @@ -37,7 +34,7 @@ class BrandDiscovery1(AdCPBaseModel): ) field_schema: Annotated[str | None, Field(alias='$schema')] = None authoritative_location: Annotated[ - AnyUrl, Field(description='HTTPS URL of the authoritative brand.json file') + WireAnyUrl, Field(description='HTTPS URL of the authoritative brand.json file') ] redirect_reason: Annotated[ RedirectReason | None, @@ -46,7 +43,7 @@ class BrandDiscovery1(AdCPBaseModel): ), ] = None redirect_effective_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Optional timestamp when this redirect became effective. Caches MUST treat any entry cached before this timestamp as stale and re-fetch through the redirect.' ), @@ -54,7 +51,7 @@ class BrandDiscovery1(AdCPBaseModel): note: Annotated[ str | None, Field(description='Optional human-readable rationale for the redirect.') ] = None - last_updated: AwareDatetime | None = None + last_updated: WireDatetime | None = None class Domain(ScalarStr): @@ -182,7 +179,7 @@ class PortfolioEntry(AdCPBaseModel): ), ] = None effective_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp when the house established this ownership claim. Consumers age mutual-assertion edges from this date for TTL purposes and MUST NOT extend relationship trust before a future effective_at is reached. Optional; absent means the consumer ages from its own durable first observation.' ), @@ -453,7 +450,7 @@ class FontFile(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - url: Annotated[AnyUrl, Field(description='HTTPS URL to the font file (WOFF2, TTF, or OTF)')] + url: Annotated[WireAnyUrl, Field(description='HTTPS URL to the font file (WOFF2, TTF, or OTF)')] weight: Annotated[ SchemaInt | None, Field(description='CSS numeric font-weight for static fonts (100-900)', ge=100, le=900), @@ -587,7 +584,7 @@ class UpdateFrequency(StrEnum): class AgenticCheckout(AdCPBaseModel): - endpoint: Annotated[AnyUrl, Field(description='Base URL for checkout session API')] + endpoint: Annotated[WireAnyUrl, Field(description='Base URL for checkout session API')] spec: Annotated[ str, Field( @@ -615,11 +612,11 @@ class DataSubjectContestation(AdCPBaseModel): extra='forbid', ) url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='HTTPS URL to a human-accessible contestation form or information page.'), ] = None email: Annotated[ - EmailStr | None, + WireEmail | None, Field( description='Email address for contestation requests. Deployer MUST monitor and respond within the timelines set by applicable law (e.g., 1 month under GDPR Art. 12(3)).' ), @@ -691,7 +688,7 @@ class Avatar(AdCPBaseModel): class Contact1(AdCPBaseModel): - email: Annotated[EmailStr | None, Field(description='Contact email')] = None + email: Annotated[WireEmail | None, Field(description='Contact email')] = None phone: Annotated[str | None, Field(description='Contact phone number')] = None @@ -727,8 +724,8 @@ class DataSubjectContestation1(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - url: Annotated[AnyUrl | None, NoExplicitNull] = None - email: Annotated[EmailStr | None, NoExplicitNull] = None + url: Annotated[WireAnyUrl | None, NoExplicitNull] = None + email: Annotated[WireEmail | None, NoExplicitNull] = None languages: Annotated[list[str] | None, NoExplicitNull] = None @@ -797,7 +794,7 @@ class Role(StrEnum): class DeclaredBy(AdCPBaseModel): agent_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL of the agent or service that declared this provenance'), NoExplicitNull, ] = None @@ -806,7 +803,7 @@ class DeclaredBy(AdCPBaseModel): class C2pa(AdCPBaseModel): manifest_url: Annotated[ - AnyUrl, Field(description='URL to the C2PA manifest store for this content') + WireAnyUrl, Field(description='URL to the C2PA manifest store for this content') ] @@ -815,7 +812,7 @@ class VerifyAgent(AdCPBaseModel): extra='forbid', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="URL of the governance agent the buyer represents was used to embed/verify this layer. MUST use the `https://` scheme and MUST appear in the seller's `creative_policy.accepted_verifiers[].agent_url` list (canonicalized per /docs/reference/url-canonicalization: lowercase scheme and host, strip default port, normalize path dot-segments). Sellers MUST NOT call this URL until the canonicalized match is confirmed." ), @@ -834,7 +831,7 @@ class VerifyAgent1(AdCPBaseModel): extra='forbid', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="URL of the governance agent the buyer represents was used to apply/detect this watermark. MUST use the `https://` scheme and MUST appear in the seller's `creative_policy.accepted_verifiers[].agent_url` list (canonicalized per /docs/reference/url-canonicalization: lowercase scheme and host, strip default port, normalize path dot-segments). Sellers MUST NOT call this URL until the canonicalized match is confirmed." ), @@ -863,7 +860,7 @@ class VerificationItem(AdCPBaseModel): ), ] verified_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the verification was performed (ISO 8601)'), NoExplicitNull, ] = None @@ -876,7 +873,7 @@ class VerificationItem(AdCPBaseModel): NoExplicitNull, ] = None details_url: Annotated[ - AnyUrl | None, Field(description='URL to the full verification report'), NoExplicitNull + WireAnyUrl | None, Field(description='URL to the full verification report'), NoExplicitNull ] = None @@ -937,7 +934,7 @@ class BrandAgent(AdCPBaseModel): extra='allow', ) url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="Brand agent MCP endpoint URL. Callers comparing this URL against another value (e.g., resolving 'is this the brand's declared agent?' against a discovery cache) MUST canonicalize both sides per the AdCP URL canonicalization rules, not byte-equality. See docs/reference/url-canonicalization." ), @@ -1028,13 +1025,13 @@ class AuthorizedOperator(AdCPBaseModel): ), ] = None valid_from: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp when this operator authorization starts. Omit when authorization is already active or the start date is not tracked.' ), ] = None valid_until: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp when this operator authorization expires. Consumers MUST treat entries at or after this timestamp as inactive for authorization decisions.' ), @@ -1776,7 +1773,7 @@ class AssetLibrary(AdCPBaseModel): ) name: Annotated[str, Field(description='Display name of the asset library')] type: Annotated[Type2 | None, Field(description='Type of asset library')] = None - url: Annotated[AnyUrl, Field(description='URL to the asset library (for human access)')] + url: Annotated[WireAnyUrl, Field(description='URL to the asset library (for human access)')] description: Annotated[ str | None, Field(description='Description of the library contents and usage') ] = None @@ -1811,7 +1808,7 @@ class Contact(AdCPBaseModel): extra='allow', ) name: Annotated[str, Field(max_length=255, min_length=1)] - email: Annotated[EmailStr | None, Field(max_length=255)] = None + email: Annotated[WireEmail | None, Field(max_length=255)] = None domain: Domain | None = None @@ -1835,7 +1832,7 @@ class BrandDiscovery2(AdCPBaseModel): ), ] = None redirect_effective_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Optional timestamp when this redirect became effective. Caches MUST treat any entry cached before this timestamp as stale and re-fetch through the redirect.' ), @@ -1843,7 +1840,7 @@ class BrandDiscovery2(AdCPBaseModel): note: Annotated[ str | None, Field(description='Optional human-readable rationale for the redirect.') ] = None - last_updated: AwareDatetime | None = None + last_updated: WireDatetime | None = None class DefaultLanguage(AdCPBaseModel): @@ -1860,7 +1857,7 @@ class Logo(AdCPBaseModel): description='Stable identifier for this logo entry. Recommended when logo usage rules or mark lockups need to bind to a specific logo asset.' ), ] = None - url: Annotated[AnyUrl, Field(description='URL to the logo asset')] + url: Annotated[WireAnyUrl, Field(description='URL to the logo asset')] orientation: Annotated[ Orientation | None, Field( @@ -1907,7 +1904,7 @@ class Asset(AdCPBaseModel): ) asset_id: Annotated[str, Field(description='Unique identifier')] asset_type: Annotated[AssetContentType, Field(description='Type of asset content')] - url: Annotated[AnyUrl, Field(description='URL to CDN-hosted asset file')] + url: Annotated[WireAnyUrl, Field(description='URL to CDN-hosted asset file')] tags: Annotated[ list[str] | None, Field(description="Tags for discovery (e.g., 'hero', 'lifestyle', 'product', 'holiday')"), @@ -1970,7 +1967,7 @@ class ProductCatalog(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - feed_url: Annotated[AnyUrl, Field(description='URL to product catalog feed')] + feed_url: Annotated[WireAnyUrl, Field(description='URL to product catalog feed')] feed_format: Annotated[FeedFormat | None, Field(description='Format of the product feed')] = ( None ) @@ -1978,7 +1975,7 @@ class ProductCatalog(AdCPBaseModel): list[str] | None, Field(description='Product categories available in the catalog') ] = None last_updated: Annotated[ - AwareDatetime | None, Field(description='When the product catalog was last updated') + WireDatetime | None, Field(description='When the product catalog was last updated') ] = None update_frequency: Annotated[ UpdateFrequency | None, Field(description='How frequently the product catalog is updated') @@ -2001,7 +1998,7 @@ class Collection(AdCPBaseModel): TalentRole | None, Field(description="This person's role on the collection") ] = None seller_agent_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='URL of the sales agent that sells inventory for this collection. Buyer agents can query this agent for collection products.' ), @@ -2034,7 +2031,7 @@ class EmbeddedProvenanceItem(AdCPBaseModel): NoExplicitNull, ] = None embedded_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the provenance data was embedded (ISO 8601)'), NoExplicitNull, ] = None @@ -2063,7 +2060,7 @@ class Watermark(AdCPBaseModel): NoExplicitNull, ] = None embedded_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the watermark was applied (ISO 8601)'), NoExplicitNull, ] = None @@ -2178,14 +2175,14 @@ class Provenance(AdCPBaseModel): NoExplicitNull, ] = None declared_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When this provenance claim was made (ISO 8601). Distinct from created_time, which records when the content itself was produced. A provenance claim may be attached well after content creation, for example when retroactively declaring AI involvement for regulatory compliance.' ), NoExplicitNull, ] = None created_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When this content was created or generated (ISO 8601)'), NoExplicitNull, ] = None @@ -2235,7 +2232,7 @@ class RightsAgent(AdCPBaseModel): extra='allow', ) url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="Rights agent MCP endpoint URL. Callers comparing this URL against another value (e.g., matching against a brand's declared rights endpoint) MUST canonicalize both sides per the AdCP URL canonicalization rules, not byte-equality. See docs/reference/url-canonicalization." ), @@ -2260,7 +2257,7 @@ class BrandAgentEntry(AdCPBaseModel): ) type: Annotated[BrandAgentType, Field(description='Functional role of this agent')] url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="Agent endpoint URL (MCP or A2A). Callers comparing a brand's declared agent URL against another value (e.g., resolving 'is this the agent that signed this artifact?' or matching against a discovery cache) MUST canonicalize both sides per the AdCP URL canonicalization rules, not byte-equality. After the publication chain and an authorized request-signing key verify, this exact entry's canonical URL is the stable Agent identity input for principal mapping; the domain, JWKS URI, kid, and key material are credential or trust facts beneath it, not the Agent identity. See docs/reference/url-canonicalization." ), @@ -2281,7 +2278,7 @@ class BrandAgentEntry(AdCPBaseModel): ), ] = None jwks_uri: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="HTTPS URL of the agent's JWKS (RFC 7517) containing public keys used to verify artifacts this agent signs or requests it sends. Verified artifacts include signed governance_context tokens (for governance agents) and RFC 9421 HTTP Signatures on outgoing requests (for any agent). When absent, verifiers MUST default to /.well-known/jwks.json on the origin of `url`. Keys are identified by `kid` in the JWS header or RFC 9421 `keyid` parameter; JWKS MAY contain multiple keys to support rotation and per-purpose separation via `key_ops` and `use`." ), @@ -2316,7 +2313,7 @@ class LogoUsageRule(AdCPBaseModel): extra='allow', ) logo_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Specific logo asset URL this rule applies to. Omit when the rule applies by variant or tags.' ), @@ -2449,7 +2446,7 @@ class BrandDiscovery3(AdCPBaseModel): brand_agent: Annotated[BrandAgent | None, Field(deprecated=True)] = None contact: Contact | None = None data_subject_contestation: DataSubjectContestation | None = None - last_updated: AwareDatetime | None = None + last_updated: WireDatetime | None = None class ImageAsset(AdCPBaseModel): @@ -2462,7 +2459,7 @@ class ImageAsset(AdCPBaseModel): description='Discriminator identifying this as an image asset. See /schemas/creative/asset-types for the registry.' ), ] = 'image' - url: Annotated[AnyUrl, Field(description='URL to the image asset')] + url: Annotated[WireAnyUrl, Field(description='URL to the image asset')] width: Annotated[SchemaInt, Field(description='Width in pixels', ge=1)] height: Annotated[SchemaInt, Field(description='Height in pixels', ge=1)] file_size_bytes: Annotated[ @@ -2625,7 +2622,7 @@ class Brand(AdCPBaseModel): BrandId, Field(description='Brand identifier within the house. House chooses this ID.') ] url: Annotated[ - AnyUrl | None, Field(description='Primary brand URL for context and asset discovery') + WireAnyUrl | None, Field(description='Primary brand URL for context and asset discovery') ] = None identity_relying_parties: Annotated[ list[IdentityRelyingParty] | None, @@ -2674,7 +2671,7 @@ class Brand(AdCPBaseModel): ] = None product_catalog: ProductCatalog | None = None privacy_policy_url: Annotated[ - AnyUrl | None, Field(description="URL to the brand's privacy policy") + WireAnyUrl | None, Field(description="URL to the brand's privacy policy") ] = None data_subject_contestation: DataSubjectContestation | None = None disclaimers: Annotated[ @@ -2795,7 +2792,7 @@ class BrandDiscovery4(AdCPBaseModel): description='House-level (corporate) registered trademarks. Brand-level marks live on individual brand entries; resolution is union.' ), ] = None - last_updated: AwareDatetime | None = None + last_updated: WireDatetime | None = None class BrandDiscovery5(Brand): @@ -2808,7 +2805,7 @@ class BrandDiscovery5(Brand): description="Optional pointer to the corporate house this brand belongs to. The named house's brand_refs[] MUST reciprocate for mutual-assertion trust. Single-hop only — a brand cannot itself declare brand_refs[]. Omit for standalone brands (no house)." ), ] = None - last_updated: AwareDatetime | None = None + last_updated: WireDatetime | None = None BrandDiscovery = Annotated[ diff --git a/src/adcp/types/domains/bundled/protocol/get_adcp_capabilities_response.py b/src/adcp/types/domains/bundled/protocol/get_adcp_capabilities_response.py index 9ccc22545..898fb65c7 100644 --- a/src/adcp/types/domains/bundled/protocol/get_adcp_capabilities_response.py +++ b/src/adcp/types/domains/bundled/protocol/get_adcp_capabilities_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: bundled/protocol/get_adcp_capabilities_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -11,13 +11,10 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Dict, Literal -from adcp.types.base import AdCPBaseModel, GeoPlaceSystemKey, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, GeoPlaceSystemKey, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime, WireEmail from adcp.types._scalar import ScalarFloat, ScalarInt, ScalarStr from pydantic import ( - AnyUrl, - AwareDatetime, ConfigDict, - EmailStr, Field, RootModel, StrictBool, @@ -235,7 +232,7 @@ class Authentication(AdCPBaseModel): class PushNotificationConfig(AdCPBaseModel): url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Webhook endpoint URL for task status notifications. The wire contract is unconstrained beyond `format: "uri"` — in particular, publishers SHOULD NOT enforce a destination-port allowlist by default, since buyers legitimately host receivers on non-standard TLS ports (`:9443`, `:4443`, path-routed multi-tenant gateways). The SSRF guard the protocol relies on is the IP-range check + DNS-rebinding-resistant connect pin defined in [Webhook URL validation (SSRF)](/docs/building/by-layer/L1/security#webhook-url-validation-ssrf), not port filtering. Operators who want a hardened destination-port allowlist as defense-in-depth (e.g., locked-down enterprise egress) opt in explicitly — see [Destination port: permissive by default](/docs/building/by-layer/L1/security#destination-port-permissive-by-default).' ), @@ -583,7 +580,7 @@ class CapabilityChanges(AdCPBaseModel): NoExplicitNull, ] = None last_modified: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp when the seller last changed the advertised capability document. This is the scoped replacement for legacy top-level `last_updated` when buyers need cache validation semantics.' ), @@ -696,7 +693,7 @@ class AnyOf(AdCPBaseModel): ) kind: Literal['agent_url'] = 'agent_url' agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Exact canonical agent endpoint without userinfo, query, or fragment. Redirect targets, DNS aliases, and URLs asserted by the candidate do not satisfy this matcher.' ), @@ -714,7 +711,7 @@ class AnyOf5(AdCPBaseModel): ) kind: Literal['verification'] = 'verification' registry: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Seller-configured trusted verification registry without userinfo, query, or fragment. The candidate cannot supply or override this URL; fetches use the registry-resolution security contract.' ), @@ -792,8 +789,8 @@ class DataSubjectContestation(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - url: Annotated[AnyUrl | None, NoExplicitNull] = None - email: Annotated[EmailStr | None, NoExplicitNull] = None + url: Annotated[WireAnyUrl | None, NoExplicitNull] = None + email: Annotated[WireEmail | None, NoExplicitNull] = None languages: Annotated[list[str] | None, NoExplicitNull] = None @@ -822,7 +819,7 @@ class Issuer1(AdCPBaseModel): ), ] = 'agent' agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical HTTPS endpoint of the issuing agent. Evaluators compare it using AdCP URL canonicalization rules.' ), @@ -848,7 +845,7 @@ class Issuer2(AdCPBaseModel): ), ] = 'origin' origin: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical HTTPS origin with no path, query, fragment, or userinfo. This identifies the issuer; it does not authorize a fetch.' ), @@ -863,8 +860,8 @@ class Issuer2(AdCPBaseModel): ] = None -class CredentialOrigin(RootModel[AnyUrl]): - root: AnyUrl +class CredentialOrigin(RootModel[WireAnyUrl]): + root: WireAnyUrl class Authentication5(StrEnum): @@ -878,7 +875,7 @@ class Resolver(AdCPBaseModel): ) resolver_id: Annotated[str, Field(max_length=255, min_length=1, pattern='^[A-Za-z0-9._:-]+$')] url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Evaluator-configured HTTPS resolver endpoint. Calls use POST with Content-Type application/json and a body containing only credential_id; query-string and path interpolation are forbidden. The evaluator still applies the attestation fetch contract before every call.' ), @@ -895,9 +892,9 @@ class AcceptedVerifier(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - agent_url: AnyUrl - claim_types: Annotated[list[AnyUrl] | None, Field(min_length=1), NoExplicitNull] = None - proof_formats: Annotated[list[AnyUrl] | None, Field(min_length=1), NoExplicitNull] = None + agent_url: WireAnyUrl + claim_types: Annotated[list[WireAnyUrl] | None, Field(min_length=1), NoExplicitNull] = None + proof_formats: Annotated[list[WireAnyUrl] | None, Field(min_length=1), NoExplicitNull] = None ext: Annotated[ dict[str, Any] | None, Field( @@ -1108,7 +1105,7 @@ class Account(AdCPBaseModel): NoExplicitNull, ] = False authorization_endpoint: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='OAuth authorization endpoint for obtaining operator-level credentials. Present when the seller supports OAuth for operator authentication. The agent directs the operator to this URL to authenticate and obtain a bearer token. If absent and require_operator_auth is true, operators obtain credentials out-of-band (e.g., seller portal, API key).' ), @@ -1190,7 +1187,7 @@ class AcceptancePolicyDiscovery(AdCPBaseModel): extra='forbid', ) catalog_url: Annotated[ - AnyUrl, + WireAnyUrl, Field(description='HTTPS document that validates against acceptance-policy-catalog.json.'), ] catalog_digest: Annotated[ @@ -1297,7 +1294,7 @@ class ReportingProfile(AdCPBaseModel): id: Annotated[str, Field(max_length=128, min_length=1, pattern='^[A-Za-z0-9_.:-]{1,128}$')] version: Annotated[str, Field(max_length=64, min_length=1)] schema_uri: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Authenticated seller/provider or AdCP-registry HTTPS origin only; never an IP literal, userinfo URL, redirect target, or mutable validation authority.' ), @@ -1344,7 +1341,7 @@ class ReportingProfile(AdCPBaseModel): 'application/vnd.adcp.reporting-canonicalization+json' ) canonicalization_uri: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Retrievable exact canonicalization contract on the authenticated seller/provider or AdCP-registry origin. SDKs apply the same bounded, redirect-free SSRF controls as schema_uri and verify canonicalization_sha256 before use.' ), @@ -1409,7 +1406,7 @@ class Schedule(AdCPBaseModel): ), NoExplicitNull, ] = None - period_anchor: Annotated[AwareDatetime | None, NoExplicitNull] = None + period_anchor: Annotated[WireDatetime | None, NoExplicitNull] = None period_timezone: Annotated[str | None, Field(max_length=255, min_length=1), NoExplicitNull] = ( None ) @@ -1478,14 +1475,14 @@ class OperationsContact(AdCPBaseModel): regex_engine="python-re", ) url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='HTTPS page a human uses to open or track a reporting issue, such as a support portal or status page. Same hardened origin shape as the offering document URIs: never an IP literal, userinfo URL, loopback host, AdCP task endpoint, webhook target, or credentialed link.' ), NoExplicitNull, ] = None email: Annotated[ - EmailStr | None, + WireEmail | None, Field( description='Monitored operations mailbox for reporting escalations. A role address, not an individual.', max_length=254, @@ -1516,8 +1513,8 @@ class Evidence(AdCPBaseModel): regex_engine="python-re", ) basis: Basis - generated_at: AwareDatetime - attestation_uri: Annotated[AnyUrl | None, NoExplicitNull] = None + generated_at: WireDatetime + attestation_uri: Annotated[WireAnyUrl | None, NoExplicitNull] = None attestation_sha256: Annotated[ str | None, Field(pattern='^[A-Fa-f0-9]{64}$'), NoExplicitNull ] = None @@ -1888,9 +1885,9 @@ class Countries8(StrEnum): ward = 'ward' -class Countries9(RootModel[AnyUrl]): +class Countries9(RootModel[WireAnyUrl]): root: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical place classification. Registered tokens have protocol-defined meanings. Catalog-specific classifications without a registered mapping MUST use an absolute HTTPS URI controlled by the vocabulary owner; consumers compare URI types as exact opaque strings.', examples=[ @@ -1922,7 +1919,7 @@ class Resolver2(AdCPBaseModel): extra='forbid', ) url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='HTTPS endpoint accepting the standard geo-place resolution query parameters.' ), @@ -1944,7 +1941,7 @@ class Catalog(AdCPBaseModel): extra='forbid', ) source: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional catalog dataset or derivative-source identifier. This records provenance/coverage and does not change the identifier namespace in system.' ), @@ -1999,9 +1996,9 @@ class GeoPlaces1(StrEnum): microsoft_ads = 'microsoft_ads' -class GeoPlaces2(RootModel[AnyUrl]): +class GeoPlaces2(RootModel[WireAnyUrl]): root: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Collision-safe identifier namespace for geographic places. Registered tokens have protocol-defined semantics. Unregistered systems MUST use an absolute HTTPS URI controlled by the catalog owner; consumers compare URI systems as exact opaque strings.', examples=[ @@ -2183,7 +2180,7 @@ class BuyerAgent(AdCPBaseModel): extra='forbid', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="Buyer agent's registered agent URL. Canonical identifier, mirroring trusted_match.providers[].agent_url." ), @@ -2398,7 +2395,7 @@ class SignalActivation(AdCPBaseModel): ), ] claim_types: Annotated[ - list[AnyUrl], + list[WireAnyUrl], Field( description='Signal-quality claim identifiers accepted for this governed action. Values are open absolute URIs and MUST be a subset of adcp.attestations.accepted_claim_types.', min_length=1, @@ -2453,7 +2450,7 @@ class PropertyFeature(AdCPBaseModel): NoExplicitNull, ] = None methodology_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='URL to documentation explaining how this feature is calculated or measured. Helps buyers understand and compare methodologies across vendors.' ), @@ -2490,7 +2487,7 @@ class CreativeFeature(AdCPBaseModel): NoExplicitNull, ] = None methodology_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='URL to documentation explaining how this feature is calculated or measured.' ), @@ -2541,7 +2538,7 @@ class Transport3(AdCPBaseModel): extra='allow', ) type: Annotated[Type9, Field(description='Protocol transport type')] - url: Annotated[AnyUrl, Field(description='Agent endpoint URL for this transport')] + url: Annotated[WireAnyUrl, Field(description='Agent endpoint URL for this transport')] class Endpoint(AdCPBaseModel): @@ -2705,7 +2702,7 @@ class SponsoredIntelligence(AdCPBaseModel): ), ] brand_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL to brand.json with colors, fonts, logos, tone'), NoExplicitNull, ] = None @@ -2904,7 +2901,7 @@ class V1FormatRefItem(AdCPBaseModel): extra='allow', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="URL of the agent that defines this format (e.g., 'https://creative.adcontextprotocol.org' for standard formats, or 'https://publisher.com/.well-known/adcp/sales' for custom formats). Callers comparing two `format-id` values MUST canonicalize `agent_url` per the AdCP URL canonicalization rules before treating two formats as the same. See docs/reference/url-canonicalization." ), @@ -2955,7 +2952,7 @@ class FormatSchema(AdCPBaseModel): extra='allow', ) uri: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="HTTPS URL identifying the extension. `https://` is mandatory — `http://`, `file://`, `data:`, and other schemes are rejected at the schema layer (defense-in-depth on top of the fetch-contract normative rules). The URI base is the owning agent's URL; the path identifies the extension within that agent. Example: 'https://creative.adcontextprotocol.org/translated/meta/extensions/meta_pixel'. The full fetch contract — SSRF allowlist, response-size cap, $ref sandbox, schema-compile bounds — is documented on `product-format-declaration.json#format_schema` and applies to ALL fetches of this reference shape regardless of whether the field is named `format_schema` (load-bearing for validation) or `platform_extensions` (informational); the *transport* rules are identical, only the *consumption* semantics differ." ), @@ -3211,24 +3208,24 @@ class KeyOrigins(AdCPBaseModel): extra='forbid', ) governance_signing: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='Origin (scheme + host) serving the governance-signing JWKS.'), NoExplicitNull, ] = None request_signing: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='Origin (scheme + host) serving the request-signing JWKS.'), NoExplicitNull, ] = None webhook_signing: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Origin (scheme + host) serving the JWKS used for webhook delivery. Webhooks are signed with `adcp_use: "request-signing"` keys; the deprecated `adcp_use: "webhook-signing"` value remains accepted during the backward-compatibility window.' ), NoExplicitNull, ] = None tmp_signing: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Origin (scheme + host) serving the TMP-signing JWKS, when this operator participates in TMP.' ), @@ -3259,7 +3256,7 @@ class Identity(AdCPBaseModel): extra='allow', ) brand_json_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="HTTPS URL of the operator's brand.json (typically `https://{operator-domain}/.well-known/brand.json`). Trust-root pointer for this agent's signing keys. See [security.mdx §Discovering an agent's signing keys via `brand_json_url`](https://adcontextprotocol.org/docs/building/by-layer/L1/security#discovering-an-agents-signing-keys-via-brand_json_url) for the verifier algorithm and `x-adcp-validation` for structured constraints. Distinct from `sponsored_intelligence.brand_url`, which is a rendering pointer for SI agent visuals — verifiers MUST use this field for key discovery and MUST NOT fall back to `sponsored_intelligence.brand_url` as a trust-root pointer." ), @@ -3332,7 +3329,7 @@ class Accreditation(AdCPBaseModel): NoExplicitNull, ] = None evidence_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="Optional URL pointing at the accrediting body's public listing for this certification (the buyer's path to verify the claim independently)." ), @@ -3362,7 +3359,7 @@ class Metric(AdCPBaseModel): ), ] standard_reference: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional URI pointing at the published standard this metric IMPLEMENTS (e.g., IAB Attention Measurement Guidelines, MRC Viewable Impression Measurement, GARM emissions framework). Distinct from `accreditations[]` — `standard_reference` is what the metric is built against; `accreditations[]` is third-party certification that the implementation actually conforms. Buyer agents normalizing across vendors SHOULD apply the AdCP URL canonicalization rules before comparing — vendors implementing the same standard MAY use different URL forms for the same canonical document.' ), @@ -3391,7 +3388,7 @@ class Metric(AdCPBaseModel): NoExplicitNull, ] = None methodology_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="URL to the vendor's full methodology documentation for this metric. Buyers SHOULD link or fetch this when human review of the methodology is in scope (compliance, RFP review, accreditation audit). Field name mirrors `governance.property_features[].methodology_url`." ), @@ -3670,8 +3667,8 @@ class ReportingReliabilityMeasurementPeriod(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - start: AwareDatetime - end: AwareDatetime + start: WireDatetime + end: WireDatetime class Mode17(StrEnum): @@ -3898,7 +3895,7 @@ class Role(StrEnum): class DeclaredBy(AdCPBaseModel): agent_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL of the agent or service that declared this provenance'), NoExplicitNull, ] = None @@ -3907,7 +3904,7 @@ class DeclaredBy(AdCPBaseModel): class C2pa(AdCPBaseModel): manifest_url: Annotated[ - AnyUrl, Field(description='URL to the C2PA manifest store for this content') + WireAnyUrl, Field(description='URL to the C2PA manifest store for this content') ] @@ -3921,7 +3918,7 @@ class VerifyAgent(AdCPBaseModel): extra='forbid', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="URL of the governance agent the buyer represents was used to embed/verify this layer. MUST use the `https://` scheme and MUST appear in the seller's `creative_policy.accepted_verifiers[].agent_url` list (canonicalized per /docs/reference/url-canonicalization: lowercase scheme and host, strip default port, normalize path dot-segments). Sellers MUST NOT call this URL until the canonicalized match is confirmed." ), @@ -3964,7 +3961,7 @@ class EmbeddedProvenanceItem(AdCPBaseModel): NoExplicitNull, ] = None embedded_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the provenance data was embedded (ISO 8601)'), NoExplicitNull, ] = None @@ -3982,7 +3979,7 @@ class VerifyAgent4(AdCPBaseModel): extra='forbid', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="URL of the governance agent the buyer represents was used to apply/detect this watermark. MUST use the `https://` scheme and MUST appear in the seller's `creative_policy.accepted_verifiers[].agent_url` list (canonicalized per /docs/reference/url-canonicalization: lowercase scheme and host, strip default port, normalize path dot-segments). Sellers MUST NOT call this URL until the canonicalized match is confirmed." ), @@ -4030,7 +4027,7 @@ class Watermark(AdCPBaseModel): NoExplicitNull, ] = None embedded_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the watermark was applied (ISO 8601)'), NoExplicitNull, ] = None @@ -4148,7 +4145,7 @@ class VerificationItem(AdCPBaseModel): ), ] verified_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the verification was performed (ISO 8601)'), NoExplicitNull, ] = None @@ -4161,7 +4158,7 @@ class VerificationItem(AdCPBaseModel): NoExplicitNull, ] = None details_url: Annotated[ - AnyUrl | None, Field(description='URL to the full verification report'), NoExplicitNull + WireAnyUrl | None, Field(description='URL to the full verification report'), NoExplicitNull ] = None @@ -4203,14 +4200,14 @@ class Provenance(AdCPBaseModel): NoExplicitNull, ] = None declared_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When this provenance claim was made (ISO 8601). Distinct from created_time, which records when the content itself was produced. A provenance claim may be attached well after content creation, for example when retroactively declaring AI involvement for regulatory compliance.' ), NoExplicitNull, ] = None created_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When this content was created or generated (ISO 8601)'), NoExplicitNull, ] = None @@ -4377,7 +4374,7 @@ class Logo(AdCPBaseModel): description='Discriminator identifying this as an image asset. See /schemas/creative/asset-types for the registry.' ), ] = 'image' - url: Annotated[AnyUrl, Field(description='URL to the image asset')] + url: Annotated[WireAnyUrl, Field(description='URL to the image asset')] width: Annotated[SchemaInt, Field(description='Width in pixels', ge=1)] height: Annotated[SchemaInt, Field(description='Height in pixels', ge=1)] file_size_bytes: Annotated[ @@ -4559,7 +4556,7 @@ class AcceptedIssuer(AdCPBaseModel): ), ] claim_types: Annotated[ - list[AnyUrl] | None, + list[WireAnyUrl] | None, Field( description='Optional subset of accepted_claim_types this issuer may assert. Omit to allow any globally accepted claim type for this issuer.', min_length=1, @@ -4567,7 +4564,7 @@ class AcceptedIssuer(AdCPBaseModel): NoExplicitNull, ] = None proof_formats: Annotated[ - list[AnyUrl] | None, + list[WireAnyUrl] | None, Field( description='Optional subset of accepted_proof_formats allowed for this issuer. Omit to allow any globally accepted proof format for this issuer.', min_length=1, @@ -4605,14 +4602,14 @@ class Attestations(AdCPBaseModel): extra='forbid', ) accepted_claim_types: Annotated[ - list[AnyUrl], + list[WireAnyUrl], Field( description='Open claim identifiers the evaluator is prepared to evaluate. Each value is an absolute URI. Absence means the evaluator has not advertised portable-attestation support; an empty list is not permitted.', min_length=1, ), ] accepted_proof_formats: Annotated[ - list[AnyUrl], + list[WireAnyUrl], Field( description='Open credential/proof format identifiers the evaluator can verify. Values are absolute URIs rather than a protocol enum so issuers can adopt new formats without AdCP endorsement.', min_length=1, @@ -4801,7 +4798,7 @@ class Offering(AdCPBaseModel): ), ] report_definition_uri: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Retrievable immutable reporting-report-definition.json document on the authenticated seller/provider or AdCP-registry origin.' ), @@ -5047,7 +5044,7 @@ class TranslationTarget(AdCPBaseModel): ) token: Annotated[str, Field(min_length=1)] dialect: MacroDialectFamily - dialect_namespace: Annotated[AnyUrl | None, NoExplicitNull] = None + dialect_namespace: Annotated[WireAnyUrl | None, NoExplicitNull] = None dialect_revision: Annotated[str | None, Field(min_length=1), NoExplicitNull] = None dialect_semantic: Annotated[str, Field(min_length=1)] mapping_status: MacroMappingStatus @@ -5068,7 +5065,7 @@ class MacroResolutionCapability(AdCPBaseModel): extra='forbid', ) dialect: MacroDialectFamily - dialect_namespace: Annotated[AnyUrl | None, NoExplicitNull] = None + dialect_namespace: Annotated[WireAnyUrl | None, NoExplicitNull] = None dialect_revision: Annotated[str | None, Field(min_length=1), NoExplicitNull] = None dialect_semantic: Annotated[str, Field(min_length=1)] mapping_status: MappingStatus @@ -5284,7 +5281,7 @@ class Execution(AdCPBaseModel): NoExplicitNull, ] = None axe_integrations: Annotated[ - list[AnyUrl] | None, + list[WireAnyUrl] | None, Field( deprecated=True, description='Deprecated. Legacy AXE integrations. Use trusted_match for new integrations.', @@ -5995,7 +5992,7 @@ class TranslationTarget1(AdCPBaseModel): ) token: Annotated[str, Field(min_length=1)] dialect: MacroDialectFamily - dialect_namespace: Annotated[AnyUrl | None, NoExplicitNull] = None + dialect_namespace: Annotated[WireAnyUrl | None, NoExplicitNull] = None dialect_revision: Annotated[str | None, Field(min_length=1), NoExplicitNull] = None dialect_semantic: Annotated[str, Field(min_length=1)] mapping_status: MacroMappingStatus @@ -6016,7 +6013,7 @@ class MacroResolutionCapability2(AdCPBaseModel): extra='forbid', ) dialect: MacroDialectFamily - dialect_namespace: Annotated[AnyUrl | None, NoExplicitNull] = None + dialect_namespace: Annotated[WireAnyUrl | None, NoExplicitNull] = None dialect_revision: Annotated[str | None, Field(min_length=1), NoExplicitNull] = None dialect_semantic: Annotated[str, Field(min_length=1)] mapping_status: MappingStatus @@ -6068,7 +6065,7 @@ class Format5(AdCPBaseModel): ] = None technical_requirements_complete: Annotated[StrictBool | None, NoExplicitNull] = None display_name: Annotated[str | None, NoExplicitNull] = None - sample_render_url: Annotated[AnyUrl | None, NoExplicitNull] = None + sample_render_url: Annotated[WireAnyUrl | None, NoExplicitNull] = None applies_to_channels: Annotated[list[MediaChannel] | None, NoExplicitNull] = None seller_preference: Annotated[SellerPreference | None, NoExplicitNull] = None locale_policy: Annotated[ @@ -6798,7 +6795,7 @@ class GetAdcpCapabilitiesResponse(AdCPBaseModel): NoExplicitNull, ] = None timestamp: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp when the response was generated. Useful for debugging, logging, cache validation, and tracking async operation progress.' ), @@ -6979,7 +6976,7 @@ class GetAdcpCapabilitiesResponse(AdCPBaseModel): NoExplicitNull, ] = None last_updated: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp of when capabilities were last updated. Buyers can use this for cache invalidation.' ), diff --git a/src/adcp/types/domains/collection/collection_list.py b/src/adcp/types/domains/collection/collection_list.py index 38da09cb0..b9689f6b8 100644 --- a/src/adcp/types/domains/collection/collection_list.py +++ b/src/adcp/types/domains/collection/collection_list.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: collection/collection_list.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field from ..core import account_ref, brand_ref from . import base_collection_source, collection_list_filters @@ -49,7 +49,7 @@ class CollectionList(AdCPBaseModel): NoExplicitNull, ] = None webhook_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL to receive notifications when the resolved list changes'), NoExplicitNull, ] = None @@ -62,10 +62,10 @@ class CollectionList(AdCPBaseModel): NoExplicitNull, ] = 168 created_at: Annotated[ - AwareDatetime | None, Field(description='When the list was created'), NoExplicitNull + WireDatetime | None, Field(description='When the list was created'), NoExplicitNull ] = None updated_at: Annotated[ - AwareDatetime | None, Field(description='When the list was last modified'), NoExplicitNull + WireDatetime | None, Field(description='When the list was last modified'), NoExplicitNull ] = None collection_count: Annotated[ SchemaInt | None, diff --git a/src/adcp/types/domains/collection/collection_list_changed_webhook.py b/src/adcp/types/domains/collection/collection_list_changed_webhook.py index 7199c70a3..4d1eda04a 100644 --- a/src/adcp/types/domains/collection/collection_list_changed_webhook.py +++ b/src/adcp/types/domains/collection/collection_list_changed_webhook.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: collection/collection_list_changed_webhook.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field from ..core import ext as ext_1 @@ -56,9 +56,9 @@ class CollectionListChangedWebhook(AdCPBaseModel): Field(description='Summary of changes to the resolved list'), NoExplicitNull, ] = None - resolved_at: Annotated[AwareDatetime, Field(description='When the list was re-resolved')] + resolved_at: Annotated[WireDatetime, Field(description='When the list was re-resolved')] cache_valid_until: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the consumer should refresh from the governance agent'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/collection/get_collection_list_response.py b/src/adcp/types/domains/collection/get_collection_list_response.py index 3957e7b8f..023bd820c 100644 --- a/src/adcp/types/domains/collection/get_collection_list_response.py +++ b/src/adcp/types/domains/collection/get_collection_list_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: collection/get_collection_list_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..core import content_rating as content_rating_1 from ..core import context as context_1 @@ -101,10 +101,10 @@ class GetCollectionListResponse(AdcpResponse, AdcpVersionEnvelope, ProtocolEnvel ] = None pagination: Annotated[pagination_response.PaginationResponse | None, NoExplicitNull] = None resolved_at: Annotated[ - AwareDatetime | None, Field(description='When the list was resolved'), NoExplicitNull + WireDatetime | None, Field(description='When the list was resolved'), NoExplicitNull ] = None cache_valid_until: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Cache expiration timestamp. Re-fetch the list after this time to get updated collections.' ), diff --git a/src/adcp/types/domains/collection/update_collection_list_request.py b/src/adcp/types/domains/collection/update_collection_list_request.py index a220c12e1..e0ac54417 100644 --- a/src/adcp/types/domains/collection/update_collection_list_request.py +++ b/src/adcp/types/domains/collection/update_collection_list_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: collection/update_collection_list_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from typing import Annotated -from adcp.types.base import NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..core import account_ref, brand_ref from ..core import context as context_1 @@ -52,7 +52,7 @@ class UpdateCollectionListRequest(AdcpRequest, AdcpVersionEnvelope): NoExplicitNull, ] = None webhook_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Update the webhook URL for list change notifications (set to empty string to remove). Governance agents MUST validate this URL against SSRF per docs/building/by-layer/L1/security#webhook-url-validation-ssrf.' ), diff --git a/src/adcp/types/domains/compliance/comply_test_controller_request.py b/src/adcp/types/domains/compliance/comply_test_controller_request.py index 67fad2e4e..d3c736d68 100644 --- a/src/adcp/types/domains/compliance/comply_test_controller_request.py +++ b/src/adcp/types/domains/compliance/comply_test_controller_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: compliance/comply_test_controller_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -10,9 +10,9 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat +from pydantic import ConfigDict, Field, StrictFloat from ..core import brand_ref, committed_metric from ..core import context as context_1 @@ -268,7 +268,7 @@ class Params(AdCPBaseModel): NoExplicitNull, ] = None target_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Deterministic sandbox clock target used by catalog_item_availability_probe advance_time.' ), diff --git a/src/adcp/types/domains/compliance/comply_test_controller_response.py b/src/adcp/types/domains/compliance/comply_test_controller_response.py index 24481ec89..0d38f9a40 100644 --- a/src/adcp/types/domains/compliance/comply_test_controller_response.py +++ b/src/adcp/types/domains/compliance/comply_test_controller_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: compliance/comply_test_controller_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -11,9 +11,9 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool +from pydantic import ConfigDict, Field, StrictBool from ..core import context as context_1 from ..core import ext as ext_1 @@ -133,7 +133,7 @@ class RecordedCalls1(AdCPBaseModel): ), ] url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Full URL of the outbound call (scheme + host + path + query). Treated as untrusted agent-controlled input by report renderers — see runner-output-contract.yaml > security.rendered_output_fencing.' ), @@ -200,7 +200,7 @@ class RecordedCalls1(AdCPBaseModel): NoExplicitNull, ] = None timestamp: Annotated[ - AwareDatetime, + WireDatetime, Field( description="ISO 8601 timestamp the adopter recorded the outbound call. MUST reflect the adopter's wall clock at the moment the outbound request was sent (not log-flush time), and MUST be monotonically non-decreasing across recorded_calls of a single response. Used by runners to scope assertions to a specific storyboard step's window — see runner-output-contract.yaml > validation_result for the timestamp boundary semantics." ), @@ -228,7 +228,7 @@ class RecordedCalls2(AdCPBaseModel): ), ] url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Full URL of the outbound call (scheme + host + path + query). Treated as untrusted agent-controlled input by report renderers — see runner-output-contract.yaml > security.rendered_output_fencing.' ), @@ -294,7 +294,7 @@ class RecordedCalls2(AdCPBaseModel): NoExplicitNull, ] = None timestamp: Annotated[ - AwareDatetime, + WireDatetime, Field( description="ISO 8601 timestamp the adopter recorded the outbound call. MUST reflect the adopter's wall clock at the moment the outbound request was sent (not log-flush time), and MUST be monotonically non-decreasing across recorded_calls of a single response. Used by runners to scope assertions to a specific storyboard step's window — see runner-output-contract.yaml > validation_result for the timestamp boundary semantics." ), @@ -466,7 +466,7 @@ class ComplyTestControllerResponse7(ComplyTestControllerResponse): NoExplicitNull, ] = None since_timestamp: Annotated[ - AwareDatetime, + WireDatetime, Field( description="Echo of the `since_timestamp` the runner requested (or the session-start timestamp the adopter substituted when the runner omitted it). Informational — the runner SHOULD use its own clock-bracket of when it issued the AdCP step request, not this echo, when attributing recorded_calls to a specific step. The controller is part of the adopter's claimed conformance; the echo is not adversarially trustworthy." ), diff --git a/src/adcp/types/domains/content_standards/artifact.py b/src/adcp/types/domains/content_standards/artifact.py index a74834062..4e5f2d695 100644 --- a/src/adcp/types/domains/content_standards/artifact.py +++ b/src/adcp/types/domains/content_standards/artifact.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: content_standards/artifact.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, RootModel +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, RootModel from ..core import format_id as format_id_1 from ..core import provenance as provenance_1 @@ -55,7 +55,7 @@ class Metadata(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - canonical: Annotated[AnyUrl | None, Field(description='Canonical URL'), NoExplicitNull] = None + canonical: Annotated[WireAnyUrl | None, Field(description='Canonical URL'), NoExplicitNull] = None author: Annotated[str | None, Field(description='Artifact author name'), NoExplicitNull] = None keywords: Annotated[str | None, Field(description='Artifact keywords'), NoExplicitNull] = None open_graph: Annotated[ @@ -87,7 +87,7 @@ class Identifiers(AdCPBaseModel): youtube_video_id: Annotated[ str | None, Field(description='YouTube video ID'), NoExplicitNull ] = None - rss_url: Annotated[AnyUrl | None, Field(description='RSS feed URL'), NoExplicitNull] = None + rss_url: Annotated[WireAnyUrl | None, Field(description='RSS feed URL'), NoExplicitNull] = None class AssetAccess1(AdCPBaseModel): @@ -182,7 +182,7 @@ class Assets1(AdCPBaseModel): class Assets2(AdCPBaseModel): type: Literal['image'] = 'image' - url: Annotated[AnyUrl, Field(description='Image URL')] + url: Annotated[WireAnyUrl, Field(description='Image URL')] access: Annotated[ AssetAccess | None, Field(description='Authentication for secured URLs'), NoExplicitNull ] = None @@ -205,7 +205,7 @@ class Assets2(AdCPBaseModel): class Assets3(AdCPBaseModel): type: Literal['video'] = 'video' - url: Annotated[AnyUrl, Field(description='Video URL')] + url: Annotated[WireAnyUrl, Field(description='Video URL')] access: Annotated[ AssetAccess | None, Field(description='Authentication for secured URLs'), NoExplicitNull ] = None @@ -233,7 +233,7 @@ class Assets3(AdCPBaseModel): NoExplicitNull, ] = None thumbnail_url: Annotated[ - AnyUrl | None, Field(description='Video thumbnail URL'), NoExplicitNull + WireAnyUrl | None, Field(description='Video thumbnail URL'), NoExplicitNull ] = None provenance: Annotated[ provenance_1.Provenance | None, @@ -244,7 +244,7 @@ class Assets3(AdCPBaseModel): class Assets4(AdCPBaseModel): type: Literal['audio'] = 'audio' - url: Annotated[AnyUrl, Field(description='Audio URL')] + url: Annotated[WireAnyUrl, Field(description='Audio URL')] access: Annotated[ AssetAccess | None, Field(description='Authentication for secured URLs'), NoExplicitNull ] = None @@ -321,19 +321,19 @@ class Artifact(AdCPBaseModel): NoExplicitNull, ] = None url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional URL for this artifact (web page, podcast feed, video page). Not all artifacts have URLs (e.g., Instagram content, podcast segments, TV scenes).' ), NoExplicitNull, ] = None published_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the artifact was published (ISO 8601 format)'), NoExplicitNull, ] = None last_update_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the artifact was last modified (ISO 8601 format)'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/content_standards/artifact_webhook_payload.py b/src/adcp/types/domains/content_standards/artifact_webhook_payload.py index 668cc3e35..b89f49bed 100644 --- a/src/adcp/types/domains/content_standards/artifact_webhook_payload.py +++ b/src/adcp/types/domains/content_standards/artifact_webhook_payload.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: content_standards/artifact_webhook_payload.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field from ..core import ext as ext_1 from . import artifact as artifact_1 @@ -32,7 +32,7 @@ class Pagination(AdCPBaseModel): class Artifact(AdCPBaseModel): artifact: Annotated[artifact_1.Artifact, Field(description='The content artifact')] delivered_at: Annotated[ - AwareDatetime, Field(description='When the impression was delivered (ISO 8601)') + WireDatetime, Field(description='When the impression was delivered (ISO 8601)') ] impression_id: Annotated[ str | None, @@ -69,7 +69,7 @@ class ArtifactWebhookPayload(AdCPBaseModel): ), ] timestamp: Annotated[ - AwareDatetime, Field(description='When this batch was generated (ISO 8601)') + WireDatetime, Field(description='When this batch was generated (ISO 8601)') ] artifacts: Annotated[ list[Artifact], Field(description='Content artifacts from delivered impressions') diff --git a/src/adcp/types/domains/content_standards/create_content_standards_request.py b/src/adcp/types/domains/content_standards/create_content_standards_request.py index 4f6cc84e3..bd65f84e7 100644 --- a/src/adcp/types/domains/content_standards/create_content_standards_request.py +++ b/src/adcp/types/domains/content_standards/create_content_standards_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: content_standards/create_content_standards_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, model_validator +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, model_validator from ..core import context as context_1 from ..core import ext as ext_1 @@ -54,7 +54,7 @@ class Scope(AdCPBaseModel): class Pass(AdCPBaseModel): type: Annotated[Literal['url'], Field(description='Indicates this is a URL reference')] = 'url' value: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="Full URL to a specific page (e.g., 'https://espn.com/nba/story/_/id/12345/lakers-win')" ), @@ -67,7 +67,7 @@ class Pass(AdCPBaseModel): class Fail(AdCPBaseModel): type: Annotated[Literal['url'], Field(description='Indicates this is a URL reference')] = 'url' value: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="Full URL to a specific page (e.g., 'https://news.example.com/controversial-article')" ), diff --git a/src/adcp/types/domains/content_standards/get_media_buy_artifacts_request.py b/src/adcp/types/domains/content_standards/get_media_buy_artifacts_request.py index 032313abd..cdd039be1 100644 --- a/src/adcp/types/domains/content_standards/get_media_buy_artifacts_request.py +++ b/src/adcp/types/domains/content_standards/get_media_buy_artifacts_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: content_standards/get_media_buy_artifacts_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool from ..core import account_ref from ..core import context as context_1 @@ -19,10 +19,10 @@ class TimeRange(AdCPBaseModel): start: Annotated[ - AwareDatetime | None, Field(description='Start of time range (inclusive)'), NoExplicitNull + WireDatetime | None, Field(description='Start of time range (inclusive)'), NoExplicitNull ] = None end: Annotated[ - AwareDatetime | None, Field(description='End of time range (exclusive)'), NoExplicitNull + WireDatetime | None, Field(description='End of time range (exclusive)'), NoExplicitNull ] = None diff --git a/src/adcp/types/domains/content_standards/get_media_buy_artifacts_response.py b/src/adcp/types/domains/content_standards/get_media_buy_artifacts_response.py index 598849802..75f1eb860 100644 --- a/src/adcp/types/domains/content_standards/get_media_buy_artifacts_response.py +++ b/src/adcp/types/domains/content_standards/get_media_buy_artifacts_response.py @@ -8,7 +8,8 @@ from typing import Literal, TypeAlias -from pydantic import AwareDatetime, ConfigDict +from adcp.types.base import WireDatetime +from pydantic import ConfigDict from ..core.version_envelope import AdcpVersionEnvelope from . import artifact as artifact_1 @@ -28,7 +29,7 @@ class BrandContext(AdcpVersionEnvelope): class Artifact(AdcpVersionEnvelope): model_config = ConfigDict(extra='allow') record_id: str - timestamp: AwareDatetime | None = None + timestamp: WireDatetime | None = None package_id: str | None = None artifact: artifact_1.Artifact country: str | None = None diff --git a/src/adcp/types/domains/content_standards/update_content_standards_request.py b/src/adcp/types/domains/content_standards/update_content_standards_request.py index 7a5d215f1..14ec6aec7 100644 --- a/src/adcp/types/domains/content_standards/update_content_standards_request.py +++ b/src/adcp/types/domains/content_standards/update_content_standards_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: content_standards/update_content_standards_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..core import context as context_1 from ..core import ext as ext_1 @@ -52,7 +52,7 @@ class Scope(AdCPBaseModel): class Pass(AdCPBaseModel): type: Annotated[Literal['url'], Field(description='Indicates this is a URL reference')] = 'url' value: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="Full URL to a specific page (e.g., 'https://espn.com/nba/story/_/id/12345/lakers-win')" ), @@ -65,7 +65,7 @@ class Pass(AdCPBaseModel): class Fail(AdCPBaseModel): type: Annotated[Literal['url'], Field(description='Indicates this is a URL reference')] = 'url' value: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="Full URL to a specific page (e.g., 'https://news.example.com/controversial-article')" ), diff --git a/src/adcp/types/domains/content_standards/validate_content_delivery_request.py b/src/adcp/types/domains/content_standards/validate_content_delivery_request.py index 3ab3a7476..367bbd864 100644 --- a/src/adcp/types/domains/content_standards/validate_content_delivery_request.py +++ b/src/adcp/types/domains/content_standards/validate_content_delivery_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: content_standards/validate_content_delivery_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import Field, StrictBool from ..core import context as context_1 from ..core import ext as ext_1 @@ -32,7 +32,7 @@ class Record(AdCPBaseModel): NoExplicitNull, ] = None timestamp: Annotated[ - AwareDatetime | None, Field(description='When the delivery occurred'), NoExplicitNull + WireDatetime | None, Field(description='When the delivery occurred'), NoExplicitNull ] = None artifact: Annotated[artifact_1.Artifact, Field(description='Artifact where ad was delivered')] country: Annotated[ diff --git a/src/adcp/types/domains/core/account.py b/src/adcp/types/domains/core/account.py index 3a5bd7caa..dd5253785 100644 --- a/src/adcp/types/domains/core/account.py +++ b/src/adcp/types/domains/core/account.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/account.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..enums import account_scope as account_scope_1 from ..enums import account_status, billing_party, cloud_storage_protocol @@ -30,7 +30,7 @@ class Setup(AdCPBaseModel): extra='allow', ) url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='URL where the human can complete the required action (credit application, legal agreement, add funds).' ), @@ -38,7 +38,7 @@ class Setup(AdCPBaseModel): ] = None message: Annotated[str, Field(description="Human-readable description of what's needed.")] expires_at: Annotated[ - AwareDatetime | None, Field(description='When this setup link expires.'), NoExplicitNull + WireDatetime | None, Field(description='When this setup link expires.'), NoExplicitNull ] = None @@ -46,7 +46,7 @@ class GovernanceAgent(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - url: Annotated[AnyUrl, Field(description='Governance agent endpoint URL. Must use HTTPS.')] + url: Annotated[WireAnyUrl, Field(description='Governance agent endpoint URL. Must use HTTPS.')] class Format(StrEnum): @@ -119,7 +119,7 @@ class ReportingBucket(AdCPBaseModel): ), ] setup_instructions: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='URL to documentation for configuring buyer read access to this bucket (IAM role, service account, etc.). Operator-facing documentation — buyer agents MUST NOT auto-fetch this URL; surface it to a human operator. If an implementation fetches it (for preview), apply webhook URL SSRF validation and do not pass the fetched content into an LLM context without indirect-prompt-injection guarding. See docs/media-buy/media-buys/optimization-reporting#security-considerations-for-offline-delivery.' ), diff --git a/src/adcp/types/domains/core/account_change.py b/src/adcp/types/domains/core/account_change.py index 4d3570ff1..2ff6cb3f0 100644 --- a/src/adcp/types/domains/core/account_change.py +++ b/src/adcp/types/domains/core/account_change.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/account_change.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool +from pydantic import ConfigDict, Field, StrictBool from . import ext as ext_1 @@ -156,13 +156,13 @@ class AccountChange(AdCPBaseModel): ), ] recorded_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='Time the seller committed or durably observed the change. Feed order is defined by the cursor, not by this timestamp.' ), ] occurred_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Upstream business time when the change occurred, only when the seller can establish it reliably.' ), diff --git a/src/adcp/types/domains/core/account_change_recorded_webhook.py b/src/adcp/types/domains/core/account_change_recorded_webhook.py index d4570f8c7..74721c4e9 100644 --- a/src/adcp/types/domains/core/account_change_recorded_webhook.py +++ b/src/adcp/types/domains/core/account_change_recorded_webhook.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/account_change_recorded_webhook.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from . import ext as ext_1 @@ -44,7 +44,7 @@ class AccountChangeRecordedWebhook(AdCPBaseModel): ), ] notification_type: Literal['account.change_recorded'] = 'account.change_recorded' - fired_at: AwareDatetime + fired_at: WireDatetime subscriber_id: Annotated[ str, Field(max_length=64, min_length=1, pattern='^[A-Za-z0-9_.:-]{1,64}$') ] @@ -52,7 +52,7 @@ class AccountChangeRecordedWebhook(AdCPBaseModel): change_id: Annotated[ str, Field(max_length=255, min_length=1, pattern='^[A-Za-z0-9_.:-]{1,255}$') ] - recorded_at: AwareDatetime + recorded_at: WireDatetime resource: Annotated[ Resource, Field(description='Resource identity copied from the corresponding account change record.'), diff --git a/src/adcp/types/domains/core/account_identity_change.py b/src/adcp/types/domains/core/account_identity_change.py index 69ea474ae..9fc567ead 100644 --- a/src/adcp/types/domains/core/account_identity_change.py +++ b/src/adcp/types/domains/core/account_identity_change.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/account_identity_change.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from . import operator_identity @@ -25,7 +25,7 @@ class AccountIdentityChange1(AdCPBaseModel): Field(description='Complete desired operator identity submitted by the buyer.'), ] requested_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the seller recorded the request.'), NoExplicitNull, ] = None @@ -46,7 +46,7 @@ class AccountIdentityChange2(AdCPBaseModel): Field(description='Complete desired operator identity submitted by the buyer.'), ] requested_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the seller recorded the request.'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/account_status_changed_webhook.py b/src/adcp/types/domains/core/account_status_changed_webhook.py index 7e9504653..fdb1ac7e2 100644 --- a/src/adcp/types/domains/core/account_status_changed_webhook.py +++ b/src/adcp/types/domains/core/account_status_changed_webhook.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/account_status_changed_webhook.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..enums import account_status from . import ext as ext_1 @@ -41,7 +41,7 @@ class Setup(AdCPBaseModel): ), ] expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the setup action or hint expires, if known.'), NoExplicitNull, ] = None @@ -76,7 +76,7 @@ class AccountStatusChangedWebhook(AdCPBaseModel): ), ] = 'account.status_changed' fired_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the seller initiated this fire. Distinct from observed_at, which is when the seller recorded the account transition.' ), @@ -107,7 +107,7 @@ class AccountStatusChangedWebhook(AdCPBaseModel): ), ] observed_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the seller recorded the account status transition. Used in the notification_id stability key; this is seller wall time for the transition, not webhook fire time.' ), diff --git a/src/adcp/types/domains/core/agent_notification_config.py b/src/adcp/types/domains/core/agent_notification_config.py index a800e3bec..e7c119b9d 100644 --- a/src/adcp/types/domains/core/agent_notification_config.py +++ b/src/adcp/types/domains/core/agent_notification_config.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/agent_notification_config.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool from ..enums import auth_scheme, notification_type from . import ext as ext_1 @@ -42,7 +42,7 @@ class AgentNotificationConfig(AdCPBaseModel): ), ] url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Webhook endpoint URL. Same wire contract as `push-notification-config.url` and account-level `notification-config.url`: `format: "uri"`, no destination-port allowlist enforced by the protocol, SSRF protection via the IP-range check defined in docs/building/by-layer/L1/security.mdx#webhook-url-validation-ssrf. Sellers MUST validate URL syntax, HTTPS usage, hostname normalization, and reserved-range rejection when writing any config, including `active: false` configs. Sellers MUST complete an activation challenge or equivalent proof-of-control before treating a new or changed active subscriber as active.' ), diff --git a/src/adcp/types/domains/core/agent_notification_config_state.py b/src/adcp/types/domains/core/agent_notification_config_state.py index 6eda58e1c..2dbf51b20 100644 --- a/src/adcp/types/domains/core/agent_notification_config_state.py +++ b/src/adcp/types/domains/core/agent_notification_config_state.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/agent_notification_config_state.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool from ..enums import auth_scheme, notification_type from . import ext as ext_1 @@ -27,7 +27,7 @@ class AgentNotificationConfigState(AdCPBaseModel): subscriber_id: Annotated[ str, Field(max_length=64, min_length=1, pattern='^[A-Za-z0-9_.:-]{1,64}$') ] - url: AnyUrl + url: WireAnyUrl event_types: Annotated[list[notification_type.NotificationType], Field(min_length=1)] all_authorized_accounts: Annotated[ StrictBool | None, diff --git a/src/adcp/types/domains/core/agent_reporting_destination_state.py b/src/adcp/types/domains/core/agent_reporting_destination_state.py index e213fb699..546f03108 100644 --- a/src/adcp/types/domains/core/agent_reporting_destination_state.py +++ b/src/adcp/types/domains/core/agent_reporting_destination_state.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/agent_reporting_destination_state.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field from ..enums import reporting_destination_setup_state from . import agent_reporting_destination, error @@ -33,14 +33,14 @@ class Setup(AdCPBaseModel): ) action: Action setup_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='HTTPS page for completing provider-native setup. Printable ASCII without whitespace, \'?\', \'#\', \'%\', \'"\', \'<\', or \'>\'. It MUST NOT carry a credential, userinfo authority, or signed component and MUST be rendered as an untrusted link, never executed as agent instructions.' ), NoExplicitNull, ] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Optional expiry of this setup action. A new sync obtains a fresh action after expiry.' ), diff --git a/src/adcp/types/domains/core/agent_signing_key.py b/src/adcp/types/domains/core/agent_signing_key.py index 2608be649..0135bbd92 100644 --- a/src/adcp/types/domains/core/agent_signing_key.py +++ b/src/adcp/types/domains/core/agent_signing_key.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/agent_signing_key.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field class AgentSigningKey(AdCPBaseModel): @@ -50,7 +50,7 @@ class AgentSigningKey(AdCPBaseModel): str | None, Field(description='Base64url-encoded RSA public exponent.'), NoExplicitNull ] = None revoked_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Optional revocation timestamp. When present, verifiers MUST reject any signature produced with this key whose signing epoch (or equivalent time reference) is at or after this timestamp. The key may continue to appear in the trust anchor during a grace period so caches that have not yet refreshed still find the key and can evaluate the revocation marker. Keys past their revocation can be removed once the cache TTL (recommended: 5 minutes) has elapsed across all verifiers.' ), diff --git a/src/adcp/types/domains/core/agent_webhook_challenge.py b/src/adcp/types/domains/core/agent_webhook_challenge.py index 49ae89e97..24fe627ee 100644 --- a/src/adcp/types/domains/core/agent_webhook_challenge.py +++ b/src/adcp/types/domains/core/agent_webhook_challenge.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/agent_webhook_challenge.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field class Mode(StrEnum): @@ -68,7 +68,7 @@ class AgentWebhookChallenge(AdCPBaseModel): ), ] seller_agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Exact seller agent URL whose RFC 9421 webhook profile key signs this challenge and that will send subsequent webhooks.' ), diff --git a/src/adcp/types/domains/core/app_item.py b/src/adcp/types/domains/core/app_item.py index 126a56b5b..f4c0f552e 100644 --- a/src/adcp/types/domains/core/app_item.py +++ b/src/adcp/types/domains/core/app_item.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/app_item.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field, StrictFloat from . import ext as ext_1 from . import offering_asset_group @@ -79,12 +79,12 @@ class AppItem(AdCPBaseModel): NoExplicitNull, ] = None icon_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='App icon image URL. Typically 1024×1024 px.'), NoExplicitNull, ] = None screenshots: Annotated[ - list[AnyUrl] | None, + list[WireAnyUrl] | None, Field( description='App store screenshot URLs. Used by platforms for creative generation when native store assets are not available.', min_length=1, @@ -92,19 +92,19 @@ class AppItem(AdCPBaseModel): NoExplicitNull, ] = None preview_video_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='App preview or gameplay video URL for use in video ad creatives.'), NoExplicitNull, ] = None store_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="Direct link to the app's store listing (Apple App Store or Google Play)." ), NoExplicitNull, ] = None deep_link_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="Deep link URI for re-engagement campaigns targeting existing users. Use Universal Links (iOS) or App Links (Android) where available (e.g., 'https://acmegames.com/app/level/5'). Falls back to URI scheme (e.g., 'acmegames://level/5') when universal links are not configured." ), diff --git a/src/adcp/types/domains/core/assets/asset_union.py b/src/adcp/types/domains/core/assets/asset_union.py index 7c03eab18..c491405ab 100644 --- a/src/adcp/types/domains/core/assets/asset_union.py +++ b/src/adcp/types/domains/core/assets/asset_union.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/assets/asset_union.json -# timestamp: 2026-10-04T00:59:59+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,11 +8,9 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, SchemaInt +from adcp.types.base import AdCPBaseModel, SchemaInt, WireAnyUrl, WireDatetime from adcp.types._scalar import ScalarFloat, ScalarStr from pydantic import ( - AnyUrl, - AwareDatetime, ConfigDict, Field, RootModel, @@ -65,7 +63,7 @@ class Role(StrEnum): class DeclaredBy(AdCPBaseModel): agent_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL of the agent or service that declared this provenance'), ] = None role: Annotated[Role, Field(description='Role of the declaring party in the supply chain')] @@ -73,7 +71,7 @@ class DeclaredBy(AdCPBaseModel): class C2pa(AdCPBaseModel): manifest_url: Annotated[ - AnyUrl, Field(description='URL to the C2PA manifest store for this content') + WireAnyUrl, Field(description='URL to the C2PA manifest store for this content') ] @@ -82,7 +80,7 @@ class VerifyAgent(AdCPBaseModel): extra='forbid', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="URL of the governance agent the buyer represents was used to embed/verify this layer. MUST use the `https://` scheme and MUST appear in the seller's `creative_policy.accepted_verifiers[].agent_url` list (canonicalized per /docs/reference/url-canonicalization: lowercase scheme and host, strip default port, normalize path dot-segments). Sellers MUST NOT call this URL until the canonicalized match is confirmed." ), @@ -100,7 +98,7 @@ class VerifyAgent1(AdCPBaseModel): extra='forbid', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="URL of the governance agent the buyer represents was used to apply/detect this watermark. MUST use the `https://` scheme and MUST appear in the seller's `creative_policy.accepted_verifiers[].agent_url` list (canonicalized per /docs/reference/url-canonicalization: lowercase scheme and host, strip default port, normalize path dot-segments). Sellers MUST NOT call this URL until the canonicalized match is confirmed." ), @@ -128,7 +126,7 @@ class VerificationItem(AdCPBaseModel): ), ] verified_time: Annotated[ - AwareDatetime | None, Field(description='When the verification was performed (ISO 8601)') + WireDatetime | None, Field(description='When the verification was performed (ISO 8601)') ] = None result: Annotated[Result, Field(description='Verification outcome')] confidence: Annotated[ @@ -138,7 +136,7 @@ class VerificationItem(AdCPBaseModel): ), ] = None details_url: Annotated[ - AnyUrl | None, Field(description='URL to the full verification report') + WireAnyUrl | None, Field(description='URL to the full verification report') ] = None @@ -270,9 +268,9 @@ class UnavailableBehavior(StrEnum): reject = 'reject' -class MacroBearingUrl1(RootModel[AnyUrl]): +class MacroBearingUrl1(RootModel[WireAnyUrl]): root: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Backward-compatible URL value that preserves the existing `uri-template` branch (including deep links, protocol-relative references, and RFC 6570 host variables) while additionally accepting absolute HTTP(S) URLs with byte-preserved macro delimiters not legal in a strict URI, including `%%...%%`, `[...]`, and `${...}`. Macro-bearing HTTP(S) URLs require a valid lexical authority and valid percent triplets; an AdCP verifier replaces each recognized token with an unreserved sentinel and validates the resulting absolute URI. Token semantics are established only by `macro_declarations`.', title='Macro-bearing URL', @@ -724,7 +722,7 @@ class ReferenceAsset(AdCPBaseModel): extra='allow', ) url: Annotated[ - AnyUrl, Field(description='URL to the reference asset (image, video, or document)') + WireAnyUrl, Field(description='URL to the reference asset (image, video, or document)') ] role: Annotated[ Role2, @@ -911,7 +909,7 @@ class IdentityRef(AdCPBaseModel): str | None, Field(description='Human-readable handle or username when available.') ] = None profile_url: Annotated[ - AnyUrl | None, Field(description='URL for the authoring profile, page, or channel.') + WireAnyUrl | None, Field(description='URL for the authoring profile, page, or channel.') ] = None platform_identity_id: Annotated[ str | None, Field(description='Opaque platform-native identity identifier.') @@ -938,15 +936,15 @@ class ReferenceAuthorization(AdCPBaseModel): ), ] checked_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the seller last checked the authorization state.'), ] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the authorization expires, if the platform exposes an expiry.'), ] = None authorization_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='Optional URL the buyer or creator can use to complete authorization.'), ] = None authorization_instructions: Annotated[ @@ -964,7 +962,7 @@ class PlatformExtensionRef(AdCPBaseModel): extra='allow', ) uri: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="HTTPS URL identifying the extension. `https://` is mandatory — `http://`, `file://`, `data:`, and other schemes are rejected at the schema layer (defense-in-depth on top of the fetch-contract normative rules). The URI base is the owning agent's URL; the path identifies the extension within that agent. Example: 'https://creative.adcontextprotocol.org/translated/meta/extensions/meta_pixel'. The full fetch contract — SSRF allowlist, response-size cap, $ref sandbox, schema-compile bounds — is documented on `product-format-declaration.json#format_schema` and applies to ALL fetches of this reference shape regardless of whether the field is named `format_schema` (load-bearing for validation) or `platform_extensions` (informational); the *transport* rules are identical, only the *consumption* semantics differ." ), @@ -1032,7 +1030,7 @@ class EmbeddedProvenanceItem(AdCPBaseModel): ), ] = None embedded_at: Annotated[ - AwareDatetime | None, Field(description='When the provenance data was embedded (ISO 8601)') + WireDatetime | None, Field(description='When the provenance data was embedded (ISO 8601)') ] = None @@ -1057,7 +1055,7 @@ class Watermark(AdCPBaseModel): Field(description='C2PA action classification for this watermark'), ] = None embedded_at: Annotated[ - AwareDatetime | None, Field(description='When the watermark was applied (ISO 8601)') + WireDatetime | None, Field(description='When the watermark was applied (ISO 8601)') ] = None @@ -1158,13 +1156,13 @@ class Provenance(AdCPBaseModel): ), ] = None declared_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When this provenance claim was made (ISO 8601). Distinct from created_time, which records when the content itself was produced. A provenance claim may be attached well after content creation, for example when retroactively declaring AI involvement for regulatory compliance.' ), ] = None created_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When this content was created or generated (ISO 8601)'), ] = None c2pa: Annotated[ @@ -1213,7 +1211,7 @@ class VideoAsset(AdCPBaseModel): description='Discriminator identifying this as a video asset. See /schemas/creative/asset-types for the registry.' ), ] = 'video' - url: Annotated[AnyUrl, Field(description='URL to the video asset')] + url: Annotated[WireAnyUrl, Field(description='URL to the video asset')] width: Annotated[ SchemaInt, Field( @@ -1289,13 +1287,13 @@ class VideoAsset(AdCPBaseModel): StrictFloat | None, Field(description='True peak level in dBFS') ] = None captions_url: Annotated[ - AnyUrl | None, Field(description='URL to captions file (WebVTT, SRT, etc.)') + WireAnyUrl | None, Field(description='URL to captions file (WebVTT, SRT, etc.)') ] = None transcript_url: Annotated[ - AnyUrl | None, Field(description='URL to text transcript of the video content') + WireAnyUrl | None, Field(description='URL to text transcript of the video content') ] = None audio_description_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL to audio description track for visually impaired users'), ] = None provenance: Annotated[ @@ -1316,7 +1314,7 @@ class AudioAsset(AdCPBaseModel): description='Discriminator identifying this as an audio asset. See /schemas/creative/asset-types for the registry.' ), ] = 'audio' - url: Annotated[AnyUrl, Field(description='URL to the audio asset')] + url: Annotated[WireAnyUrl, Field(description='URL to the audio asset')] duration_ms: Annotated[ SchemaInt | None, Field(description='Audio duration in milliseconds', ge=0) ] = None @@ -1350,7 +1348,7 @@ class AudioAsset(AdCPBaseModel): None ) transcript_url: Annotated[ - AnyUrl | None, Field(description='URL to text transcript of the audio content') + WireAnyUrl | None, Field(description='URL to text transcript of the audio content') ] = None provenance: Annotated[ Provenance | None, @@ -1374,7 +1372,7 @@ class MacroTranslationTarget(AdCPBaseModel): ) token: Annotated[str, Field(min_length=1)] dialect: MacroDialect - dialect_namespace: AnyUrl | None = None + dialect_namespace: WireAnyUrl | None = None dialect_revision: Annotated[str | None, Field(min_length=1)] = None dialect_semantic: Annotated[str, Field(min_length=1)] mapping_status: MacroMappingStatus @@ -1469,7 +1467,7 @@ class ZipAsset(AdCPBaseModel): description='Discriminator identifying this as a zip-bundled asset. See /schemas/creative/asset-types for the registry.' ), ] = 'zip' - url: Annotated[AnyUrl, Field(description='URL where the zip archive is hosted. Must be HTTPS.')] + url: Annotated[WireAnyUrl, Field(description='URL where the zip archive is hosted. Must be HTTPS.')] max_file_size_kb: Annotated[ SchemaInt | None, Field( @@ -1490,7 +1488,7 @@ class ZipAsset(AdCPBaseModel): ), ] = None backup_image_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Fallback image URL for environments that cannot render the bundled creative (e.g., non-HTML5 endpoints, ad blockers). Recommended for HTML5 banners.' ), @@ -1534,7 +1532,7 @@ class WebhookAsset(AdCPBaseModel): description='Discriminator identifying this as a webhook asset. See /schemas/creative/asset-types for the registry.' ), ] = 'webhook' - url: Annotated[AnyUrl, Field(description='Webhook URL to call for dynamic content')] + url: Annotated[WireAnyUrl, Field(description='Webhook URL to call for dynamic content')] method: Annotated[HttpMethod | None, Field(description='HTTP method')] = HttpMethod.POST timeout_ms: Annotated[ SchemaInt | None, @@ -1605,7 +1603,7 @@ class MacroDeclaration6(AdCPBaseModel): ] dialect: MacroDialect dialect_namespace: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Authority-controlled registry or mapping URI. Required for IAB VAST, IAB DAAST, and vendor dialects.' ), @@ -1690,7 +1688,7 @@ class DaastAsset1(AdCPBaseModel): StrictBool | None, Field(description='Whether companion display ads are included') ] = None transcript_url: Annotated[ - AnyUrl | None, Field(description='URL to text transcript of the audio content') + WireAnyUrl | None, Field(description='URL to text transcript of the audio content') ] = None provenance: Annotated[ Provenance | None, @@ -1738,7 +1736,7 @@ class MacroDeclaration7(AdCPBaseModel): ] dialect: MacroDialect dialect_namespace: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Authority-controlled registry or mapping URI. Required for IAB VAST, IAB DAAST, and vendor dialects.' ), @@ -1823,7 +1821,7 @@ class DaastAsset2(AdCPBaseModel): StrictBool | None, Field(description='Whether companion display ads are included') ] = None transcript_url: Annotated[ - AnyUrl | None, Field(description='URL to text transcript of the audio content') + WireAnyUrl | None, Field(description='URL to text transcript of the audio content') ] = None provenance: Annotated[ Provenance | None, @@ -1962,7 +1960,7 @@ class Catalog(AdCPBaseModel): ), ] url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="URL to an external catalog feed. The platform fetches and resolves items from this URL. For offering-type catalogs, the feed contains an array of Offering objects. For other types, the feed format is determined by feed_format. When omitted with type 'product', the platform uses its synced copy of the brand's product catalog." ), @@ -2052,7 +2050,7 @@ class PublishedPostAsset1(AdCPBaseModel): ), ] = 'published_post' post_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical URL for the published post. Preferred when the platform exposes a stable public or authenticated URL.' ), @@ -2076,7 +2074,7 @@ class PublishedPostAsset1(AdCPBaseModel): ), ] = None published_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the referenced post was originally published, if known.'), ] = None reference_authorization: Annotated[ @@ -2104,7 +2102,7 @@ class PublishedPostAsset2(AdCPBaseModel): ), ] = 'published_post' post_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Canonical URL for the published post. Preferred when the platform exposes a stable public or authenticated URL.' ), @@ -2128,7 +2126,7 @@ class PublishedPostAsset2(AdCPBaseModel): ), ] = None published_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the referenced post was originally published, if known.'), ] = None reference_authorization: Annotated[ @@ -2171,7 +2169,7 @@ class ImageAsset(AdCPBaseModel): description='Discriminator identifying this as an image asset. See /schemas/creative/asset-types for the registry.' ), ] = 'image' - url: Annotated[AnyUrl, Field(description='URL to the image asset')] + url: Annotated[WireAnyUrl, Field(description='URL to the image asset')] width: Annotated[SchemaInt, Field(description='Width in pixels', ge=1)] height: Annotated[SchemaInt, Field(description='Height in pixels', ge=1)] file_size_bytes: Annotated[ @@ -2241,7 +2239,7 @@ class MacroDeclaration(AdCPBaseModel): ] dialect: MacroDialect dialect_namespace: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Authority-controlled registry or mapping URI. Required for IAB VAST, IAB DAAST, and vendor dialects.' ), @@ -2337,10 +2335,10 @@ class VastAsset1(AdCPBaseModel): Field(description='Tracking events supported by this VAST tag'), ] = None captions_url: Annotated[ - AnyUrl | None, Field(description='URL to captions file (WebVTT, SRT, etc.)') + WireAnyUrl | None, Field(description='URL to captions file (WebVTT, SRT, etc.)') ] = None audio_description_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL to audio description track for visually impaired users'), ] = None provenance: Annotated[ @@ -2382,7 +2380,7 @@ class MacroDeclaration1(AdCPBaseModel): ] dialect: MacroDialect dialect_namespace: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Authority-controlled registry or mapping URI. Required for IAB VAST, IAB DAAST, and vendor dialects.' ), @@ -2478,10 +2476,10 @@ class VastAsset2(AdCPBaseModel): Field(description='Tracking events supported by this VAST tag'), ] = None captions_url: Annotated[ - AnyUrl | None, Field(description='URL to captions file (WebVTT, SRT, etc.)') + WireAnyUrl | None, Field(description='URL to captions file (WebVTT, SRT, etc.)') ] = None audio_description_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL to audio description track for visually impaired users'), ] = None provenance: Annotated[ @@ -2535,7 +2533,7 @@ class MacroDeclarationModel(AdCPBaseModel): ] dialect: MacroDialect dialect_namespace: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Authority-controlled registry or mapping URI. Required for IAB VAST, IAB DAAST, and vendor dialects.' ), @@ -2617,7 +2615,7 @@ class MacroDeclaration2(MacroDeclarationModel): ] dialect: MacroDialect dialect_namespace: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Authority-controlled registry or mapping URI. Required for IAB VAST, IAB DAAST, and vendor dialects.' ), @@ -2730,7 +2728,7 @@ class MacroDeclaration3(MacroDeclarationModel): ] dialect: MacroDialect dialect_namespace: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Authority-controlled registry or mapping URI. Required for IAB VAST, IAB DAAST, and vendor dialects.' ), @@ -2848,7 +2846,7 @@ class MacroDeclaration4(MacroDeclarationModel): ] dialect: MacroDialect dialect_namespace: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Authority-controlled registry or mapping URI. Required for IAB VAST, IAB DAAST, and vendor dialects.' ), diff --git a/src/adcp/types/domains/core/assets/audio_asset.py b/src/adcp/types/domains/core/assets/audio_asset.py index 01ca0c875..fea6c1d6d 100644 --- a/src/adcp/types/domains/core/assets/audio_asset.py +++ b/src/adcp/types/domains/core/assets/audio_asset.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/assets/audio_asset.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from enum import IntEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field, StrictFloat from ...enums import audio_channel_layout from .. import provenance as provenance_1 @@ -30,7 +30,7 @@ class AudioAsset(AdCPBaseModel): description='Discriminator identifying this as an audio asset. See /schemas/creative/asset-types for the registry.' ), ] = 'audio' - url: Annotated[AnyUrl, Field(description='URL to the audio asset')] + url: Annotated[WireAnyUrl, Field(description='URL to the audio asset')] duration_ms: Annotated[ SchemaInt | None, Field(description='Audio duration in milliseconds', ge=0), NoExplicitNull ] = None @@ -70,7 +70,7 @@ class AudioAsset(AdCPBaseModel): StrictFloat | None, Field(description='True peak level in dBFS'), NoExplicitNull ] = None transcript_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL to text transcript of the audio content'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/assets/daast_asset.py b/src/adcp/types/domains/core/assets/daast_asset.py index 5823c9807..9c50c486e 100644 --- a/src/adcp/types/domains/core/assets/daast_asset.py +++ b/src/adcp/types/domains/core/assets/daast_asset.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/assets/daast_asset.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Any, Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field, RootModel, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field, RootModel, StrictBool from ...enums import daast_tracking_event from ...enums import daast_version as daast_version_1 @@ -83,7 +83,7 @@ class MacroDeclaration(AdCPBaseModel): ] dialect: asset_union.MacroDialect dialect_namespace: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Authority-controlled registry or mapping URI. Required for IAB VAST, IAB DAAST, and vendor dialects.' ), @@ -169,7 +169,7 @@ class MacroDeclaration12(AdCPBaseModel): ] dialect: asset_union.MacroDialect dialect_namespace: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Authority-controlled registry or mapping URI. Required for IAB VAST, IAB DAAST, and vendor dialects.' ), @@ -265,7 +265,7 @@ class DaastAsset3(AdCPBaseModel): NoExplicitNull, ] = None transcript_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL to text transcript of the audio content'), NoExplicitNull, ] = None @@ -327,7 +327,7 @@ class DaastAsset4(AdCPBaseModel): NoExplicitNull, ] = None transcript_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL to text transcript of the audio content'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/assets/display_tag_asset.py b/src/adcp/types/domains/core/assets/display_tag_asset.py index 4423f1a7f..4ef590ca1 100644 --- a/src/adcp/types/domains/core/assets/display_tag_asset.py +++ b/src/adcp/types/domains/core/assets/display_tag_asset.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/assets/display_tag_asset.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Any, Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field, RootModel, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field, RootModel, StrictBool from .. import macro_bearing_url from .. import provenance as provenance_1 @@ -81,7 +81,7 @@ class MacroDeclaration(MacroDeclaration_1): ] dialect: asset_union.MacroDialect dialect_namespace: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Authority-controlled registry or mapping URI. Required for IAB VAST, IAB DAAST, and vendor dialects.' ), @@ -201,7 +201,7 @@ class MacroDeclaration15(MacroDeclaration_1): ] dialect: asset_union.MacroDialect dialect_namespace: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Authority-controlled registry or mapping URI. Required for IAB VAST, IAB DAAST, and vendor dialects.' ), @@ -326,7 +326,7 @@ class MacroDeclaration16(MacroDeclaration_1): ] dialect: asset_union.MacroDialect dialect_namespace: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Authority-controlled registry or mapping URI. Required for IAB VAST, IAB DAAST, and vendor dialects.' ), diff --git a/src/adcp/types/domains/core/assets/image_asset.py b/src/adcp/types/domains/core/assets/image_asset.py index 15130540d..239b07a5d 100644 --- a/src/adcp/types/domains/core/assets/image_asset.py +++ b/src/adcp/types/domains/core/assets/image_asset.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/assets/image_asset.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl from adcp.types._scalar import ScalarFloat -from pydantic import AnyUrl, ConfigDict, Field, StrictFloat +from pydantic import ConfigDict, Field, StrictFloat from .. import provenance as provenance_1 @@ -28,7 +28,7 @@ class ImageAsset(AdCPBaseModel): description='Discriminator identifying this as an image asset. See /schemas/creative/asset-types for the registry.' ), ] = 'image' - url: Annotated[AnyUrl, Field(description='URL to the image asset')] + url: Annotated[WireAnyUrl, Field(description='URL to the image asset')] width: Annotated[SchemaInt, Field(description='Width in pixels', ge=1)] height: Annotated[SchemaInt, Field(description='Height in pixels', ge=1)] file_size_bytes: Annotated[ diff --git a/src/adcp/types/domains/core/assets/published_post_asset.py b/src/adcp/types/domains/core/assets/published_post_asset.py index 20e9df09f..f10c442a1 100644 --- a/src/adcp/types/domains/core/assets/published_post_asset.py +++ b/src/adcp/types/domains/core/assets/published_post_asset.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/assets/published_post_asset.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, model_validator +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, model_validator from .. import provenance as provenance_1 @@ -26,7 +26,7 @@ class IdentityRef(AdCPBaseModel): NoExplicitNull, ] = None profile_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL for the authoring profile, page, or channel.'), NoExplicitNull, ] = None @@ -55,17 +55,17 @@ class ReferenceAuthorization(AdCPBaseModel): ), ] checked_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the seller last checked the authorization state.'), NoExplicitNull, ] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the authorization expires, if the platform exposes an expiry.'), NoExplicitNull, ] = None authorization_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='Optional URL the buyer or creator can use to complete authorization.'), NoExplicitNull, ] = None @@ -87,7 +87,7 @@ class PublishedPostAsset(AdCPBaseModel): ), ] = 'published_post' post_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Canonical URL for the published post. Preferred when the platform exposes a stable public or authenticated URL.' ), @@ -115,7 +115,7 @@ class PublishedPostAsset(AdCPBaseModel): NoExplicitNull, ] = None published_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the referenced post was originally published, if known.'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/assets/vast_asset.py b/src/adcp/types/domains/core/assets/vast_asset.py index 6f85ec7a9..194b293a3 100644 --- a/src/adcp/types/domains/core/assets/vast_asset.py +++ b/src/adcp/types/domains/core/assets/vast_asset.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/assets/vast_asset.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Any, Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field, RootModel, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field, RootModel, StrictBool from ...enums import vast_tracking_event from ...enums import vast_version as vast_version_1 @@ -83,7 +83,7 @@ class MacroDeclaration(AdCPBaseModel): ] dialect: asset_union.MacroDialect dialect_namespace: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Authority-controlled registry or mapping URI. Required for IAB VAST, IAB DAAST, and vendor dialects.' ), @@ -189,10 +189,10 @@ class VastAsset3(AdCPBaseModel): NoExplicitNull, ] = None captions_url: Annotated[ - AnyUrl | None, Field(description='URL to captions file (WebVTT, SRT, etc.)'), NoExplicitNull + WireAnyUrl | None, Field(description='URL to captions file (WebVTT, SRT, etc.)'), NoExplicitNull ] = None audio_description_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL to audio description track for visually impaired users'), NoExplicitNull, ] = None @@ -236,7 +236,7 @@ class MacroDeclaration20(AdCPBaseModel): ] dialect: asset_union.MacroDialect dialect_namespace: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Authority-controlled registry or mapping URI. Required for IAB VAST, IAB DAAST, and vendor dialects.' ), @@ -342,10 +342,10 @@ class VastAsset4(AdCPBaseModel): NoExplicitNull, ] = None captions_url: Annotated[ - AnyUrl | None, Field(description='URL to captions file (WebVTT, SRT, etc.)'), NoExplicitNull + WireAnyUrl | None, Field(description='URL to captions file (WebVTT, SRT, etc.)'), NoExplicitNull ] = None audio_description_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL to audio description track for visually impaired users'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/assets/video_asset.py b/src/adcp/types/domains/core/assets/video_asset.py index eba2a43cc..fc32b8db4 100644 --- a/src/adcp/types/domains/core/assets/video_asset.py +++ b/src/adcp/types/domains/core/assets/video_asset.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/assets/video_asset.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ...enums import audio_channel_layout from ...enums import frame_rate_type as frame_rate_type_1 @@ -63,7 +63,7 @@ class VideoAsset(AdCPBaseModel): description='Discriminator identifying this as a video asset. See /schemas/creative/asset-types for the registry.' ), ] = 'video' - url: Annotated[AnyUrl, Field(description='URL to the video asset')] + url: Annotated[WireAnyUrl, Field(description='URL to the video asset')] width: Annotated[ SchemaInt, Field( @@ -164,15 +164,15 @@ class VideoAsset(AdCPBaseModel): StrictFloat | None, Field(description='True peak level in dBFS'), NoExplicitNull ] = None captions_url: Annotated[ - AnyUrl | None, Field(description='URL to captions file (WebVTT, SRT, etc.)'), NoExplicitNull + WireAnyUrl | None, Field(description='URL to captions file (WebVTT, SRT, etc.)'), NoExplicitNull ] = None transcript_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL to text transcript of the video content'), NoExplicitNull, ] = None audio_description_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL to audio description track for visually impaired users'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/assets/webhook_asset.py b/src/adcp/types/domains/core/assets/webhook_asset.py index e30b32d79..5e6424488 100644 --- a/src/adcp/types/domains/core/assets/webhook_asset.py +++ b/src/adcp/types/domains/core/assets/webhook_asset.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/assets/webhook_asset.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field from ...enums import http_method, universal_macro, webhook_response_type, webhook_security_method from .. import provenance as provenance_1 @@ -37,7 +37,7 @@ class WebhookAsset(AdCPBaseModel): description='Discriminator identifying this as a webhook asset. See /schemas/creative/asset-types for the registry.' ), ] = 'webhook' - url: Annotated[AnyUrl, Field(description='Webhook URL to call for dynamic content')] + url: Annotated[WireAnyUrl, Field(description='Webhook URL to call for dynamic content')] method: Annotated[ http_method.HttpMethod | None, Field(description='HTTP method'), NoExplicitNull ] = http_method.HttpMethod.POST diff --git a/src/adcp/types/domains/core/assets/zip_asset.py b/src/adcp/types/domains/core/assets/zip_asset.py index a3c7fe7cf..c70c5aa6e 100644 --- a/src/adcp/types/domains/core/assets/zip_asset.py +++ b/src/adcp/types/domains/core/assets/zip_asset.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/assets/zip_asset.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool from .. import provenance as provenance_1 @@ -47,7 +47,7 @@ class ZipAsset(AdCPBaseModel): description='Discriminator identifying this as a zip-bundled asset. See /schemas/creative/asset-types for the registry.' ), ] = 'zip' - url: Annotated[AnyUrl, Field(description='URL where the zip archive is hosted. Must be HTTPS.')] + url: Annotated[WireAnyUrl, Field(description='URL where the zip archive is hosted. Must be HTTPS.')] max_file_size_kb: Annotated[ SchemaInt | None, Field( @@ -71,7 +71,7 @@ class ZipAsset(AdCPBaseModel): NoExplicitNull, ] = None backup_image_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Fallback image URL for environments that cannot render the bundled creative (e.g., non-HTML5 endpoints, ad blockers). Recommended for HTML5 banners.' ), diff --git a/src/adcp/types/domains/core/async_response_refs/media_buy/get_products_async_response_submitted.py b/src/adcp/types/domains/core/async_response_refs/media_buy/get_products_async_response_submitted.py index b76ef7ad8..f11eea3e6 100644 --- a/src/adcp/types/domains/core/async_response_refs/media_buy/get_products_async_response_submitted.py +++ b/src/adcp/types/domains/core/async_response_refs/media_buy/get_products_async_response_submitted.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/async_response_refs/media_buy/get_products_async_response_submitted.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ... import context as context_1 from ... import error @@ -39,7 +39,7 @@ class GetProductsSubmitted(AdCPBaseModel): NoExplicitNull, ] = None estimated_completion: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Estimated completion time for the search'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/async_response_refs/signals/get_signals_async_response_submitted.py b/src/adcp/types/domains/core/async_response_refs/signals/get_signals_async_response_submitted.py index de2e72d89..dc8535824 100644 --- a/src/adcp/types/domains/core/async_response_refs/signals/get_signals_async_response_submitted.py +++ b/src/adcp/types/domains/core/async_response_refs/signals/get_signals_async_response_submitted.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/async_response_refs/signals/get_signals_async_response_submitted.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ... import context as context_1 from ... import error @@ -39,7 +39,7 @@ class GetSignalsSubmitted(AdCPBaseModel): NoExplicitNull, ] = None estimated_completion: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Estimated completion time for the signal discovery task.'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/attestation_capabilities.py b/src/adcp/types/domains/core/attestation_capabilities.py index e72a95e99..8f61fae29 100644 --- a/src/adcp/types/domains/core/attestation_capabilities.py +++ b/src/adcp/types/domains/core/attestation_capabilities.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/attestation_capabilities.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field, RootModel +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field, RootModel from . import attestation_issuer from . import ext as ext_1 @@ -20,8 +20,8 @@ class SupportedDeliveryMethod(StrEnum): embedded = 'embedded' -class CredentialOrigin(RootModel[AnyUrl]): - root: AnyUrl +class CredentialOrigin(RootModel[WireAnyUrl]): + root: WireAnyUrl class Authentication(StrEnum): @@ -35,7 +35,7 @@ class Resolver(AdCPBaseModel): ) resolver_id: Annotated[str, Field(max_length=255, min_length=1, pattern='^[A-Za-z0-9._:-]+$')] url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Evaluator-configured HTTPS resolver endpoint. Calls use POST with Content-Type application/json and a body containing only credential_id; query-string and path interpolation are forbidden. The evaluator still applies the attestation fetch contract before every call.' ), @@ -52,9 +52,9 @@ class AcceptedVerifier(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - agent_url: AnyUrl - claim_types: Annotated[list[AnyUrl] | None, Field(min_length=1), NoExplicitNull] = None - proof_formats: Annotated[list[AnyUrl] | None, Field(min_length=1), NoExplicitNull] = None + agent_url: WireAnyUrl + claim_types: Annotated[list[WireAnyUrl] | None, Field(min_length=1), NoExplicitNull] = None + proof_formats: Annotated[list[WireAnyUrl] | None, Field(min_length=1), NoExplicitNull] = None ext: Annotated[ext_1.ExtensionObject | None, NoExplicitNull] = None @@ -64,7 +64,7 @@ class AcceptedIssuer(AdCPBaseModel): ) issuer: attestation_issuer.AttestationIssuer claim_types: Annotated[ - list[AnyUrl] | None, + list[WireAnyUrl] | None, Field( description='Optional subset of accepted_claim_types this issuer may assert. Omit to allow any globally accepted claim type for this issuer.', min_length=1, @@ -72,7 +72,7 @@ class AcceptedIssuer(AdCPBaseModel): NoExplicitNull, ] = None proof_formats: Annotated[ - list[AnyUrl] | None, + list[WireAnyUrl] | None, Field( description='Optional subset of accepted_proof_formats allowed for this issuer. Omit to allow any globally accepted proof format for this issuer.', min_length=1, @@ -103,14 +103,14 @@ class AttestationCapabilities(AdCPBaseModel): extra='forbid', ) accepted_claim_types: Annotated[ - list[AnyUrl], + list[WireAnyUrl], Field( description='Open claim identifiers the evaluator is prepared to evaluate. Each value is an absolute URI. Absence means the evaluator has not advertised portable-attestation support; an empty list is not permitted.', min_length=1, ), ] accepted_proof_formats: Annotated[ - list[AnyUrl], + list[WireAnyUrl], Field( description='Open credential/proof format identifiers the evaluator can verify. Values are absolute URIs rather than a protocol enum so issuers can adopt new formats without AdCP endorsement.', min_length=1, diff --git a/src/adcp/types/domains/core/attestation_evaluation.py b/src/adcp/types/domains/core/attestation_evaluation.py index 5521a0fa8..a3c248255 100644 --- a/src/adcp/types/domains/core/attestation_evaluation.py +++ b/src/adcp/types/domains/core/attestation_evaluation.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/attestation_evaluation.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictFloat +from pydantic import ConfigDict, Field, StrictFloat from . import ext as ext_1 @@ -39,7 +39,7 @@ class ActionBinding(AdCPBaseModel): extra='forbid', ) action_type: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Absolute URI naming the action vocabulary, such as an AdCP governance-check or audience-evidence snapshot type.' ), @@ -83,7 +83,7 @@ class AttestationEvaluation(AdCPBaseModel): NoExplicitNull, ] = None proof_format: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Absolute URI identifying the credential/proof format used to interpret and hash obtained credential bytes. REQUIRED whenever credential_digest is present.' ), @@ -95,15 +95,15 @@ class AttestationEvaluation(AdCPBaseModel): description='Stable evaluation outcome. verified means all required issuer, signature, claim type, subject, consuming-action scope, validity, revocation, and digest checks passed under evaluator policy. subject_mismatch covers either a credential/presentation subject mismatch or a presentation/consuming-action subject mismatch. Other values are non-authoritative failures and MUST NOT be treated as verified.' ), ] - evaluated_at: AwareDatetime + evaluated_at: WireDatetime evaluated_by: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical agent URL of the evaluator-of-record that produced this result, even when it delegated proof checking to an accepted verifier.' ), ] verifier_agent_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Accepted verifier agent actually used, when verification was delegated. Omit for local verification or when no verifier was called.' ), @@ -127,14 +127,14 @@ class AttestationEvaluation(AdCPBaseModel): NoExplicitNull, ] = None revocation_checked_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the evaluator last checked authoritative revocation state. Its freshness is governed by proof format and evaluator policy.' ), NoExplicitNull, ] = None valid_until: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Upper bound on reuse of this evaluation, considering credential expiry, revocation-cache TTL, and evaluator policy. Consumers MUST re-evaluate after this time.' ), diff --git a/src/adcp/types/domains/core/attestation_issuer.py b/src/adcp/types/domains/core/attestation_issuer.py index 2009ac580..85cd9f008 100644 --- a/src/adcp/types/domains/core/attestation_issuer.py +++ b/src/adcp/types/domains/core/attestation_issuer.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/attestation_issuer.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from . import brand_ref from . import ext as ext_1 @@ -24,7 +24,7 @@ class AttestationIssuer2(AdCPBaseModel): ), ] = 'agent' agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical HTTPS endpoint of the issuing agent. Evaluators compare it using AdCP URL canonicalization rules.' ), @@ -43,7 +43,7 @@ class AttestationIssuer3(AdCPBaseModel): ), ] = 'origin' origin: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical HTTPS origin with no path, query, fragment, or userinfo. This identifies the issuer; it does not authorize a fetch.' ), diff --git a/src/adcp/types/domains/core/attestation_reference.py b/src/adcp/types/domains/core/attestation_reference.py index 74760a7a7..b70537394 100644 --- a/src/adcp/types/domains/core/attestation_reference.py +++ b/src/adcp/types/domains/core/attestation_reference.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/attestation_reference.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, model_validator +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, model_validator from . import attestation_issuer, attestation_subject from . import ext as ext_1 @@ -20,15 +20,15 @@ class ValidityHint(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - not_before: Annotated[AwareDatetime | None, NoExplicitNull] = None - expires_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + not_before: Annotated[WireDatetime | None, NoExplicitNull] = None + expires_at: Annotated[WireDatetime | None, NoExplicitNull] = None class VerifyAgent(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - agent_url: AnyUrl + agent_url: WireAnyUrl class Locator(AdCPBaseModel): @@ -37,7 +37,7 @@ class Locator(AdCPBaseModel): ) type: Literal['credential_uri'] = 'credential_uri' credential_uri: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='HTTPS URI of the credential. Before any fetch, evaluators MUST match its canonical origin to credential_origins on the accepted issuer capability and then apply the attestation fetch contract.' ), @@ -75,7 +75,7 @@ class EmbeddedCredential(AdCPBaseModel): extra='forbid', ) format: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Open, absolute URI identifying the credential/proof format. AdCP does not impose a universal issuer payload schema.' ), @@ -96,7 +96,7 @@ class AttestationReference(AdCPBaseModel): ) issuer: attestation_issuer.AttestationIssuer claim_type: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Open, absolute URI identifying the claim vocabulary. The URI is an identifier and need not be dereferenceable. AdCP does not maintain an enum of approved claims.' ), diff --git a/src/adcp/types/domains/core/attestation_subject.py b/src/adcp/types/domains/core/attestation_subject.py index 1519bf545..9b74d9dbf 100644 --- a/src/adcp/types/domains/core/attestation_subject.py +++ b/src/adcp/types/domains/core/attestation_subject.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/attestation_subject.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from . import brand_ref from . import ext as ext_1 @@ -19,7 +19,7 @@ class AttestationSubject2(AdCPBaseModel): ) type: Literal['agent'] = 'agent' agent_url: Annotated[ - AnyUrl, Field(description='Canonical HTTPS endpoint of the agent the claim concerns.') + WireAnyUrl, Field(description='Canonical HTTPS endpoint of the agent the claim concerns.') ] ext: Annotated[ext_1.ExtensionObject | None, NoExplicitNull] = None @@ -30,13 +30,13 @@ class AttestationSubject3(AdCPBaseModel): ) type: Literal['resource'] = 'resource' resource_type: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Open, absolute URI naming the subject vocabulary, such as https://adcontextprotocol.org/claims/subjects/signal. AdCP does not maintain an exhaustive enum.' ), ] namespace: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Absolute URI identifying the namespace in which id is unique. This may be an AdCP agent endpoint, a catalog origin, or a domain-specific namespace URI.' ), diff --git a/src/adcp/types/domains/core/audience_activation_method.py b/src/adcp/types/domains/core/audience_activation_method.py index 35393c94e..f60a0370d 100644 --- a/src/adcp/types/domains/core/audience_activation_method.py +++ b/src/adcp/types/domains/core/audience_activation_method.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/audience_activation_method.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field from ..enums import cloud_storage_protocol from . import brand_ref @@ -26,7 +26,7 @@ class BuyerAgent(AdCPBaseModel): extra='forbid', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="Buyer agent's registered agent URL. Canonical identifier, mirroring trusted_match.providers[].agent_url." ), diff --git a/src/adcp/types/domains/core/audience_characteristic.py b/src/adcp/types/domains/core/audience_characteristic.py index e2ca29480..bd6bb2eb6 100644 --- a/src/adcp/types/domains/core/audience_characteristic.py +++ b/src/adcp/types/domains/core/audience_characteristic.py @@ -1,21 +1,21 @@ # generated by datamodel-codegen: # filename: core/audience_characteristic.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Any, Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, RootModel, StrictBool, StrictFloat, model_validator +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, RootModel, StrictBool, StrictFloat, model_validator from pydantic_core import PydanticCustomError -class Dimension(RootModel[AnyUrl]): +class Dimension(RootModel[WireAnyUrl]): root: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical dimension name or HTTPS URI. AdCP 3.2 defines `age`; other values remain provider- or taxonomy-scoped.' ), @@ -47,7 +47,7 @@ class Taxonomy(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - system: AnyUrl + system: WireAnyUrl version: Annotated[str | None, Field(min_length=1), NoExplicitNull] = None value_id: Annotated[str | None, Field(min_length=1), NoExplicitNull] = None diff --git a/src/adcp/types/domains/core/audience_evidence.py b/src/adcp/types/domains/core/audience_evidence.py index a6a263e09..f29545a1f 100644 --- a/src/adcp/types/domains/core/audience_evidence.py +++ b/src/adcp/types/domains/core/audience_evidence.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/audience_evidence.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Any, Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, RootModel, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, RootModel, StrictFloat from ..enums import audience_evidence_methodology, audience_resolution_method, audience_subject_type from . import audience_characteristic, brand_ref, date_range @@ -32,7 +32,7 @@ class Baseline(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - system: AnyUrl + system: WireAnyUrl population_id: Annotated[str, Field(min_length=1)] version: Annotated[str, Field(min_length=1)] description: Annotated[str | None, NoExplicitNull] = None @@ -50,7 +50,7 @@ class Subject12(AdCPBaseModel): ) type: Literal['agent'] = 'agent' agent_url: Annotated[ - AnyUrl, Field(description='Canonical HTTPS endpoint of the agent the claim concerns.') + WireAnyUrl, Field(description='Canonical HTTPS endpoint of the agent the claim concerns.') ] ext: Annotated[ext_1.ExtensionObject | None, NoExplicitNull] = None @@ -61,13 +61,13 @@ class Subject13(AdCPBaseModel): ) type: Literal['resource'] = 'resource' resource_type: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Open, absolute URI naming the subject vocabulary, such as https://adcontextprotocol.org/claims/subjects/signal. AdCP does not maintain an exhaustive enum.' ), ] namespace: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Absolute URI identifying the namespace in which id is unique. This may be an AdCP agent endpoint, a catalog origin, or a domain-specific namespace URI.' ), @@ -107,7 +107,7 @@ class Subject24(AdCPBaseModel): resource_type: Literal['https://adcontextprotocol.org/claims/subjects/audience-evidence'] = 'https://adcontextprotocol.org/claims/subjects/audience-evidence' content_digest: Annotated[str, Field(pattern='^sha256:[a-f0-9]{64}$')] agent_url: Annotated[ - AnyUrl, Field(description='Canonical HTTPS endpoint of the agent the claim concerns.') + WireAnyUrl, Field(description='Canonical HTTPS endpoint of the agent the claim concerns.') ] ext: Annotated[ext_1.ExtensionObject | None, NoExplicitNull] = None @@ -140,7 +140,7 @@ class Subject34(AdCPBaseModel): resource_type: Literal['https://adcontextprotocol.org/claims/subjects/audience-evidence'] = 'https://adcontextprotocol.org/claims/subjects/audience-evidence' content_digest: Annotated[str, Field(pattern='^sha256:[a-f0-9]{64}$')] namespace: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Absolute URI identifying the namespace in which id is unique. This may be an AdCP agent endpoint, a catalog origin, or a domain-specific namespace URI.' ), @@ -161,13 +161,13 @@ class Subject36(Subject32): extra='forbid', ) resource_type: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Open, absolute URI naming the subject vocabulary, such as https://adcontextprotocol.org/claims/subjects/signal. AdCP does not maintain an exhaustive enum.' ), ] namespace: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Absolute URI identifying the namespace in which id is unique. This may be an AdCP agent endpoint, a catalog origin, or a domain-specific namespace URI.' ), @@ -273,7 +273,7 @@ class Subject25(Subject21): extra='forbid', ) agent_url: Annotated[ - AnyUrl, Field(description='Canonical HTTPS endpoint of the agent the claim concerns.') + WireAnyUrl, Field(description='Canonical HTTPS endpoint of the agent the claim concerns.') ] @@ -315,13 +315,13 @@ class Subject35(Subject31): extra='forbid', ) resource_type: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Open, absolute URI naming the subject vocabulary, such as https://adcontextprotocol.org/claims/subjects/signal. AdCP does not maintain an exhaustive enum.' ), ] namespace: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Absolute URI identifying the namespace in which id is unique. This may be an AdCP agent endpoint, a catalog origin, or a domain-specific namespace URI.' ), @@ -462,11 +462,11 @@ class AudienceEvidence(AdCPBaseModel): NoExplicitNull, ] = None last_updated: Annotated[ - AwareDatetime, + WireDatetime, Field(description='When the provider last produced or refreshed this immutable snapshot.'), ] methodology_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Documentation for interpreting the methodology. It is disclosure, not executable routing.' ), diff --git a/src/adcp/types/domains/core/audience_evidence_requirements.py b/src/adcp/types/domains/core/audience_evidence_requirements.py index d06b227d8..f409c29f0 100644 --- a/src/adcp/types/domains/core/audience_evidence_requirements.py +++ b/src/adcp/types/domains/core/audience_evidence_requirements.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/audience_evidence_requirements.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..enums import audience_evidence_methodology, audience_resolution_method, audience_subject_type from . import attestation_issuer, brand_ref @@ -137,7 +137,7 @@ class AudienceEvidenceRequirements(AdCPBaseModel): NoExplicitNull, ] = None accepted_attestation_claim_types: Annotated[ - list[AnyUrl] | None, + list[WireAnyUrl] | None, Field( description='Optional acceptable claim types when attestation is used. Buyer-supplied claim types further constrain but never broaden seller policy.', min_length=1, diff --git a/src/adcp/types/domains/core/audience_source.py b/src/adcp/types/domains/core/audience_source.py index 7d709856c..1ff0c0ce3 100644 --- a/src/adcp/types/domains/core/audience_source.py +++ b/src/adcp/types/domains/core/audience_source.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/audience_source.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from . import brand_ref @@ -30,7 +30,7 @@ class AudienceSource1(AdCPBaseModel): ), ] access_expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 time after which the buyer will revoke the grant. Declarative — tells the seller when the pipe closes so re-read behavior is predictable. Expiry bounds the access window, not retention: revocation does not claw back matched membership, and retention remains governed by the buyer-seller data processing agreement.' ), diff --git a/src/adcp/types/domains/core/authorized_agent_base.py b/src/adcp/types/domains/core/authorized_agent_base.py index 91a2594bd..e3d480690 100644 --- a/src/adcp/types/domains/core/authorized_agent_base.py +++ b/src/adcp/types/domains/core/authorized_agent_base.py @@ -1,20 +1,20 @@ # generated by datamodel-codegen: # filename: core/authorized_agent_base.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import Field from . import agent_encryption_key, agent_signing_key class AuthorizedAgentBaseFields(AdCPBaseModel): url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="The authorized agent's protocol endpoint URL: the exact agent URL buyers are given and send AdCP requests to, including the path (e.g., `https://agent.example.com/mcp`). A bare origin matches only an agent served at the root. An agent reachable at more than one agent URL (for example, separate MCP and A2A endpoints) needs one entry per URL. Callers comparing this URL against a registry (sales-agent list, signal-provider registry, TMP provider lookup, etc.) MUST canonicalize both sides per the AdCP URL canonicalization rules, not byte-equality — two URLs that differ only in case, default port, or percent-encoding of unreserved characters are the same agent. See docs/reference/url-canonicalization." ), @@ -44,7 +44,7 @@ class AuthorizedAgentBaseFields(AdCPBaseModel): NoExplicitNull, ] = None last_updated: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Optional ISO 8601 timestamp indicating when this `authorized_agents[]` entry last changed. Independent of the file-level `last_updated`. Lets validators perform a partial walk by skipping entries whose `last_updated` is older than their indexed value. Advisory — consumers MAY ignore and re-index the full file.' ), diff --git a/src/adcp/types/domains/core/brand_ref.py b/src/adcp/types/domains/core/brand_ref.py index 7ef0dfc49..b23f6a3a6 100644 --- a/src/adcp/types/domains/core/brand_ref.py +++ b/src/adcp/types/domains/core/brand_ref.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/brand_ref.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireEmail from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, ConfigDict, EmailStr, Field +from pydantic import ConfigDict, Field from . import brand_id as brand_id_1 from .assets import image_asset @@ -23,8 +23,8 @@ class DataSubjectContestation(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - url: Annotated[AnyUrl | None, NoExplicitNull] = None - email: Annotated[EmailStr | None, NoExplicitNull] = None + url: Annotated[WireAnyUrl | None, NoExplicitNull] = None + email: Annotated[WireEmail | None, NoExplicitNull] = None languages: Annotated[list[str] | None, NoExplicitNull] = None diff --git a/src/adcp/types/domains/core/brand_response_authorization_result.py b/src/adcp/types/domains/core/brand_response_authorization_result.py index 21271220e..1ba98cb57 100644 --- a/src/adcp/types/domains/core/brand_response_authorization_result.py +++ b/src/adcp/types/domains/core/brand_response_authorization_result.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/brand_response_authorization_result.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field class Trust(StrEnum): @@ -44,7 +44,7 @@ class BrandResponseAuthorizationResult1(AdCPBaseModel): ] = None kid: Annotated[str, Field(description='JWS kid evaluated by the cross-check.', min_length=1)] jwks_uri: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='JWKS URI selected only from the matched brand.json agent entry, or its same-origin default.' ), @@ -68,7 +68,7 @@ class BrandResponseAuthorizationResult2(AdCPBaseModel): NoExplicitNull, ] = None jwks_uri: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='JWKS URI selected only from the matched brand.json agent entry, or its same-origin default.' ), diff --git a/src/adcp/types/domains/core/business_entity.py b/src/adcp/types/domains/core/business_entity.py index c1cbf241d..195f58bef 100644 --- a/src/adcp/types/domains/core/business_entity.py +++ b/src/adcp/types/domains/core/business_entity.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/business_entity.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import ConfigDict, EmailStr, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireEmail +from pydantic import ConfigDict, Field from . import ext as ext_1 @@ -47,7 +47,7 @@ class Contact(AdCPBaseModel): name: Annotated[ str | None, Field(description='Full name of the contact', max_length=200), NoExplicitNull ] = None - email: Annotated[EmailStr | None, Field(max_length=254), NoExplicitNull] = None + email: Annotated[WireEmail | None, Field(max_length=254), NoExplicitNull] = None phone: Annotated[str | None, Field(max_length=30), NoExplicitNull] = None diff --git a/src/adcp/types/domains/core/canonical_audience_evidence.py b/src/adcp/types/domains/core/canonical_audience_evidence.py index a31e1f918..b84762798 100644 --- a/src/adcp/types/domains/core/canonical_audience_evidence.py +++ b/src/adcp/types/domains/core/canonical_audience_evidence.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/canonical_audience_evidence.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictFloat +from pydantic import ConfigDict, Field, StrictFloat from ..enums import audience_evidence_methodology, audience_resolution_method, audience_subject_type from . import audience_characteristic, brand_key, date_range @@ -32,7 +32,7 @@ class Baseline(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - system: AnyUrl + system: WireAnyUrl population_id: Annotated[str, Field(min_length=1)] version: Annotated[str, Field(min_length=1)] description: Annotated[str | None, NoExplicitNull] = None @@ -72,8 +72,8 @@ class CanonicalAudienceEvidence(AdCPBaseModel): measurement_window: date_range.DateRange sample_size: Annotated[SchemaInt | None, Field(ge=1), NoExplicitNull] = None confidence: Annotated[StrictFloat | None, Field(ge=0.0, le=1.0), NoExplicitNull] = None - last_updated: AwareDatetime - methodology_url: Annotated[AnyUrl | None, NoExplicitNull] = None + last_updated: WireDatetime + methodology_url: Annotated[WireAnyUrl | None, NoExplicitNull] = None attestation_digests: Annotated[ list[AttestationDigest] | None, Field( diff --git a/src/adcp/types/domains/core/canonical_delivery_forecast.py b/src/adcp/types/domains/core/canonical_delivery_forecast.py index 03a2c45c9..6821f7b70 100644 --- a/src/adcp/types/domains/core/canonical_delivery_forecast.py +++ b/src/adcp/types/domains/core/canonical_delivery_forecast.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/canonical_delivery_forecast.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..enums import demographic_system as demographic_system_1 from ..enums import forecast_method @@ -35,6 +35,6 @@ class CanonicalDeliveryForecast(AdCPBaseModel): str | None, Field(max_length=64, pattern='^[a-z0-9_]+$'), NoExplicitNull ] = None reach_unit: Annotated[reach_unit_1.ReachUnit | None, NoExplicitNull] = None - generated_at: Annotated[AwareDatetime | None, NoExplicitNull] = None - valid_until: Annotated[AwareDatetime | None, NoExplicitNull] = None + generated_at: Annotated[WireDatetime | None, NoExplicitNull] = None + valid_until: Annotated[WireDatetime | None, NoExplicitNull] = None ext: Annotated[ext_1.ExtensionObject | None, NoExplicitNull] = None diff --git a/src/adcp/types/domains/core/canonical_format_option.py b/src/adcp/types/domains/core/canonical_format_option.py index 676f2b26b..e38833661 100644 --- a/src/adcp/types/domains/core/canonical_format_option.py +++ b/src/adcp/types/domains/core/canonical_format_option.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/canonical_format_option.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool from ..enums import channels from . import creative_locale_policy, macro_resolution_capability, platform_extension_ref @@ -71,7 +71,7 @@ class CanonicalFormatOption(AdCPBaseModel): NoExplicitNull, ] = None display_name: Annotated[str | None, Field(min_length=1), NoExplicitNull] = None - sample_render_url: Annotated[AnyUrl | None, NoExplicitNull] = None + sample_render_url: Annotated[WireAnyUrl | None, NoExplicitNull] = None applies_to_channels: Annotated[ list[channels.MediaChannel] | None, Field(min_length=1), NoExplicitNull ] = None diff --git a/src/adcp/types/domains/core/canonical_product.py b/src/adcp/types/domains/core/canonical_product.py index b1797ccc7..412f27773 100644 --- a/src/adcp/types/domains/core/canonical_product.py +++ b/src/adcp/types/domains/core/canonical_product.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/canonical_product.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool +from pydantic import ConfigDict, Field, StrictBool from ..enums import audio_distribution_type, catalog_type from ..enums import channels as channels_1 @@ -315,7 +315,7 @@ class CanonicalProduct(AdCPBaseModel): NoExplicitNull, ] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Expiration of a request-specific configured offer. Canonical products have no is_custom flag; expires_at is what marks an offer as request-specific. After it, the buyer rediscovers.' ), diff --git a/src/adcp/types/domains/core/canonical_proposal.py b/src/adcp/types/domains/core/canonical_proposal.py index 198266239..a3dc145e6 100644 --- a/src/adcp/types/domains/core/canonical_proposal.py +++ b/src/adcp/types/domains/core/canonical_proposal.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/canonical_proposal.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field, StrictFloat from ..enums import proposal_status as proposal_status_1 from ..media_buy import commercial_terms as commercial_terms_1 @@ -65,9 +65,9 @@ class CanonicalProposal(AdCPBaseModel): description='draft is indicative and unreserved; committed has firm terms with inventory reserved until expires_at; accepted is the historical snapshot attached to a MediaBuy.' ), ] - accepted_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + accepted_at: Annotated[WireDatetime | None, NoExplicitNull] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='For a draft, the indicative-terms freshness deadline. For a committed proposal, the inventory-hold deadline.' ), diff --git a/src/adcp/types/domains/core/canonical_reporting_commitment.py b/src/adcp/types/domains/core/canonical_reporting_commitment.py index 04e0cb474..8b1ec7de4 100644 --- a/src/adcp/types/domains/core/canonical_reporting_commitment.py +++ b/src/adcp/types/domains/core/canonical_reporting_commitment.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/canonical_reporting_commitment.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..enums import available_metric from . import brand_key, canonical_metric_qualifier, vendor_metric_id @@ -22,7 +22,7 @@ class CanonicalReportingCommitment1(AdCPBaseModel): qualifier: Annotated[ canonical_metric_qualifier.CanonicalMetricQualifier | None, NoExplicitNull ] = None - effective_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + effective_at: Annotated[WireDatetime | None, NoExplicitNull] = None class CanonicalReportingCommitment2(AdCPBaseModel): @@ -35,7 +35,7 @@ class CanonicalReportingCommitment2(AdCPBaseModel): qualifier: Annotated[ canonical_metric_qualifier.CanonicalMetricQualifier | None, NoExplicitNull ] = None - effective_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + effective_at: Annotated[WireDatetime | None, NoExplicitNull] = None CanonicalReportingCommitment = Annotated[ diff --git a/src/adcp/types/domains/core/capabilities_changed_webhook.py b/src/adcp/types/domains/core/capabilities_changed_webhook.py index fc16c9071..7fe5fd74e 100644 --- a/src/adcp/types/domains/core/capabilities_changed_webhook.py +++ b/src/adcp/types/domains/core/capabilities_changed_webhook.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/capabilities_changed_webhook.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field from . import ext as ext_1 @@ -58,7 +58,7 @@ class CapabilitiesChangedWebhook(AdCPBaseModel): ), ] = 'capabilities.changed' fired_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the seller initiated this fire. Distinct from `changed_at`, which is when the seller recorded the material capability change.' ), @@ -73,13 +73,13 @@ class CapabilitiesChangedWebhook(AdCPBaseModel): ), ] agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical seller agent URL whose capabilities changed. Receivers that subscribe to multiple agents use this to select the cache entry to invalidate.' ), ] changed_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the seller recorded the material capability change. SHOULD match or precede `adcp.capability_changes.last_modified` on the next `get_adcp_capabilities` response.' ), diff --git a/src/adcp/types/domains/core/catalog.py b/src/adcp/types/domains/core/catalog.py index 9b6d401d7..cca311cf3 100644 --- a/src/adcp/types/domains/core/catalog.py +++ b/src/adcp/types/domains/core/catalog.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/catalog.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, ConfigDict, Field +from pydantic import ConfigDict, Field from ..enums import catalog_type from ..enums import content_id_type as content_id_type_1 @@ -48,7 +48,7 @@ class Catalog(AdCPBaseModel): ), ] url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="URL to an external catalog feed. The platform fetches and resolves items from this URL. For offering-type catalogs, the feed contains an array of Offering objects. For other types, the feed format is determined by feed_format. When omitted with type 'product', the platform uses its synced copy of the brand's product catalog." ), diff --git a/src/adcp/types/domains/core/catalog_item_availability_state.py b/src/adcp/types/domains/core/catalog_item_availability_state.py index 52e3d05b7..f6a5e5665 100644 --- a/src/adcp/types/domains/core/catalog_item_availability_state.py +++ b/src/adcp/types/domains/core/catalog_item_availability_state.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/catalog_item_availability_state.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field from . import catalog_item_availability_error from . import ext as ext_1 @@ -60,14 +60,14 @@ class CatalogItemAvailabilityState(AdCPBaseModel): NoExplicitNull, ] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Current automatic expiry, present only while availability is suppressed with an expiry.' ), NoExplicitNull, ] = None updated_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Seller timestamp of the state represented by overlay_revision.'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/catalog_item_availability_update.py b/src/adcp/types/domains/core/catalog_item_availability_update.py index afa7f41f1..8149feb07 100644 --- a/src/adcp/types/domains/core/catalog_item_availability_update.py +++ b/src/adcp/types/domains/core/catalog_item_availability_update.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/catalog_item_availability_update.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field from . import ext as ext_1 @@ -88,7 +88,7 @@ class CatalogItemAvailabilityUpdate(AdCPBaseModel): NoExplicitNull, ] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Optional expiry for a suppress overlay. At this instant the seller automatically removes the buyer-authored suppression as if it received restore. Valid only with action suppress; omit for an indefinite suppression. Sellers MUST reject a timestamp that is not in the future when the request is processed. On a repeated suppress, this value is complete replacement state: a changed timestamp replaces the prior expiry, and omission clears a prior expiry to make suppression indefinite. Either change returns status applied, not unchanged.' ), diff --git a/src/adcp/types/domains/core/catalog_item_availability_update_result.py b/src/adcp/types/domains/core/catalog_item_availability_update_result.py index 38a5821dc..34fc98422 100644 --- a/src/adcp/types/domains/core/catalog_item_availability_update_result.py +++ b/src/adcp/types/domains/core/catalog_item_availability_update_result.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/catalog_item_availability_update_result.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field from . import catalog_item_availability_error from . import ext as ext_1 @@ -67,14 +67,14 @@ class CatalogItemAvailabilityUpdateResult(AdCPBaseModel): NoExplicitNull, ] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Persisted expiry after this update, present only for a suppressed state with an expiry.' ), NoExplicitNull, ] = None applied_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Seller timestamp when the buyer-availability state took effect. Required for applied. Optional for unchanged when the seller knows the timestamp of the already-persisted state.' ), diff --git a/src/adcp/types/domains/core/collection_list_ref.py b/src/adcp/types/domains/core/collection_list_ref.py index 876e2220c..88a50c5dc 100644 --- a/src/adcp/types/domains/core/collection_list_ref.py +++ b/src/adcp/types/domains/core/collection_list_ref.py @@ -1,20 +1,20 @@ # generated by datamodel-codegen: # filename: core/collection_list_ref.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field class CollectionListReference(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - agent_url: Annotated[AnyUrl, Field(description='URL of the agent managing the collection list')] + agent_url: Annotated[WireAnyUrl, Field(description='URL of the agent managing the collection list')] list_id: Annotated[ str, Field(description='Identifier for the collection list within the agent', min_length=1) ] diff --git a/src/adcp/types/domains/core/committed_metric.py b/src/adcp/types/domains/core/committed_metric.py index dfea203f0..82fdbc51e 100644 --- a/src/adcp/types/domains/core/committed_metric.py +++ b/src/adcp/types/domains/core/committed_metric.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/committed_metric.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..enums import attribution_methodology as attribution_methodology_1 from ..enums import available_metric @@ -110,7 +110,7 @@ class CommittedMetric1(AdCPBaseModel): NoExplicitNull, ] = None committed_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when this metric became part of the contract. Day-1 commitments use `create_media_buy.confirmed_at`; mid-flight additions use the time the amendment was accepted.' ), @@ -154,7 +154,7 @@ class CommittedMetric2(AdCPBaseModel): NoExplicitNull, ] = None committed_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when this vendor metric became part of the contract.' ), diff --git a/src/adcp/types/domains/core/creative_filters.py b/src/adcp/types/domains/core/creative_filters.py index 47fe442bf..4836f0339 100644 --- a/src/adcp/types/domains/core/creative_filters.py +++ b/src/adcp/types/domains/core/creative_filters.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/creative_filters.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool from ..enums import asset_content_type, creative_status, indicator_type from . import account_ref @@ -53,22 +53,22 @@ class CreativeFilters(AdCPBaseModel): NoExplicitNull, ] = None created_after: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Filter creatives created after this date (ISO 8601)'), NoExplicitNull, ] = None created_before: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Filter creatives created before this date (ISO 8601)'), NoExplicitNull, ] = None updated_after: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Filter creatives last updated after this date (ISO 8601)'), NoExplicitNull, ] = None updated_before: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Filter creatives last updated before this date (ISO 8601)'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/creative_item.py b/src/adcp/types/domains/core/creative_item.py index 8d7210e69..7d86caeb5 100644 --- a/src/adcp/types/domains/core/creative_item.py +++ b/src/adcp/types/domains/core/creative_item.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/creative_item.json -# timestamp: 2026-10-02T23:18:44+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, WireAnyUrl +from pydantic import ConfigDict, Field class CreativeItem1(AdCPBaseModel): @@ -27,7 +27,7 @@ class CreativeItem1(AdCPBaseModel): asset_id: Annotated[ str, Field(description='Unique identifier for the asset within the creative') ] - content_uri: Annotated[AnyUrl, Field(description='URL for media assets (images, videos, etc.)')] + content_uri: Annotated[WireAnyUrl, Field(description='URL for media assets (images, videos, etc.)')] class CreativeItem2(AdCPBaseModel): diff --git a/src/adcp/types/domains/core/creative_operation_format_declaration.py b/src/adcp/types/domains/core/creative_operation_format_declaration.py index 7b685a320..3ffa43c7e 100644 --- a/src/adcp/types/domains/core/creative_operation_format_declaration.py +++ b/src/adcp/types/domains/core/creative_operation_format_declaration.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/creative_operation_format_declaration.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import Field, StrictBool from ..enums import channels from . import creative_locale_policy, format_id, macro_resolution_capability, platform_extension_ref @@ -46,7 +46,7 @@ class CreativeOperationFormatDeclaration(AdCPBaseModel): ] = None technical_requirements_complete: Annotated[StrictBool | None, NoExplicitNull] = None display_name: Annotated[str | None, NoExplicitNull] = None - sample_render_url: Annotated[AnyUrl | None, NoExplicitNull] = None + sample_render_url: Annotated[WireAnyUrl | None, NoExplicitNull] = None applies_to_channels: Annotated[list[channels.MediaChannel] | None, NoExplicitNull] = None seller_preference: Annotated[SellerPreference | None, NoExplicitNull] = None locale_policy: Annotated[creative_locale_policy.CreativeLocalePolicy | None, NoExplicitNull] = ( diff --git a/src/adcp/types/domains/core/creative_policy.py b/src/adcp/types/domains/core/creative_policy.py index 70c2593ac..398fa03a6 100644 --- a/src/adcp/types/domains/core/creative_policy.py +++ b/src/adcp/types/domains/core/creative_policy.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/creative_policy.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool from ..enums import co_branding_requirement, landing_page_requirement @@ -51,7 +51,7 @@ class AcceptedVerifier(AdCPBaseModel): extra='forbid', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="URL of the governance agent. MUST use the `https://` scheme. The seller calls this URL via `get_creative_features` to verify a buyer's claim; the seller has already vetted the endpoint and accepts responsibility for outbound calls to it." ), diff --git a/src/adcp/types/domains/core/datetime_range.py b/src/adcp/types/domains/core/datetime_range.py index cca442b54..85f86db8b 100644 --- a/src/adcp/types/domains/core/datetime_range.py +++ b/src/adcp/types/domains/core/datetime_range.py @@ -1,18 +1,18 @@ # generated by datamodel-codegen: # filename: core/datetime_range.json -# timestamp: 2026-05-02T19:36:29+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, WireDatetime +from pydantic import ConfigDict, Field class DatetimeRange(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - start: Annotated[AwareDatetime, Field(description='Start timestamp (inclusive), ISO 8601')] - end: Annotated[AwareDatetime, Field(description='End timestamp (inclusive), ISO 8601')] + start: Annotated[WireDatetime, Field(description='Start timestamp (inclusive), ISO 8601')] + end: Annotated[WireDatetime, Field(description='End timestamp (inclusive), ISO 8601')] diff --git a/src/adcp/types/domains/core/delivery_forecast.py b/src/adcp/types/domains/core/delivery_forecast.py index a5f6eb90c..e20b09b84 100644 --- a/src/adcp/types/domains/core/delivery_forecast.py +++ b/src/adcp/types/domains/core/delivery_forecast.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/delivery_forecast.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..enums import demographic_system as demographic_system_1 from ..enums import forecast_method @@ -90,10 +90,10 @@ class DeliveryForecast(AdCPBaseModel): NoExplicitNull, ] = None generated_at: Annotated[ - AwareDatetime | None, Field(description='When this forecast was computed'), NoExplicitNull + WireDatetime | None, Field(description='When this forecast was computed'), NoExplicitNull ] = None valid_until: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When this forecast expires. After this time, the forecast should be refreshed. Forecast expiry does not affect proposal executability.' ), diff --git a/src/adcp/types/domains/core/deployment.py b/src/adcp/types/domains/core/deployment.py index 602f26c6d..1a901e0ed 100644 --- a/src/adcp/types/domains/core/deployment.py +++ b/src/adcp/types/domains/core/deployment.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/deployment.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from . import activation_key as activation_key_1 @@ -43,7 +43,7 @@ class Deployment1(AdCPBaseModel): NoExplicitNull, ] = None deployed_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Timestamp when activation completed (if is_live=true)'), NoExplicitNull, ] = None @@ -57,7 +57,7 @@ class Deployment2(AdCPBaseModel): Literal['agent'], Field(description='Discriminator indicating this is an agent URL-based deployment'), ] = 'agent' - agent_url: Annotated[AnyUrl, Field(description='URL identifying the deployment agent')] + agent_url: Annotated[WireAnyUrl, Field(description='URL identifying the deployment agent')] account: Annotated[ str | None, Field(description='Account identifier if applicable'), NoExplicitNull ] = None @@ -80,7 +80,7 @@ class Deployment2(AdCPBaseModel): NoExplicitNull, ] = None deployed_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Timestamp when activation completed (if is_live=true)'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/destination.py b/src/adcp/types/domains/core/destination.py index cfe638cf1..9bbb31681 100644 --- a/src/adcp/types/domains/core/destination.py +++ b/src/adcp/types/domains/core/destination.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/destination.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field class Destination1(AdCPBaseModel): @@ -36,7 +36,7 @@ class Destination2(AdCPBaseModel): Field(description='Discriminator indicating this is an agent URL-based deployment'), ] = 'agent' agent_url: Annotated[ - AnyUrl, Field(description='URL identifying the deployment agent (for sales agents, etc.)') + WireAnyUrl, Field(description='URL identifying the deployment agent (for sales agents, etc.)') ] account: Annotated[ str | None, Field(description='Optional account identifier on the agent'), NoExplicitNull diff --git a/src/adcp/types/domains/core/destination_item.py b/src/adcp/types/domains/core/destination_item.py index 02919603a..65cb78780 100644 --- a/src/adcp/types/domains/core/destination_item.py +++ b/src/adcp/types/domains/core/destination_item.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/destination_item.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictFloat from . import ext as ext_1 from . import offering_asset_group @@ -76,10 +76,10 @@ class DestinationItem(AdCPBaseModel): NoExplicitNull, ] = None image_url: Annotated[ - AnyUrl | None, Field(description='Destination hero image URL.'), NoExplicitNull + WireAnyUrl | None, Field(description='Destination hero image URL.'), NoExplicitNull ] = None url: Annotated[ - AnyUrl | None, Field(description='Destination landing page or booking URL.'), NoExplicitNull + WireAnyUrl | None, Field(description='Destination landing page or booking URL.'), NoExplicitNull ] = None rating: Annotated[ StrictFloat | None, diff --git a/src/adcp/types/domains/core/downstream_connection_requirement.py b/src/adcp/types/domains/core/downstream_connection_requirement.py index c3b37a119..1f3e561ae 100644 --- a/src/adcp/types/domains/core/downstream_connection_requirement.py +++ b/src/adcp/types/domains/core/downstream_connection_requirement.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/downstream_connection_requirement.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field class ConnectionType(StrEnum): @@ -67,7 +67,7 @@ class ResourceRef(AdCPBaseModel): NoExplicitNull, ] = None profile_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='Public URL for the owning identity, when available.'), NoExplicitNull, ] = None @@ -79,7 +79,7 @@ class ResourceRef(AdCPBaseModel): NoExplicitNull, ] = None post_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='Public URL for the referenced post, when available.'), NoExplicitNull, ] = None @@ -134,7 +134,7 @@ class DownstreamConnectionRequirement(AdCPBaseModel): NoExplicitNull, ] = None authorization_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Seller-hosted or provider-hosted URL where a human can complete or restore this downstream connection.' ), @@ -148,7 +148,7 @@ class DownstreamConnectionRequirement(AdCPBaseModel): NoExplicitNull, ] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Expiration time for the downstream grant, when known.'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/education_item.py b/src/adcp/types/domains/core/education_item.py index dcabeba08..d06e26424 100644 --- a/src/adcp/types/domains/core/education_item.py +++ b/src/adcp/types/domains/core/education_item.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/education_item.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from . import ext as ext_1 from . import offering_asset_group @@ -97,10 +97,10 @@ class EducationItem(AdCPBaseModel): NoExplicitNull, ] = None image_url: Annotated[ - AnyUrl | None, Field(description='Program or institution image URL.'), NoExplicitNull + WireAnyUrl | None, Field(description='Program or institution image URL.'), NoExplicitNull ] = None url: Annotated[ - AnyUrl | None, Field(description='Program landing page or enrollment URL.'), NoExplicitNull + WireAnyUrl | None, Field(description='Program landing page or enrollment URL.'), NoExplicitNull ] = None tags: Annotated[ list[str] | None, diff --git a/src/adcp/types/domains/core/evaluator_spec.py b/src/adcp/types/domains/core/evaluator_spec.py index e99bc95af..0fe49ec6e 100644 --- a/src/adcp/types/domains/core/evaluator_spec.py +++ b/src/adcp/types/domains/core/evaluator_spec.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/evaluator_spec.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field, StrictFloat from ..content_standards import artifact from . import ext as ext_1 @@ -44,7 +44,7 @@ class FeatureAgent(AdCPBaseModel): extra='forbid', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="URL of the get_creative_features-capable agent the producing agent calls to obtain creative-feature values for the gate. MUST use https:// and MUST match an entry in the seller's `creative_policy.accepted_verifiers[].agent_url`; off-list → `EVALUATOR_AGENT_NOT_ACCEPTED`." ), @@ -161,7 +161,7 @@ class EvaluatorSpec3(AdCPBaseModel): ] = None ext: Annotated[ext_1.ExtensionObject | None, NoExplicitNull] = None agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="URL of an external get_creative_features-capable judge agent the seller calls to score the produced leaves. MUST match an entry in the seller's `creative_policy.accepted_verifiers[].agent_url` (off-list → `EVALUATOR_AGENT_NOT_ACCEPTED`); an on-list agent that is unreachable or rejects the producing agent's transport authentication degrades to seller-default ranking (advisory errors[] note), not a failure. Authentication and trust material for this call belongs on the transport or in account provisioning, not in the evaluator payload." ), diff --git a/src/adcp/types/domains/core/event.py b/src/adcp/types/domains/core/event.py index d0350947d..c0f816661 100644 --- a/src/adcp/types/domains/core/event.py +++ b/src/adcp/types/domains/core/event.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/event.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field from ..enums import action_source as action_source_1 from ..enums import event_type as event_type_1 @@ -30,7 +30,7 @@ class Event(AdCPBaseModel): ] event_type: Annotated[event_type_1.EventType, Field(description='Standard event type')] event_time: Annotated[ - AwareDatetime, Field(description='ISO 8601 timestamp when the event occurred') + WireDatetime, Field(description='ISO 8601 timestamp when the event occurred') ] user_match: Annotated[ user_match_1.UserMatch | None, @@ -55,7 +55,7 @@ class Event(AdCPBaseModel): NoExplicitNull, ] = None event_source_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="URL where the event occurred (required when action_source is 'website')" ), diff --git a/src/adcp/types/domains/core/event_source_health.py b/src/adcp/types/domains/core/event_source_health.py index d8e91c2c8..8209b89cd 100644 --- a/src/adcp/types/domains/core/event_source_health.py +++ b/src/adcp/types/domains/core/event_source_health.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/event_source_health.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field, StrictFloat from ..enums import assessment_status from . import diagnostic_issue @@ -63,14 +63,14 @@ class EventSourceHealth(AdCPBaseModel): NoExplicitNull, ] = None last_event_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp of the most recent event received from this source. Absent when no events have been received.' ), NoExplicitNull, ] = None evaluated_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp of when this health assessment was computed. When health is derived from reporting data, this may lag real-time. Buyer agents can use this to decide whether to trust stale assessments or re-request.' ), diff --git a/src/adcp/types/domains/core/flight_item.py b/src/adcp/types/domains/core/flight_item.py index 4f065dd4c..fd7134733 100644 --- a/src/adcp/types/domains/core/flight_item.py +++ b/src/adcp/types/domains/core/flight_item.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/flight_item.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field from . import ext as ext_1 from . import offering_asset_group @@ -54,20 +54,20 @@ class FlightItem(AdCPBaseModel): str | None, Field(description='Route description or promotional text.'), NoExplicitNull ] = None departure_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Departure date and time (ISO 8601).'), NoExplicitNull, ] = None arrival_time: Annotated[ - AwareDatetime | None, Field(description='Arrival date and time (ISO 8601).'), NoExplicitNull + WireDatetime | None, Field(description='Arrival date and time (ISO 8601).'), NoExplicitNull ] = None image_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='Promotional image URL (typically a destination photo).'), NoExplicitNull, ] = None url: Annotated[ - AnyUrl | None, Field(description='Booking page URL for this route.'), NoExplicitNull + WireAnyUrl | None, Field(description='Booking page URL for this route.'), NoExplicitNull ] = None tags: Annotated[ list[str] | None, diff --git a/src/adcp/types/domains/core/forecast_dimension_time.py b/src/adcp/types/domains/core/forecast_dimension_time.py index ef4f3838b..dd1e52f9d 100644 --- a/src/adcp/types/domains/core/forecast_dimension_time.py +++ b/src/adcp/types/domains/core/forecast_dimension_time.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/forecast_dimension_time.json -# timestamp: 2026-08-21T04:35:18+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, WireDatetime +from pydantic import ConfigDict, Field class TimeForecastDimension(AdCPBaseModel): @@ -16,11 +16,11 @@ class TimeForecastDimension(AdCPBaseModel): ) kind: Annotated[Literal['time'], Field(description='Dimension family discriminator.')] = 'time' start_time: Annotated[ - AwareDatetime, + WireDatetime, Field(description='Inclusive window start (RFC 3339 date-time with timezone offset).'), ] end_time: Annotated[ - AwareDatetime, + WireDatetime, Field( description='Exclusive window end (RFC 3339 date-time with timezone offset). MUST be after start_time.' ), diff --git a/src/adcp/types/domains/core/format.py b/src/adcp/types/domains/core/format.py index 56fa0cba1..ee098b1b3 100644 --- a/src/adcp/types/domains/core/format.py +++ b/src/adcp/types/domains/core/format.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/format.json -# timestamp: 2026-10-22T12:24:23+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field, RootModel, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field, RootModel, StrictBool, StrictFloat from ..enums import ( available_metric, @@ -780,7 +780,7 @@ class CanonicalParameters1(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -893,7 +893,7 @@ class CanonicalParameters2(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1006,7 +1006,7 @@ class CanonicalParameters3(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1119,7 +1119,7 @@ class CanonicalParameters4(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1232,7 +1232,7 @@ class CanonicalParameters5(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1345,7 +1345,7 @@ class CanonicalParameters6(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1458,7 +1458,7 @@ class CanonicalParameters7(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1571,7 +1571,7 @@ class CanonicalParameters8(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1684,7 +1684,7 @@ class CanonicalParameters9(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1797,7 +1797,7 @@ class CanonicalParameters10(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1910,7 +1910,7 @@ class CanonicalParameters11(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -2023,7 +2023,7 @@ class CanonicalParameters12(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -2136,7 +2136,7 @@ class CanonicalParameters13(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -2249,7 +2249,7 @@ class CanonicalParameters14(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -2362,7 +2362,7 @@ class CanonicalParameters15(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -2475,7 +2475,7 @@ class CanonicalParameters16(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -2780,7 +2780,7 @@ class Format(AdCPBaseModel): NoExplicitNull, ] = None example_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional URL to showcase page with examples and interactive demos of this format' ), diff --git a/src/adcp/types/domains/core/generation_credential.py b/src/adcp/types/domains/core/generation_credential.py index ec992dabd..8a0829077 100644 --- a/src/adcp/types/domains/core/generation_credential.py +++ b/src/adcp/types/domains/core/generation_credential.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/generation_credential.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field from ..enums import right_use from . import ext as ext_1 @@ -34,14 +34,14 @@ class GenerationCredential(AdCPBaseModel): Field(description='Which rights uses this credential covers', min_length=1), ] expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When this credential expires. Key lifetime is determined by the provider.' ), NoExplicitNull, ] = None endpoint: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="Provider API endpoint to use with this credential, if different from the provider's default" ), diff --git a/src/adcp/types/domains/core/geo_place_catalog_capability.py b/src/adcp/types/domains/core/geo_place_catalog_capability.py index 65077d279..a2e67185a 100644 --- a/src/adcp/types/domains/core/geo_place_catalog_capability.py +++ b/src/adcp/types/domains/core/geo_place_catalog_capability.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/geo_place_catalog_capability.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, ConfigDict, Field +from pydantic import ConfigDict, Field from . import geo_place_resolver @@ -23,7 +23,7 @@ class GeographicPlaceCatalogCapability(AdCPBaseModel): extra='forbid', ) source: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional catalog dataset or derivative-source identifier. This records provenance/coverage and does not change the identifier namespace in system.' ), diff --git a/src/adcp/types/domains/core/geo_place_catalog_entry.py b/src/adcp/types/domains/core/geo_place_catalog_entry.py index 0976189b1..9f8a67d6a 100644 --- a/src/adcp/types/domains/core/geo_place_catalog_entry.py +++ b/src/adcp/types/domains/core/geo_place_catalog_entry.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/geo_place_catalog_entry.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field from . import ext as ext_1 from . import geo_place_type @@ -81,7 +81,7 @@ class GeographicPlaceCatalogEntry(AdCPBaseModel): NoExplicitNull, ] = None valid_until: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Known time after which the identifier must no longer be accepted for new targeting.' ), diff --git a/src/adcp/types/domains/core/geo_place_resolver.py b/src/adcp/types/domains/core/geo_place_resolver.py index 743e54b41..d2368e7c2 100644 --- a/src/adcp/types/domains/core/geo_place_resolver.py +++ b/src/adcp/types/domains/core/geo_place_resolver.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/geo_place_resolver.json -# timestamp: 2026-08-17T23:02:13+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, WireAnyUrl +from pydantic import ConfigDict, Field class Auth(StrEnum): @@ -21,7 +21,7 @@ class GeographicPlaceResolver(AdCPBaseModel): extra='forbid', ) url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='HTTPS endpoint accepting the standard geo-place resolution query parameters.' ), diff --git a/src/adcp/types/domains/core/geo_place_system.py b/src/adcp/types/domains/core/geo_place_system.py index 5fba727fb..cdba4ac00 100644 --- a/src/adcp/types/domains/core/geo_place_system.py +++ b/src/adcp/types/domains/core/geo_place_system.py @@ -1,13 +1,14 @@ # generated by datamodel-codegen: # filename: core/geo_place_system.json -# timestamp: 2026-10-02T23:18:44+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from pydantic import AnyUrl, Field, RootModel +from adcp.types.base import WireAnyUrl +from pydantic import Field, RootModel class GeographicPlaceIdentifierSystem1(StrEnum): @@ -16,9 +17,9 @@ class GeographicPlaceIdentifierSystem1(StrEnum): microsoft_ads = 'microsoft_ads' -class GeographicPlaceIdentifierSystem2(RootModel[AnyUrl]): +class GeographicPlaceIdentifierSystem2(RootModel[WireAnyUrl]): root: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Collision-safe identifier namespace for geographic places. Registered tokens have protocol-defined semantics. Unregistered systems MUST use an absolute HTTPS URI controlled by the catalog owner; consumers compare URI systems as exact opaque strings.', examples=[ diff --git a/src/adcp/types/domains/core/geo_place_type.py b/src/adcp/types/domains/core/geo_place_type.py index 3bc1f9929..550fc18a6 100644 --- a/src/adcp/types/domains/core/geo_place_type.py +++ b/src/adcp/types/domains/core/geo_place_type.py @@ -1,13 +1,14 @@ # generated by datamodel-codegen: # filename: core/geo_place_type.json -# timestamp: 2026-10-02T23:18:44+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from pydantic import AnyUrl, Field, RootModel +from adcp.types.base import WireAnyUrl +from pydantic import Field, RootModel class GeographicPlaceType1(StrEnum): @@ -29,9 +30,9 @@ class GeographicPlaceType1(StrEnum): ward = 'ward' -class GeographicPlaceType2(RootModel[AnyUrl]): +class GeographicPlaceType2(RootModel[WireAnyUrl]): root: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical place classification. Registered tokens have protocol-defined meanings. Catalog-specific classifications without a registered mapping MUST use an absolute HTTPS URI controlled by the vocabulary owner; consumers compare URI types as exact opaque strings.', examples=[ diff --git a/src/adcp/types/domains/core/hotel_item.py b/src/adcp/types/domains/core/hotel_item.py index 431733ca9..3d5de9999 100644 --- a/src/adcp/types/domains/core/hotel_item.py +++ b/src/adcp/types/domains/core/hotel_item.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/hotel_item.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from datetime import date from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field, StrictFloat from . import ext as ext_1 from . import offering_asset_group @@ -83,10 +83,10 @@ class HotelItem(AdCPBaseModel): NoExplicitNull, ] = None image_url: Annotated[ - AnyUrl | None, Field(description='Primary property image URL.'), NoExplicitNull + WireAnyUrl | None, Field(description='Primary property image URL.'), NoExplicitNull ] = None url: Annotated[ - AnyUrl | None, Field(description='Property landing page or booking URL.'), NoExplicitNull + WireAnyUrl | None, Field(description='Property landing page or booking URL.'), NoExplicitNull ] = None phone: Annotated[ str | None, Field(description='Property phone number in E.164 format.'), NoExplicitNull diff --git a/src/adcp/types/domains/core/impairment.py b/src/adcp/types/domains/core/impairment.py index fd4ca21be..1e3897a5a 100644 --- a/src/adcp/types/domains/core/impairment.py +++ b/src/adcp/types/domains/core/impairment.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/impairment.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..enums import impairment_offline_state, impairment_reason_code @@ -90,7 +90,7 @@ class Impairment(AdCPBaseModel): NoExplicitNull, ] = None observed_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the seller observed the resource transition to its offline state.' ), diff --git a/src/adcp/types/domains/core/indicator.py b/src/adcp/types/domains/core/indicator.py index e292654f2..48ee7c7e2 100644 --- a/src/adcp/types/domains/core/indicator.py +++ b/src/adcp/types/domains/core/indicator.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/indicator.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..enums import indicator_type from . import ext as ext_1 @@ -20,7 +20,7 @@ class Indicator(AdCPBaseModel): ) type: indicator_type.IndicatorType detected_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the seller first detected the current uninterrupted occurrence of this indicator. Keep this value stable while the condition remains present. If the condition clears and is later detected again, use the new detection time. Optional because some upstream platforms expose current assessments without an original detection timestamp.' ), diff --git a/src/adcp/types/domains/core/indicator_bearing.py b/src/adcp/types/domains/core/indicator_bearing.py index 0ac4b8057..0ac59f931 100644 --- a/src/adcp/types/domains/core/indicator_bearing.py +++ b/src/adcp/types/domains/core/indicator_bearing.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/indicator_bearing.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..enums import indicator_type from . import indicator, indicator_scope @@ -33,7 +33,7 @@ class IndicatorBearingResourceState(AdCPBaseModel): NoExplicitNull, ] = None indicators_as_of: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the seller completed this evaluation. Required whenever indicators is present, including an empty array. State changes require a strictly newer timestamp; equal-timestamp conflicts are invalid and buyers retain stored state.' ), diff --git a/src/adcp/types/domains/core/indicators_changed_webhook.py b/src/adcp/types/domains/core/indicators_changed_webhook.py index c66092ae9..946ec8c5d 100644 --- a/src/adcp/types/domains/core/indicators_changed_webhook.py +++ b/src/adcp/types/domains/core/indicators_changed_webhook.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/indicators_changed_webhook.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..enums import indicator_type from . import ext as ext_1 @@ -52,7 +52,7 @@ class IndicatorsChangedWebhook(AdCPBaseModel): ), ] notification_type: Literal['indicators.changed'] = 'indicators.changed' - fired_at: AwareDatetime + fired_at: WireDatetime subscriber_id: Annotated[ str, Field(max_length=64, min_length=1, pattern='^[A-Za-z0-9_.:-]{1,64}$') ] @@ -77,6 +77,6 @@ class IndicatorsChangedWebhook(AdCPBaseModel): ), ] observed_at: Annotated[ - AwareDatetime, Field(description='When the seller observed the semantic snapshot change.') + WireDatetime, Field(description='When the seller observed the semantic snapshot change.') ] ext: Annotated[ext_1.ExtensionObject | None, NoExplicitNull] = None diff --git a/src/adcp/types/domains/core/insertion_order.py b/src/adcp/types/domains/core/insertion_order.py index 452405186..f1cc8dd41 100644 --- a/src/adcp/types/domains/core/insertion_order.py +++ b/src/adcp/types/domains/core/insertion_order.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/insertion_order.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat class TotalBudget(AdCPBaseModel): @@ -44,10 +44,10 @@ class Terms(AdCPBaseModel): TotalBudget | None, Field(description='Total committed budget'), NoExplicitNull ] = None flight_start: Annotated[ - AwareDatetime | None, Field(description='Campaign start date'), NoExplicitNull + WireDatetime | None, Field(description='Campaign start date'), NoExplicitNull ] = None flight_end: Annotated[ - AwareDatetime | None, Field(description='Campaign end date'), NoExplicitNull + WireDatetime | None, Field(description='Campaign end date'), NoExplicitNull ] = None payment_terms: Annotated[ PaymentTerms | None, Field(description='Payment terms'), NoExplicitNull @@ -73,14 +73,14 @@ class InsertionOrder(AdCPBaseModel): NoExplicitNull, ] = None terms_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='URL to a human-readable document containing the full insertion order terms' ), NoExplicitNull, ] = None signing_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='URL to an electronic signing service (e.g., DocuSign) for human signature workflows. When present, a human must sign before the buyer agent can proceed with create_media_buy.' ), diff --git a/src/adcp/types/domains/core/installment.py b/src/adcp/types/domains/core/installment.py index 86ac0dfec..6deedc314 100644 --- a/src/adcp/types/domains/core/installment.py +++ b/src/adcp/types/domains/core/installment.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/installment.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field, StrictBool from ..enums import derivative_type, installment_status from . import ad_inventory_config @@ -65,7 +65,7 @@ class Installment(AdCPBaseModel): NoExplicitNull, ] = None scheduled_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the installment airs or publishes (ISO 8601)'), NoExplicitNull, ] = None @@ -85,7 +85,7 @@ class Installment(AdCPBaseModel): NoExplicitNull, ] = None valid_until: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When this installment data expires and should be re-queried. Agents should re-query before committing budget to products with tentative installments.' ), diff --git a/src/adcp/types/domains/core/installment_deadlines.py b/src/adcp/types/domains/core/installment_deadlines.py index 6d9b4dbed..d4cc7e8f4 100644 --- a/src/adcp/types/domains/core/installment_deadlines.py +++ b/src/adcp/types/domains/core/installment_deadlines.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/installment_deadlines.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from . import material_deadline @@ -17,14 +17,14 @@ class InstallmentDeadlines(AdCPBaseModel): extra='allow', ) booking_deadline: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Last date/time to book a placement in this installment (ISO 8601). After this point, the seller will not accept new bookings.' ), NoExplicitNull, ] = None cancellation_deadline: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="Last date/time to cancel without penalty (ISO 8601). Cancellations after this point may incur fees per the seller's terms." ), diff --git a/src/adcp/types/domains/core/installment_delivery_metrics.py b/src/adcp/types/domains/core/installment_delivery_metrics.py index 26ae391b1..bd7200fcd 100644 --- a/src/adcp/types/domains/core/installment_delivery_metrics.py +++ b/src/adcp/types/domains/core/installment_delivery_metrics.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/installment_delivery_metrics.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Any -from adcp.types.base import NoExplicitNull -from pydantic import AwareDatetime, Field +from adcp.types.base import NoExplicitNull, WireDatetime +from pydantic import Field from . import installment_ref as installment_ref_1 from .delivery_metrics import DeliveryMetrics @@ -23,7 +23,7 @@ class InstallmentDeliveryMetrics(DeliveryMetrics): NoExplicitNull, ] = None scheduled_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="Convenience echo of the installment's scheduled publication or air time." ), diff --git a/src/adcp/types/domains/core/installment_property_delivery_metrics.py b/src/adcp/types/domains/core/installment_property_delivery_metrics.py index 5819c2ff6..63c2ca0d4 100644 --- a/src/adcp/types/domains/core/installment_property_delivery_metrics.py +++ b/src/adcp/types/domains/core/installment_property_delivery_metrics.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/installment_property_delivery_metrics.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Any -from adcp.types.base import NoExplicitNull -from pydantic import AwareDatetime, Field +from adcp.types.base import NoExplicitNull, WireDatetime +from pydantic import Field from . import identifier as identifier_1 from . import installment_ref as installment_ref_1 @@ -23,7 +23,7 @@ class InstallmentPropertyDeliveryMetrics(DeliveryMetrics): NoExplicitNull, ] = None scheduled_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="Convenience echo of the installment's scheduled publication or air time." ), diff --git a/src/adcp/types/domains/core/inventory_list_application.py b/src/adcp/types/domains/core/inventory_list_application.py index 0224d0156..91b35970b 100644 --- a/src/adcp/types/domains/core/inventory_list_application.py +++ b/src/adcp/types/domains/core/inventory_list_application.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/inventory_list_application.json -# timestamp: 2026-10-04T00:59:59+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, SchemaInt -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, SchemaInt, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field class Effect(StrEnum): @@ -46,7 +46,7 @@ class InventoryListApplication1(AdCPBaseModel): ), ] agent_url: Annotated[ - AnyUrl, Field(description='Agent URL from the effective property-list targeting reference.') + WireAnyUrl, Field(description='Agent URL from the effective property-list targeting reference.') ] list_id: Annotated[ str, @@ -56,13 +56,13 @@ class InventoryListApplication1(AdCPBaseModel): ), ] resolved_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='Timestamp identifying the complete resolved-list snapshot the seller evaluated. If the list response supplied resolved_at, the seller MUST copy it; otherwise the seller records when it completed assembling the snapshot, including all fetched pages.' ), ] evaluated_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description="When the seller intersected that snapshot with this product's then-current inventory." ), @@ -108,7 +108,7 @@ class InventoryListApplication2(AdCPBaseModel): ), ] agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field(description='Agent URL from the effective collection-list targeting reference.'), ] list_id: Annotated[ @@ -119,13 +119,13 @@ class InventoryListApplication2(AdCPBaseModel): ), ] resolved_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='Timestamp identifying the complete resolved-list snapshot the seller evaluated. If the list response supplied resolved_at, the seller MUST copy it; otherwise the seller records when it completed assembling the snapshot, including all fetched pages.' ), ] evaluated_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description="When the seller intersected that snapshot with this product's then-current inventory." ), diff --git a/src/adcp/types/domains/core/job_item.py b/src/adcp/types/domains/core/job_item.py index 6833d3895..bd4d75b0c 100644 --- a/src/adcp/types/domains/core/job_item.py +++ b/src/adcp/types/domains/core/job_item.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/job_item.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictFloat from . import ext as ext_1 from . import offering_asset_group @@ -90,7 +90,7 @@ class JobItem(AdCPBaseModel): date | None, Field(description='Application deadline (ISO 8601 date).'), NoExplicitNull ] = None apply_url: Annotated[ - AnyUrl | None, Field(description='Direct application URL.'), NoExplicitNull + WireAnyUrl | None, Field(description='Direct application URL.'), NoExplicitNull ] = None job_functions: Annotated[ list[str] | None, diff --git a/src/adcp/types/domains/core/limited_series.py b/src/adcp/types/domains/core/limited_series.py index d8b12715e..8238bf595 100644 --- a/src/adcp/types/domains/core/limited_series.py +++ b/src/adcp/types/domains/core/limited_series.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/limited_series.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field class LimitedSeries(AdCPBaseModel): @@ -18,8 +18,8 @@ class LimitedSeries(AdCPBaseModel): SchemaInt, Field(description='Planned number of installments in the series', ge=1) ] starts: Annotated[ - AwareDatetime | None, Field(description='When the series begins (ISO 8601)'), NoExplicitNull + WireDatetime | None, Field(description='When the series begins (ISO 8601)'), NoExplicitNull ] = None ends: Annotated[ - AwareDatetime | None, Field(description='When the series ends (ISO 8601)'), NoExplicitNull + WireDatetime | None, Field(description='When the series ends (ISO 8601)'), NoExplicitNull ] = None diff --git a/src/adcp/types/domains/core/macro_bearing_url.py b/src/adcp/types/domains/core/macro_bearing_url.py index edadb821a..3d37abbd3 100644 --- a/src/adcp/types/domains/core/macro_bearing_url.py +++ b/src/adcp/types/domains/core/macro_bearing_url.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/macro_bearing_url.json -# timestamp: 2026-10-04T00:53:27+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,12 +8,13 @@ from typing import Annotated -from pydantic import AnyUrl, Field, RootModel +from adcp.types.base import WireAnyUrl +from pydantic import Field, RootModel -class MacroBearingUrl3(RootModel[AnyUrl]): +class MacroBearingUrl3(RootModel[WireAnyUrl]): root: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Backward-compatible URL value that preserves the existing `uri-template` branch (including deep links, protocol-relative references, and RFC 6570 host variables) while additionally accepting absolute HTTP(S) URLs with byte-preserved macro delimiters not legal in a strict URI, including `%%...%%`, `[...]`, and `${...}`. Macro-bearing HTTP(S) URLs require a valid lexical authority and valid percent triplets; an AdCP verifier replaces each recognized token with an unreserved sentinel and validates the resulting absolute URI. Token semantics are established only by `macro_declarations`.', title='Macro-bearing URL', diff --git a/src/adcp/types/domains/core/macro_declaration.py b/src/adcp/types/domains/core/macro_declaration.py index ba983f075..862b7616f 100644 --- a/src/adcp/types/domains/core/macro_declaration.py +++ b/src/adcp/types/domains/core/macro_declaration.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/macro_declaration.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool from .assets import asset_union @@ -63,7 +63,7 @@ class MacroDeclaration(AdCPBaseModel): ] dialect: asset_union.MacroDialect dialect_namespace: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Authority-controlled registry or mapping URI. Required for IAB VAST, IAB DAAST, and vendor dialects.' ), diff --git a/src/adcp/types/domains/core/macro_resolution_capability.py b/src/adcp/types/domains/core/macro_resolution_capability.py index fbc5d7db6..da357fa85 100644 --- a/src/adcp/types/domains/core/macro_resolution_capability.py +++ b/src/adcp/types/domains/core/macro_resolution_capability.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/macro_resolution_capability.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..enums import macro_dialect, macro_resolver, macro_value_context, universal_macro from . import macro_encoding, macro_translation_target @@ -29,7 +29,7 @@ class MacroProcessingCapability(AdCPBaseModel): extra='forbid', ) dialect: macro_dialect.MacroDialectFamily - dialect_namespace: Annotated[AnyUrl | None, NoExplicitNull] = None + dialect_namespace: Annotated[WireAnyUrl | None, NoExplicitNull] = None dialect_revision: Annotated[str | None, Field(min_length=1), NoExplicitNull] = None dialect_semantic: Annotated[str, Field(min_length=1)] mapping_status: MappingStatus diff --git a/src/adcp/types/domains/core/macro_resolution_result.py b/src/adcp/types/domains/core/macro_resolution_result.py index fd601c31a..dfe6ca0e0 100644 --- a/src/adcp/types/domains/core/macro_resolution_result.py +++ b/src/adcp/types/domains/core/macro_resolution_result.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/macro_resolution_result.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool from ..enums import ( macro_dialect, @@ -45,7 +45,7 @@ class MacroResolutionResult(AdCPBaseModel): ] token: Annotated[str, Field(min_length=1)] dialect: macro_dialect.MacroDialectFamily - dialect_namespace: Annotated[AnyUrl | None, NoExplicitNull] = None + dialect_namespace: Annotated[WireAnyUrl | None, NoExplicitNull] = None dialect_revision: Annotated[str | None, Field(min_length=1), NoExplicitNull] = None dialect_semantic: Annotated[str, Field(min_length=1)] mapping_status: macro_mapping_status.MacroMappingStatus diff --git a/src/adcp/types/domains/core/macro_translation_target.py b/src/adcp/types/domains/core/macro_translation_target.py index 88893b2f4..57d2109e1 100644 --- a/src/adcp/types/domains/core/macro_translation_target.py +++ b/src/adcp/types/domains/core/macro_translation_target.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/macro_translation_target.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..enums import macro_dialect, macro_mapping_status, macro_resolver, universal_macro from . import macro_encoding @@ -19,7 +19,7 @@ class MacroTranslationTarget(AdCPBaseModel): ) token: Annotated[str, Field(min_length=1)] dialect: macro_dialect.MacroDialectFamily - dialect_namespace: Annotated[AnyUrl | None, NoExplicitNull] = None + dialect_namespace: Annotated[WireAnyUrl | None, NoExplicitNull] = None dialect_revision: Annotated[str | None, Field(min_length=1), NoExplicitNull] = None dialect_semantic: Annotated[str, Field(min_length=1)] mapping_status: macro_mapping_status.MacroMappingStatus diff --git a/src/adcp/types/domains/core/material_deadline.py b/src/adcp/types/domains/core/material_deadline.py index d5eca4985..823a38e18 100644 --- a/src/adcp/types/domains/core/material_deadline.py +++ b/src/adcp/types/domains/core/material_deadline.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/material_deadline.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field class MaterialDeadline(AdCPBaseModel): @@ -22,7 +22,7 @@ class MaterialDeadline(AdCPBaseModel): ), ] due_at: Annotated[ - AwareDatetime, Field(description='When materials for this stage are due (ISO 8601)') + WireDatetime, Field(description='When materials for this stage are due (ISO 8601)') ] label: Annotated[ str | None, diff --git a/src/adcp/types/domains/core/mcp_webhook_payload.py b/src/adcp/types/domains/core/mcp_webhook_payload.py index d05d7489f..b7620a3c8 100644 --- a/src/adcp/types/domains/core/mcp_webhook_payload.py +++ b/src/adcp/types/domains/core/mcp_webhook_payload.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/mcp_webhook_payload.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..enums import adcp_protocol, task_status from ..enums import task_type as task_type_1 @@ -69,7 +69,7 @@ class McpWebhookPayload(AdCPBaseModel): ), ] timestamp: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when this logical webhook delivery was first generated. Every retry under the same idempotency_key MUST repeat this exact body value, along with every other payload member; only transport/signature metadata such as a fresh RFC 9421 nonce or created parameter may change between attempts.' ), diff --git a/src/adcp/types/domains/core/media_buy.py b/src/adcp/types/domains/core/media_buy.py index 340a92505..eb490842d 100644 --- a/src/adcp/types/domains/core/media_buy.py +++ b/src/adcp/types/domains/core/media_buy.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/media_buy.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field, StrictFloat from ..enums import canceled_by as canceled_by_1 from ..enums import media_buy_health, media_buy_status @@ -26,7 +26,7 @@ class Cancellation(AdCPBaseModel): extra='forbid', ) canceled_at: Annotated[ - AwareDatetime, Field(description='ISO 8601 timestamp when this media buy was canceled.') + WireDatetime, Field(description='ISO 8601 timestamp when this media buy was canceled.') ] canceled_by: Annotated[ canceled_by_1.CanceledBy, @@ -101,7 +101,7 @@ class MediaBuy(AdCPBaseModel): NoExplicitNull, ] = None confirmed_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp when the seller committed to this media buy. May be null until seller commitment occurs in deferred/manual approval flows. Once populated, remains stable through later pause, resume, activation, completion, cancellation, and reporting transitions.' ), @@ -183,7 +183,7 @@ class MediaBuy(AdCPBaseModel): NoExplicitNull, ] = None creative_deadline: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='ISO 8601 timestamp for creative upload deadline'), NoExplicitNull, ] = None @@ -195,9 +195,9 @@ class MediaBuy(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime | None, Field(description='Creation timestamp'), NoExplicitNull + WireDatetime | None, Field(description='Creation timestamp'), NoExplicitNull ] = None updated_at: Annotated[ - AwareDatetime | None, Field(description='Last update timestamp'), NoExplicitNull + WireDatetime | None, Field(description='Last update timestamp'), NoExplicitNull ] = None ext: Annotated[ext_1.ExtensionObject | None, NoExplicitNull] = None diff --git a/src/adcp/types/domains/core/notification_config.py b/src/adcp/types/domains/core/notification_config.py index 7553e7aed..5220a2c51 100644 --- a/src/adcp/types/domains/core/notification_config.py +++ b/src/adcp/types/domains/core/notification_config.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/notification_config.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool from ..enums import auth_scheme from . import ext as ext_1 @@ -69,7 +69,7 @@ class NotificationConfig(AdCPBaseModel): ), ] url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Webhook endpoint URL. Same wire contract as `push-notification-config.url` — `format: "uri"`, no destination-port allowlist enforced by the protocol, SSRF protection via the IP-range check defined in docs/building/by-layer/L1/security.mdx#webhook-url-validation-ssrf. Sellers MUST validate URL syntax, HTTPS usage, hostname normalization, and reserved-range rejection when writing any config, including `active: false` configs. Sellers MUST complete an activation challenge or equivalent proof-of-control before treating a new or changed active subscriber as active.' ), diff --git a/src/adcp/types/domains/core/offering.py b/src/adcp/types/domains/core/offering.py index ac476ff9b..b6218f8c4 100644 --- a/src/adcp/types/domains/core/offering.py +++ b/src/adcp/types/domains/core/offering.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/offering.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field from ..enums import metro_system from . import ext as ext_1 @@ -97,28 +97,28 @@ class Offering(AdCPBaseModel): str | None, Field(description='Short promotional tagline for the offering'), NoExplicitNull ] = None valid_from: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the offering becomes available. If not specified, offering is immediately available.' ), NoExplicitNull, ] = None valid_to: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the offering expires. If not specified, offering has no expiration.' ), NoExplicitNull, ] = None checkout_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="URL for checkout/purchase flow when the brand doesn't support agentic checkout." ), NoExplicitNull, ] = None landing_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="Landing page URL for this offering. For catalog-driven creatives, this is the per-item click-through destination that platforms map to the ad's link-out URL. Every offering in a catalog should have a landing_url unless the format provides its own destination logic." ), diff --git a/src/adcp/types/domains/core/opportunity_context.py b/src/adcp/types/domains/core/opportunity_context.py index cf88a322d..d79dba163 100644 --- a/src/adcp/types/domains/core/opportunity_context.py +++ b/src/adcp/types/domains/core/opportunity_context.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/opportunity_context.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from . import date_range @@ -70,7 +70,7 @@ class OpportunityContext(AdCPBaseModel): NoExplicitNull, ] = None response_deadline: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Deadline by which the buyer needs a seller response.'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/overlay.py b/src/adcp/types/domains/core/overlay.py index 2b326b754..de8866098 100644 --- a/src/adcp/types/domains/core/overlay.py +++ b/src/adcp/types/domains/core/overlay.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/overlay.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictFloat class Visual(AdCPBaseModel): @@ -16,21 +16,21 @@ class Visual(AdCPBaseModel): extra='forbid', ) url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='URL to a theme-neutral overlay graphic (SVG or PNG). Use when a single file works for all backgrounds, e.g. an SVG using CSS custom properties or currentColor.' ), NoExplicitNull, ] = None light: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='URL to the overlay graphic for use on light/bright backgrounds (SVG or PNG)' ), NoExplicitNull, ] = None dark: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL to the overlay graphic for use on dark backgrounds (SVG or PNG)'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/package.py b/src/adcp/types/domains/core/package.py index 03dfa499c..f578bf9d1 100644 --- a/src/adcp/types/domains/core/package.py +++ b/src/adcp/types/domains/core/package.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/package.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..enums import canceled_by as canceled_by_1 from ..enums import pacing as pacing_1 @@ -32,7 +32,7 @@ class Cancellation(AdCPBaseModel): extra='forbid', ) canceled_at: Annotated[ - AwareDatetime, Field(description='ISO 8601 timestamp when this package was canceled.') + WireDatetime, Field(description='ISO 8601 timestamp when this package was canceled.') ] canceled_by: Annotated[ canceled_by_1.CanceledBy, @@ -44,7 +44,7 @@ class Cancellation(AdCPBaseModel): NoExplicitNull, ] = None acknowledged_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp when the seller acknowledged the cancellation. Confirms inventory has been released and billing stopped. Absent until the seller processes the cancellation.' ), @@ -284,14 +284,14 @@ class Package(AdCPBaseModel): NoExplicitNull, ] = None start_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="Flight start date/time for this package in ISO 8601 format. When omitted, the package inherits the media buy's start_time. Sellers SHOULD always include the resolved value in responses, even when inherited." ), NoExplicitNull, ] = None end_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="Flight end date/time for this package in ISO 8601 format. When omitted, the package inherits the media buy's end_time. Sellers SHOULD always include the resolved value in responses, even when inherited." ), @@ -325,7 +325,7 @@ class Package(AdCPBaseModel): NoExplicitNull, ] = None creative_deadline: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="ISO 8601 timestamp for creative upload or change deadline for this package. After this deadline, creative changes are rejected. When absent, the media buy's creative_deadline applies." ), diff --git a/src/adcp/types/domains/core/package_format_snapshot.py b/src/adcp/types/domains/core/package_format_snapshot.py index 3bda32ec6..966e4c778 100644 --- a/src/adcp/types/domains/core/package_format_snapshot.py +++ b/src/adcp/types/domains/core/package_format_snapshot.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/package_format_snapshot.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool from ..enums import channels, daast_version, vast_version from ..formats.canonical import ( @@ -144,7 +144,7 @@ class PackageFormatSnapshot1(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -257,7 +257,7 @@ class PackageFormatSnapshot2(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -370,7 +370,7 @@ class PackageFormatSnapshot3(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -483,7 +483,7 @@ class PackageFormatSnapshot4(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -596,7 +596,7 @@ class PackageFormatSnapshot5(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -709,7 +709,7 @@ class PackageFormatSnapshot6(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -822,7 +822,7 @@ class PackageFormatSnapshot7(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -935,7 +935,7 @@ class PackageFormatSnapshot8(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1048,7 +1048,7 @@ class PackageFormatSnapshot9(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1161,7 +1161,7 @@ class PackageFormatSnapshot10(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1274,7 +1274,7 @@ class PackageFormatSnapshot11(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1387,7 +1387,7 @@ class PackageFormatSnapshot12(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1500,7 +1500,7 @@ class PackageFormatSnapshot13(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1613,7 +1613,7 @@ class PackageFormatSnapshot14(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1726,7 +1726,7 @@ class PackageFormatSnapshot15(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1839,7 +1839,7 @@ class PackageFormatSnapshot16(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), diff --git a/src/adcp/types/domains/core/performance_feedback.py b/src/adcp/types/domains/core/performance_feedback.py index de74472de..72f749441 100644 --- a/src/adcp/types/domains/core/performance_feedback.py +++ b/src/adcp/types/domains/core/performance_feedback.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/performance_feedback.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictFloat from ..enums import attribution_methodology as attribution_methodology_1 from ..enums import available_metric @@ -25,10 +25,10 @@ class MeasurementPeriod(AdCPBaseModel): extra='allow', ) start: Annotated[ - AwareDatetime, Field(description='ISO 8601 start timestamp for measurement period') + WireDatetime, Field(description='ISO 8601 start timestamp for measurement period') ] end: Annotated[ - AwareDatetime, Field(description='ISO 8601 end timestamp for measurement period') + WireDatetime, Field(description='ISO 8601 end timestamp for measurement period') ] @@ -159,10 +159,10 @@ class PerformanceFeedback(AdCPBaseModel): ] = None status: Annotated[Status, Field(description='Processing status of the performance feedback')] submitted_at: Annotated[ - AwareDatetime, Field(description='ISO 8601 timestamp when feedback was submitted') + WireDatetime, Field(description='ISO 8601 timestamp when feedback was submitted') ] applied_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp when feedback was applied to optimization algorithms' ), diff --git a/src/adcp/types/domains/core/performance_feedback_assertion.py b/src/adcp/types/domains/core/performance_feedback_assertion.py index 54f6dcf84..6d58c540b 100644 --- a/src/adcp/types/domains/core/performance_feedback_assertion.py +++ b/src/adcp/types/domains/core/performance_feedback_assertion.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/performance_feedback_assertion.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..enums import feedback_source as feedback_source_1 from ..enums import metric_type as metric_type_1 @@ -153,14 +153,14 @@ class PerformanceFeedbackAssertion(AdCPBaseModel): NoExplicitNull, ] = None evidence_ref: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Provider-hosted evidence or result reference for parties authorized to inspect the full study.' ), NoExplicitNull, ] = None as_of: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the producer computed this assertion.'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/placement_evidence.py b/src/adcp/types/domains/core/placement_evidence.py index 9897f25f4..6010e9270 100644 --- a/src/adcp/types/domains/core/placement_evidence.py +++ b/src/adcp/types/domains/core/placement_evidence.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/placement_evidence.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictFloat class PlacementEvidence(AdCPBaseModel): @@ -15,13 +15,13 @@ class PlacementEvidence(AdCPBaseModel): extra='allow', ) url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='The evidence artifact — photograph, scanned tearsheet, or evidence package' ), ] captured_at: Annotated[ - AwareDatetime | None, Field(description='When the artifact was captured'), NoExplicitNull + WireDatetime | None, Field(description='When the artifact was captured'), NoExplicitNull ] = None latitude: Annotated[ StrictFloat | None, diff --git a/src/adcp/types/domains/core/placement_presentation.py b/src/adcp/types/domains/core/placement_presentation.py index 2a19c3262..87291298d 100644 --- a/src/adcp/types/domains/core/placement_presentation.py +++ b/src/adcp/types/domains/core/placement_presentation.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/placement_presentation.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, ConfigDict, Field, field_validator +from pydantic import ConfigDict, Field, field_validator class Canvas(AdCPBaseModel): @@ -64,7 +64,7 @@ class ImageRef(AdCPBaseModel): extra='forbid', ) uri: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Untrusted image asset. Fetch with public-address-only SSRF protection, no redirects, DNS pinning, and body/time limits; do not attach ambient credentials.' ), @@ -79,7 +79,7 @@ class ImageRef(AdCPBaseModel): @field_validator('uri') @classmethod - def _require_https_uri(cls, value: AnyUrl) -> AnyUrl: + def _require_https_uri(cls, value: WireAnyUrl) -> WireAnyUrl: if value.scheme != 'https': raise ValueError('uri must use https') return value diff --git a/src/adcp/types/domains/core/planned_delivery.py b/src/adcp/types/domains/core/planned_delivery.py index 3ef41da84..7879385e7 100644 --- a/src/adcp/types/domains/core/planned_delivery.py +++ b/src/adcp/types/domains/core/planned_delivery.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/planned_delivery.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictFloat from ..enums import channels as channels_1 from ..enums import pacing as pacing_1 @@ -71,12 +71,12 @@ class PlannedDelivery(AdCPBaseModel): NoExplicitNull, ] = None start_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Actual flight start the seller will use.'), NoExplicitNull, ] = None end_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Actual flight end the seller will use.'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/platform_extension_ref.py b/src/adcp/types/domains/core/platform_extension_ref.py index de8012c52..ebc2cbf24 100644 --- a/src/adcp/types/domains/core/platform_extension_ref.py +++ b/src/adcp/types/domains/core/platform_extension_ref.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/platform_extension_ref.json -# timestamp: 2026-10-03T15:15:43+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, WireAnyUrl +from pydantic import ConfigDict, Field class PlatformExtensionReference(AdCPBaseModel): @@ -15,7 +15,7 @@ class PlatformExtensionReference(AdCPBaseModel): extra='allow', ) uri: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="HTTPS URL identifying the extension. `https://` is mandatory — `http://`, `file://`, `data:`, and other schemes are rejected at the schema layer (defense-in-depth on top of the fetch-contract normative rules). The URI base is the owning agent's URL; the path identifies the extension within that agent. Example: 'https://creative.adcontextprotocol.org/translated/meta/extensions/meta_pixel'. The full fetch contract — SSRF allowlist, response-size cap, $ref sandbox, schema-compile bounds — is documented on `product-format-declaration.json#format_schema` and applies to ALL fetches of this reference shape regardless of whether the field is named `format_schema` (load-bearing for validation) or `platform_extensions` (informational); the *transport* rules are identical, only the *consumption* semantics differ." ), diff --git a/src/adcp/types/domains/core/presentation_ref.py b/src/adcp/types/domains/core/presentation_ref.py index af4450597..64dc5f13c 100644 --- a/src/adcp/types/domains/core/presentation_ref.py +++ b/src/adcp/types/domains/core/presentation_ref.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/presentation_ref.json -# timestamp: 2026-10-03T15:15:43+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel -from pydantic import AnyUrl, ConfigDict, Field, field_validator +from adcp.types.base import AdCPBaseModel, WireAnyUrl +from pydantic import ConfigDict, Field, field_validator class PlacementPresentationReference(AdCPBaseModel): @@ -15,7 +15,7 @@ class PlacementPresentationReference(AdCPBaseModel): extra='forbid', ) uri: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Publisher-controlled HTTPS URL for the presentation metadata. Consumers MUST apply the same SSRF, redirect, response-size, timeout, and DNS-rebinding protections used for format_schema fetches.' ), @@ -40,7 +40,7 @@ class PlacementPresentationReference(AdCPBaseModel): @field_validator('uri') @classmethod - def _require_https_uri(cls, value: AnyUrl) -> AnyUrl: + def _require_https_uri(cls, value: WireAnyUrl) -> WireAnyUrl: if value.scheme != 'https': raise ValueError('uri must use https') return value diff --git a/src/adcp/types/domains/core/preview_provider.py b/src/adcp/types/domains/core/preview_provider.py index e4317c416..a0e5c540b 100644 --- a/src/adcp/types/domains/core/preview_provider.py +++ b/src/adcp/types/domains/core/preview_provider.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/preview_provider.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictBool, field_validator +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool, field_validator class Route(AdCPBaseModel): @@ -42,7 +42,7 @@ class PublisherDesignatedPreviewProvider(AdCPBaseModel): extra='forbid', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='HTTPS URL of the delegated creative-agent endpoint. Buyers call get_adcp_capabilities and preview_creative on this endpoint. They MUST allow only public IPs, pin DNS resolution through connection, refuse redirects, cap time and response size, and attach provider credentials only after exact normalized-origin binding.' ), @@ -57,7 +57,7 @@ class PublisherDesignatedPreviewProvider(AdCPBaseModel): @field_validator('agent_url') @classmethod - def _require_https_agent_url(cls, value: AnyUrl) -> AnyUrl: + def _require_https_agent_url(cls, value: WireAnyUrl) -> WireAnyUrl: if value.scheme != 'https': raise ValueError('agent_url must use https') return value diff --git a/src/adcp/types/domains/core/principal_changed_webhook.py b/src/adcp/types/domains/core/principal_changed_webhook.py index 18933a007..1108cfd20 100644 --- a/src/adcp/types/domains/core/principal_changed_webhook.py +++ b/src/adcp/types/domains/core/principal_changed_webhook.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/principal_changed_webhook.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field from . import ext as ext_1 @@ -51,7 +51,7 @@ class PrincipalChangedWebhook(AdCPBaseModel): ), ] = 'principal.changed' fired_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the seller initiated this fire. Distinct from `changed_at`, which is when the seller recorded the state transition.' ), @@ -66,13 +66,13 @@ class PrincipalChangedWebhook(AdCPBaseModel): ), ] agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical seller agent URL whose principal state changed. Receivers connected to multiple agents use this to select which principal record to re-read.' ), ] changed_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the seller recorded the principal-state transition.' ), diff --git a/src/adcp/types/domains/core/principal_state.py b/src/adcp/types/domains/core/principal_state.py index 01364216a..9d6afef59 100644 --- a/src/adcp/types/domains/core/principal_state.py +++ b/src/adcp/types/domains/core/principal_state.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/principal_state.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field from . import ( agent_notification_config_state, @@ -37,7 +37,7 @@ class RetiredDestination(AdCPBaseModel): min_length=1, ), ] - revoked_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + revoked_at: Annotated[WireDatetime | None, NoExplicitNull] = None class PrincipalState(AdCPBaseModel): diff --git a/src/adcp/types/domains/core/product.py b/src/adcp/types/domains/core/product.py index b7bfb1bf2..21bcaac87 100644 --- a/src/adcp/types/domains/core/product.py +++ b/src/adcp/types/domains/core/product.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/product.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Any, Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime, WireEmail from adcp.types._scalar import ScalarFloat, ScalarStr -from pydantic import AnyUrl, AwareDatetime, ConfigDict, EmailStr, Field, StrictBool, model_validator +from pydantic import ConfigDict, Field, StrictBool, model_validator from ..enums import action_source, audio_distribution_type, catalog_type from ..enums import channels as channels_1 @@ -215,12 +215,12 @@ class MaterialSubmission(AdCPBaseModel): extra='allow', ) url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='HTTPS URL for uploading or submitting physical creative materials'), NoExplicitNull, ] = None email: Annotated[ - EmailStr | None, + WireEmail | None, Field(description='Email address for creative material submission'), NoExplicitNull, ] = None @@ -347,7 +347,7 @@ class Provider(AdCPBaseModel): extra='allow', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="Provider's agent URL from the registry. Canonical identifier for this TMP provider." ), @@ -892,7 +892,7 @@ def _coerce_publisher_property_models(cls, data: Any) -> Any: NoExplicitNull, ] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Expiration timestamp. Required for request-specific configured products whose targeting resolution, price, forecast, inventory, or terms are time-bound. After this time, a seller that still recognizes the issued configured ID within the authenticated account and referenced discovery/refinement lineage rejects create_media_buy with PRODUCT_EXPIRED and the buyer re-runs get_products. Once the seller no longer retains an expiry tombstone, or whenever the ID belongs to another account or lineage, PRODUCT_NOT_FOUND applies instead; sellers are not required to retain tombstones indefinitely and MUST NOT disclose cross-tenant existence through error choice.' ), diff --git a/src/adcp/types/domains/core/product_allocation.py b/src/adcp/types/domains/core/product_allocation.py index 1fe2e9dfc..460a97d49 100644 --- a/src/adcp/types/domains/core/product_allocation.py +++ b/src/adcp/types/domains/core/product_allocation.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/product_allocation.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field, StrictFloat from ..enums import pacing as pacing_1 from . import daypart_target, delivery_forecast @@ -80,14 +80,14 @@ class ProductAllocation(AdCPBaseModel): NoExplicitNull, ] = None start_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Recommended flight start date/time for this allocation in ISO 8601 format. Allows publishers to propose per-flight scheduling within a proposal. When omitted, the allocation applies to the full campaign date range.' ), NoExplicitNull, ] = None end_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Recommended flight end date/time for this allocation in ISO 8601 format. Allows publishers to propose per-flight scheduling within a proposal. When omitted, the allocation applies to the full campaign date range.' ), diff --git a/src/adcp/types/domains/core/product_audience_evidence_requirements.py b/src/adcp/types/domains/core/product_audience_evidence_requirements.py index 4687cb3e1..21757d6df 100644 --- a/src/adcp/types/domains/core/product_audience_evidence_requirements.py +++ b/src/adcp/types/domains/core/product_audience_evidence_requirements.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/product_audience_evidence_requirements.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..enums import audience_evidence_methodology, audience_resolution_method, audience_subject_type from . import brand_key @@ -63,7 +63,7 @@ class AcceptedAttestationIssuers2(AdCPBaseModel): extra='forbid', ) type: Literal['agent'] = 'agent' - agent_url: AnyUrl + agent_url: WireAnyUrl ext: Annotated[ext_1.ExtensionObject | None, NoExplicitNull] = None @@ -72,7 +72,7 @@ class AcceptedAttestationIssuers3(AdCPBaseModel): extra='forbid', ) type: Literal['origin'] = 'origin' - origin: AnyUrl + origin: WireAnyUrl ext: Annotated[ext_1.ExtensionObject | None, NoExplicitNull] = None @@ -123,6 +123,6 @@ class ProductAudienceEvidenceRequirements(AdCPBaseModel): list[AcceptedAttestationIssuers] | None, Field(min_length=1), NoExplicitNull ] = None accepted_attestation_claim_types: Annotated[ - list[AnyUrl] | None, Field(min_length=1), NoExplicitNull + list[WireAnyUrl] | None, Field(min_length=1), NoExplicitNull ] = None ext: Annotated[ext_1.ExtensionObject | None, NoExplicitNull] = None diff --git a/src/adcp/types/domains/core/product_filters.py b/src/adcp/types/domains/core/product_filters.py index b96781d40..34c9b8ce9 100644 --- a/src/adcp/types/domains/core/product_filters.py +++ b/src/adcp/types/domains/core/product_filters.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/product_filters.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,9 +8,9 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, ConfigDict, Field, StrictBool, StrictFloat +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..enums import audio_distribution_type, available_metric from ..enums import channels as channels_1 @@ -88,7 +88,7 @@ class Provider(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - agent_url: Annotated[AnyUrl, Field(description="Provider's agent URL from the registry.")] + agent_url: Annotated[WireAnyUrl, Field(description="Provider's agent URL from the registry.")] context_match: Annotated[ StrictBool | None, Field(description='When true, require this provider to support context match.'), @@ -112,7 +112,7 @@ class BuyerAgent(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - agent_url: AnyUrl + agent_url: WireAnyUrl class AudienceActivationMethods1(AdCPBaseModel): @@ -638,7 +638,7 @@ class ProductFilters(AdCPBaseModel): NoExplicitNull, ] = None required_axe_integrations: Annotated[ - list[AnyUrl] | None, + list[WireAnyUrl] | None, Field( deprecated=True, description='Deprecated: Use trusted_match filter instead. Filter to products executable through specific agentic ad exchanges. URLs are canonical identifiers.', diff --git a/src/adcp/types/domains/core/product_format_declaration.py b/src/adcp/types/domains/core/product_format_declaration.py index 47f37d788..010a1bc98 100644 --- a/src/adcp/types/domains/core/product_format_declaration.py +++ b/src/adcp/types/domains/core/product_format_declaration.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/product_format_declaration.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import Field, StrictBool from ..enums import channels from ..formats.canonical import ( @@ -84,7 +84,7 @@ class ProductFormatDeclaration1(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -197,7 +197,7 @@ class ProductFormatDeclaration2(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -310,7 +310,7 @@ class ProductFormatDeclaration3(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -423,7 +423,7 @@ class ProductFormatDeclaration4(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -536,7 +536,7 @@ class ProductFormatDeclaration5(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -649,7 +649,7 @@ class ProductFormatDeclaration6(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -762,7 +762,7 @@ class ProductFormatDeclaration7(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -875,7 +875,7 @@ class ProductFormatDeclaration8(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -988,7 +988,7 @@ class ProductFormatDeclaration9(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1101,7 +1101,7 @@ class ProductFormatDeclaration10(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1214,7 +1214,7 @@ class ProductFormatDeclaration11(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1327,7 +1327,7 @@ class ProductFormatDeclaration12(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1440,7 +1440,7 @@ class ProductFormatDeclaration13(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1553,7 +1553,7 @@ class ProductFormatDeclaration14(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1666,7 +1666,7 @@ class ProductFormatDeclaration15(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1779,7 +1779,7 @@ class ProductFormatDeclaration16(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), diff --git a/src/adcp/types/domains/core/product_offer_filters.py b/src/adcp/types/domains/core/product_offer_filters.py index 0227ced72..c132bd699 100644 --- a/src/adcp/types/domains/core/product_offer_filters.py +++ b/src/adcp/types/domains/core/product_offer_filters.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/product_offer_filters.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,9 +8,9 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..enums import audio_distribution_type, available_metric from ..enums import channels as channels_1 @@ -50,11 +50,11 @@ class AvailabilityHorizon(AdCPBaseModel): extra='forbid', ) start_time: Annotated[ - AwareDatetime, + WireDatetime, Field(description='Inclusive horizon start (RFC 3339 date-time with timezone offset).'), ] end_time: Annotated[ - AwareDatetime, + WireDatetime, Field( description='Exclusive horizon end (RFC 3339 date-time with timezone offset). MUST be after start_time.' ), @@ -113,7 +113,7 @@ class Provider(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - agent_url: AnyUrl + agent_url: WireAnyUrl context_match: Annotated[StrictBool | None, NoExplicitNull] = None identity_match: Annotated[StrictBool | None, NoExplicitNull] = None diff --git a/src/adcp/types/domains/core/property_list_ref.py b/src/adcp/types/domains/core/property_list_ref.py index 9c2de6935..f7a5e693a 100644 --- a/src/adcp/types/domains/core/property_list_ref.py +++ b/src/adcp/types/domains/core/property_list_ref.py @@ -1,20 +1,20 @@ # generated by datamodel-codegen: # filename: core/property_list_ref.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field class PropertyListReference(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - agent_url: Annotated[AnyUrl, Field(description='URL of the agent managing the property list')] + agent_url: Annotated[WireAnyUrl, Field(description='URL of the agent managing the property list')] list_id: Annotated[ str, Field(description='Identifier for the property list within the agent', min_length=1) ] diff --git a/src/adcp/types/domains/core/proposal.py b/src/adcp/types/domains/core/proposal.py index c88896b85..9bd0d0771 100644 --- a/src/adcp/types/domains/core/proposal.py +++ b/src/adcp/types/domains/core/proposal.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/proposal.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictFloat from ..enums import pacing as pacing_1 from ..enums import proposal_status as proposal_status_1 @@ -97,7 +97,7 @@ class Proposal(AdCPBaseModel): NoExplicitNull, ] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When this proposal expires and can no longer be executed. For draft proposals, indicates when indicative pricing becomes stale. For committed proposals, indicates when the inventory hold lapses — the buyer must call create_media_buy before this time.' ), diff --git a/src/adcp/types/domains/core/protocol_envelope.py b/src/adcp/types/domains/core/protocol_envelope.py index 35498ba36..5369da89c 100644 --- a/src/adcp/types/domains/core/protocol_envelope.py +++ b/src/adcp/types/domains/core/protocol_envelope.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/protocol_envelope.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool from ..enums import task_status from . import context as context_1 @@ -54,7 +54,7 @@ class ProtocolEnvelope(AdCPBaseModel): NoExplicitNull, ] = None timestamp: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp when the response was generated. Useful for debugging, logging, cache validation, and tracking async operation progress.' ), diff --git a/src/adcp/types/domains/core/provenance.py b/src/adcp/types/domains/core/provenance.py index b22fee04a..7fdc2d531 100644 --- a/src/adcp/types/domains/core/provenance.py +++ b/src/adcp/types/domains/core/provenance.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/provenance.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..enums import c2pa_watermark_action from ..enums import digital_source_type as digital_source_type_1 @@ -62,7 +62,7 @@ class Role(StrEnum): class DeclaredBy(AdCPBaseModel): agent_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL of the agent or service that declared this provenance'), NoExplicitNull, ] = None @@ -71,7 +71,7 @@ class DeclaredBy(AdCPBaseModel): class C2pa(AdCPBaseModel): manifest_url: Annotated[ - AnyUrl, Field(description='URL to the C2PA manifest store for this content') + WireAnyUrl, Field(description='URL to the C2PA manifest store for this content') ] @@ -80,7 +80,7 @@ class VerifyAgent(AdCPBaseModel): extra='forbid', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="URL of the governance agent the buyer represents was used to embed/verify this layer. MUST use the `https://` scheme and MUST appear in the seller's `creative_policy.accepted_verifiers[].agent_url` list (canonicalized per /docs/reference/url-canonicalization: lowercase scheme and host, strip default port, normalize path dot-segments). Sellers MUST NOT call this URL until the canonicalized match is confirmed." ), @@ -120,7 +120,7 @@ class EmbeddedProvenanceItem(AdCPBaseModel): NoExplicitNull, ] = None embedded_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the provenance data was embedded (ISO 8601)'), NoExplicitNull, ] = None @@ -131,7 +131,7 @@ class VerifyAgent10(AdCPBaseModel): extra='forbid', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="URL of the governance agent the buyer represents was used to apply/detect this watermark. MUST use the `https://` scheme and MUST appear in the seller's `creative_policy.accepted_verifiers[].agent_url` list (canonicalized per /docs/reference/url-canonicalization: lowercase scheme and host, strip default port, normalize path dot-segments). Sellers MUST NOT call this URL until the canonicalized match is confirmed." ), @@ -232,7 +232,7 @@ class VerificationItem(AdCPBaseModel): ), ] verified_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the verification was performed (ISO 8601)'), NoExplicitNull, ] = None @@ -245,7 +245,7 @@ class VerificationItem(AdCPBaseModel): NoExplicitNull, ] = None details_url: Annotated[ - AnyUrl | None, Field(description='URL to the full verification report'), NoExplicitNull + WireAnyUrl | None, Field(description='URL to the full verification report'), NoExplicitNull ] = None @@ -273,7 +273,7 @@ class Watermark(AdCPBaseModel): NoExplicitNull, ] = None embedded_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the watermark was applied (ISO 8601)'), NoExplicitNull, ] = None @@ -316,14 +316,14 @@ class Provenance(AdCPBaseModel): NoExplicitNull, ] = None declared_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When this provenance claim was made (ISO 8601). Distinct from created_time, which records when the content itself was produced. A provenance claim may be attached well after content creation, for example when retroactively declaring AI involvement for regulatory compliance.' ), NoExplicitNull, ] = None created_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When this content was created or generated (ISO 8601)'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/push_notification_config.py b/src/adcp/types/domains/core/push_notification_config.py index 11d7fbde8..0d9447ebb 100644 --- a/src/adcp/types/domains/core/push_notification_config.py +++ b/src/adcp/types/domains/core/push_notification_config.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/push_notification_config.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..enums import auth_scheme @@ -35,7 +35,7 @@ class Authentication(AdCPBaseModel): class PushNotificationConfig(AdCPBaseModel): url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Webhook endpoint URL for task status notifications. The wire contract is unconstrained beyond `format: "uri"` — in particular, publishers SHOULD NOT enforce a destination-port allowlist by default, since buyers legitimately host receivers on non-standard TLS ports (`:9443`, `:4443`, path-routed multi-tenant gateways). The SSRF guard the protocol relies on is the IP-range check + DNS-rebinding-resistant connect pin defined in [Webhook URL validation (SSRF)](/docs/building/by-layer/L1/security#webhook-url-validation-ssrf), not port filtering. Operators who want a hardened destination-port allowlist as defense-in-depth (e.g., locked-down enterprise egress) opt in explicitly — see [Destination port: permissive by default](/docs/building/by-layer/L1/security#destination-port-permissive-by-default).' ), diff --git a/src/adcp/types/domains/core/real_estate_item.py b/src/adcp/types/domains/core/real_estate_item.py index 45b5ed6e3..ea876e254 100644 --- a/src/adcp/types/domains/core/real_estate_item.py +++ b/src/adcp/types/domains/core/real_estate_item.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/real_estate_item.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field, StrictFloat from . import ext as ext_1 from . import offering_asset_group @@ -116,9 +116,9 @@ class RealEstateItem(AdCPBaseModel): NoExplicitNull, ] = None image_url: Annotated[ - AnyUrl | None, Field(description='Primary property image URL.'), NoExplicitNull + WireAnyUrl | None, Field(description='Primary property image URL.'), NoExplicitNull ] = None - url: Annotated[AnyUrl | None, Field(description='Listing page URL.'), NoExplicitNull] = None + url: Annotated[WireAnyUrl | None, Field(description='Listing page URL.'), NoExplicitNull] = None neighborhood: Annotated[ str | None, Field(description='Neighborhood or area name.'), NoExplicitNull ] = None diff --git a/src/adcp/types/domains/core/reference_asset.py b/src/adcp/types/domains/core/reference_asset.py index 9c952e0c4..6dd0e308a 100644 --- a/src/adcp/types/domains/core/reference_asset.py +++ b/src/adcp/types/domains/core/reference_asset.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reference_asset.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field class Role(StrEnum): @@ -26,7 +26,7 @@ class ReferenceAsset(AdCPBaseModel): extra='allow', ) url: Annotated[ - AnyUrl, Field(description='URL to the reference asset (image, video, or document)') + WireAnyUrl, Field(description='URL to the reference asset (image, video, or document)') ] role: Annotated[ Role, diff --git a/src/adcp/types/domains/core/reference_renderer.py b/src/adcp/types/domains/core/reference_renderer.py index da0ab1e98..81e5fae48 100644 --- a/src/adcp/types/domains/core/reference_renderer.py +++ b/src/adcp/types/domains/core/reference_renderer.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/reference_renderer.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, field_validator +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, field_validator class Provenance(AdCPBaseModel): @@ -15,7 +15,7 @@ class Provenance(AdCPBaseModel): extra='forbid', ) source_repository: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Allowlisted source repository that the npm provenance attestation MUST identify.' ), @@ -30,7 +30,7 @@ class Provenance(AdCPBaseModel): @field_validator('source_repository') @classmethod - def _require_github_source_repository(cls, value: AnyUrl) -> AnyUrl: + def _require_github_source_repository(cls, value: WireAnyUrl) -> WireAnyUrl: if value.scheme != 'https' or value.host != 'github.com' or value.port != 443: raise ValueError('source_repository must use https://github.com/') if value.username is not None or value.password is not None: diff --git a/src/adcp/types/domains/core/registry_event.py b/src/adcp/types/domains/core/registry_event.py index 5943fa871..050c93487 100644 --- a/src/adcp/types/domains/core/registry_event.py +++ b/src/adcp/types/domains/core/registry_event.py @@ -1,16 +1,15 @@ # generated by datamodel-codegen: # filename: core/registry_event.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from uuid import UUID -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime, WireUuid from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, RootModel, StrictBool +from pydantic import ConfigDict, Field, RootModel, StrictBool from ..enums import adcp_protocol, collection_kind, distribution_identifier_type from . import agent_signing_key @@ -56,10 +55,10 @@ class Payload(AdCPBaseModel): extra='allow', ) alias_rid: Annotated[ - UUID, Field(description='Retired property_rid that now aliases to canonical_rid.') + WireUuid, Field(description='Retired property_rid that now aliases to canonical_rid.') ] canonical_rid: Annotated[ - UUID, Field(description='Canonical property_rid that consumers should retain.') + WireUuid, Field(description='Canonical property_rid that consumers should retain.') ] evidence: Annotated[ str | None, @@ -72,7 +71,7 @@ class Payload(AdCPBaseModel): class RegistryEvent3(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -103,7 +102,7 @@ class RegistryEvent3(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -114,14 +113,14 @@ class Payload1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - property_rid: UUID - last_resolved_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + property_rid: WireUuid + last_resolved_at: Annotated[WireDatetime | None, NoExplicitNull] = None reason: Annotated[str | None, NoExplicitNull] = None class RegistryEvent4(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -152,7 +151,7 @@ class RegistryEvent4(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -164,10 +163,10 @@ class Payload3(AdCPBaseModel): extra='allow', ) alias_rid: Annotated[ - UUID, Field(description='Retired collection_rid that now aliases to canonical_rid.') + WireUuid, Field(description='Retired collection_rid that now aliases to canonical_rid.') ] canonical_rid: Annotated[ - UUID, Field(description='Canonical collection_rid that consumers should retain.') + WireUuid, Field(description='Canonical collection_rid that consumers should retain.') ] evidence: Annotated[ str | None, @@ -180,7 +179,7 @@ class Payload3(AdCPBaseModel): class RegistryEvent8(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -211,7 +210,7 @@ class RegistryEvent8(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -331,7 +330,7 @@ class AgentProfilePayload(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - agent_url: Annotated[AnyUrl | None, NoExplicitNull] = None + agent_url: Annotated[WireAnyUrl | None, NoExplicitNull] = None name: Annotated[str | None, NoExplicitNull] = None type: Annotated[Type | None, NoExplicitNull] = None channels: Annotated[StringArray | None, NoExplicitNull] = None @@ -353,7 +352,7 @@ class AgentProfilePayload(AdCPBaseModel): property_count: Annotated[SchemaInt | None, Field(ge=0), NoExplicitNull] = None publisher_count: Annotated[SchemaInt | None, Field(ge=0), NoExplicitNull] = None has_tmp: Annotated[StrictBool | None, NoExplicitNull] = None - updated_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + updated_at: Annotated[WireDatetime | None, NoExplicitNull] = None class Country(Market): @@ -383,7 +382,7 @@ class StoryboardStatus(StrEnum): class RegistryEvent10(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -414,7 +413,7 @@ class RegistryEvent10(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -425,7 +424,7 @@ class Payload5(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - agent_url: AnyUrl + agent_url: WireAnyUrl removed_from_publishers: Annotated[list[Domain] | None, Field(min_length=1), NoExplicitNull] = ( None ) @@ -433,7 +432,7 @@ class Payload5(AdCPBaseModel): class RegistryEvent11(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -464,7 +463,7 @@ class RegistryEvent11(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -478,7 +477,7 @@ class Payload6(AgentProfilePayload): class RegistryEvent12(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -509,7 +508,7 @@ class RegistryEvent12(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -530,7 +529,7 @@ class Payload7(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - agent_url: AnyUrl + agent_url: WireAnyUrl previous_status: ComplianceStatus current_status: ComplianceStatus headline: str | None = None @@ -544,7 +543,7 @@ class Payload7(AdCPBaseModel): class RegistryEvent13(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -575,7 +574,7 @@ class RegistryEvent13(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -586,7 +585,7 @@ class Payload8(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - agent_url: AnyUrl + agent_url: WireAnyUrl role: BadgeRole verified_specialisms: Annotated[list[str], Field(min_length=1)] adcp_version: Annotated[str | None, NoExplicitNull] = None @@ -601,7 +600,7 @@ class Payload8(AdCPBaseModel): class RegistryEvent14(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -632,7 +631,7 @@ class RegistryEvent14(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -643,7 +642,7 @@ class Payload9(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - agent_url: AnyUrl + agent_url: WireAnyUrl role: BadgeRole reason: str adcp_version: Annotated[str | None, NoExplicitNull] = None @@ -658,7 +657,7 @@ class Payload9(AdCPBaseModel): class RegistryEvent15(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -689,7 +688,7 @@ class RegistryEvent15(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -711,8 +710,8 @@ class Payload10(AdCPBaseModel): ] = None properties_added: Annotated[SchemaInt | None, Field(ge=0), NoExplicitNull] = None properties_removed: Annotated[SchemaInt | None, Field(ge=0), NoExplicitNull] = None - agents_added: Annotated[list[AnyUrl] | None, NoExplicitNull] = None - agents_removed: Annotated[list[AnyUrl] | None, NoExplicitNull] = None + agents_added: Annotated[list[WireAnyUrl] | None, NoExplicitNull] = None + agents_removed: Annotated[list[WireAnyUrl] | None, NoExplicitNull] = None agent_count: Annotated[SchemaInt | None, Field(ge=0), NoExplicitNull] = None property_count: Annotated[SchemaInt | None, Field(ge=0), NoExplicitNull] = None collection_count: Annotated[SchemaInt | None, Field(ge=0), NoExplicitNull] = None @@ -736,7 +735,7 @@ class Payload10(AdCPBaseModel): class RegistryEvent16(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -767,7 +766,7 @@ class RegistryEvent16(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -779,13 +778,13 @@ class Payload12(AdCPBaseModel): extra='allow', ) id: Annotated[ - UUID | None, + WireUuid | None, Field( description='Registry authorization row id when the event is backed by a materialized effective authorization row.' ), NoExplicitNull, ] = None - agent_url: AnyUrl + agent_url: WireAnyUrl agent_url_canonical: Annotated[ str | None, Field(description='Registry-canonicalized form of agent_url for equality checks.'), @@ -815,7 +814,7 @@ class Payload12(AdCPBaseModel): NoExplicitNull, ] = None property_rid: Annotated[ - UUID | None, + WireUuid | None, Field( description='Catalog property_rid for materialized per-property authorization rows. Null for publisher-wide rows.' ), @@ -847,21 +846,21 @@ class Payload12(AdCPBaseModel): ), NoExplicitNull, ] = None - effective_from: Annotated[AwareDatetime | None, NoExplicitNull] = None - effective_until: Annotated[AwareDatetime | None, NoExplicitNull] = None + effective_from: Annotated[WireDatetime | None, NoExplicitNull] = None + effective_until: Annotated[WireDatetime | None, NoExplicitNull] = None evidence: Annotated[Evidence | None, NoExplicitNull] = None disputed: Annotated[StrictBool | None, NoExplicitNull] = None created_by: str | None = None - expires_at: AwareDatetime | None = None - created_at: AwareDatetime | None = None - updated_at: AwareDatetime | None = None + expires_at: WireDatetime | None = None + created_at: WireDatetime | None = None + updated_at: WireDatetime | None = None override_applied: Annotated[StrictBool | None, NoExplicitNull] = None override_reason: str | None = None class RegistryEvent18(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -892,7 +891,7 @@ class RegistryEvent18(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -905,7 +904,7 @@ class Payload13(Payload12): class RegistryEvent19(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -936,7 +935,7 @@ class RegistryEvent19(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -949,7 +948,7 @@ class Payload14(Payload12): class RegistryEvent20(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -980,7 +979,7 @@ class RegistryEvent20(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -995,7 +994,7 @@ class PropertyPayload(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - property_rid: Annotated[UUID | None, NoExplicitNull] = None + property_rid: Annotated[WireUuid | None, NoExplicitNull] = None classification: Annotated[Classification | None, NoExplicitNull] = None source: Annotated[PropertySource | None, NoExplicitNull] = None identifiers: Annotated[ @@ -1020,7 +1019,7 @@ class CompliancePayload(Payload7): class RegistryEvent1(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -1051,7 +1050,7 @@ class RegistryEvent1(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -1060,7 +1059,7 @@ class RegistryEvent1(AdCPBaseModel): class RegistryEvent2(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -1091,7 +1090,7 @@ class RegistryEvent2(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -1099,13 +1098,13 @@ class RegistryEvent2(AdCPBaseModel): class Payload2(PropertyPayload): - reactivated_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + reactivated_at: Annotated[WireDatetime | None, NoExplicitNull] = None property_rid: Any class RegistryEvent5(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -1136,7 +1135,7 @@ class RegistryEvent5(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -1146,14 +1145,14 @@ class RegistryEvent5(AdCPBaseModel): class Payload11(PublisherAdagentsPayload): properties_added: Annotated[SchemaInt | None, Field(ge=0), NoExplicitNull] = None properties_removed: Annotated[SchemaInt | None, Field(ge=0), NoExplicitNull] = None - agents_added: Annotated[list[AnyUrl] | None, NoExplicitNull] = None - agents_removed: Annotated[list[AnyUrl] | None, NoExplicitNull] = None + agents_added: Annotated[list[WireAnyUrl] | None, NoExplicitNull] = None + agents_removed: Annotated[list[WireAnyUrl] | None, NoExplicitNull] = None changed_fields: Annotated[ChangedFields | None, NoExplicitNull] = None class RegistryEvent17(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -1184,7 +1183,7 @@ class RegistryEvent17(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -1195,7 +1194,7 @@ class CollectionPayload(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - collection_rid: Annotated[UUID | None, NoExplicitNull] = None + collection_rid: Annotated[WireUuid | None, NoExplicitNull] = None publisher_domain: Annotated[Domain | None, NoExplicitNull] = None collection_id: Annotated[ str | None, @@ -1220,7 +1219,7 @@ class CollectionPayload(AdCPBaseModel): class RegistryEvent6(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -1251,7 +1250,7 @@ class RegistryEvent6(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -1260,7 +1259,7 @@ class RegistryEvent6(AdCPBaseModel): class RegistryEvent7(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -1291,7 +1290,7 @@ class RegistryEvent7(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), @@ -1306,7 +1305,7 @@ class Payload4(CollectionPayload): class RegistryEvent9(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier and feed cursor. UUID v7 is REQUIRED so consumers can apply events in event_id order without relying on producer clocks.' ), @@ -1337,7 +1336,7 @@ class RegistryEvent9(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the registry emitted the event. Advisory only; consumers MUST order and cursor by event_id.' ), diff --git a/src/adcp/types/domains/core/registry_feed_response.py b/src/adcp/types/domains/core/registry_feed_response.py index 7ec6e80bf..b4d629a28 100644 --- a/src/adcp/types/domains/core/registry_feed_response.py +++ b/src/adcp/types/domains/core/registry_feed_response.py @@ -1,14 +1,13 @@ # generated by datamodel-codegen: # filename: core/registry_feed_response.json -# timestamp: 2026-10-03T16:00:16+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from uuid import UUID -from adcp.types.base import AdCPBaseModel, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, SchemaInt, WireDatetime, WireUuid +from pydantic import ConfigDict, Field, StrictBool from . import registry_event @@ -18,10 +17,10 @@ class Freshness(AdCPBaseModel): extra='forbid', ) generated_at: Annotated[ - AwareDatetime, Field(description='Server timestamp when this feed page was generated.') + WireDatetime, Field(description='Server timestamp when this feed page was generated.') ] latest_event_created_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Newest event creation timestamp currently visible in the feed for the requested type filter. Null when no matching event exists inside retention.' ), @@ -45,7 +44,7 @@ class RegistryFeedResponse(AdCPBaseModel): ) events: list[registry_event.RegistryEvent] cursor: Annotated[ - UUID | None, + WireUuid | None, Field( description='Pass this value as cursor on the next request to continue polling. Null only when the feed has no events and no prior cursor.' ), diff --git a/src/adcp/types/domains/core/reporting_adjustment.py b/src/adcp/types/domains/core/reporting_adjustment.py index 071cac2d2..7125e83a4 100644 --- a/src/adcp/types/domains/core/reporting_adjustment.py +++ b/src/adcp/types/domains/core/reporting_adjustment.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reporting_adjustment.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from . import reporting_control_total @@ -26,8 +26,8 @@ class AccountingPeriod(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - start: AwareDatetime - end: AwareDatetime + start: WireDatetime + end: WireDatetime class ReportingAdjustment(AdCPBaseModel): @@ -80,5 +80,5 @@ class ReportingAdjustment(AdCPBaseModel): ), NoExplicitNull, ] = None - correction_observed_at: AwareDatetime - created_at: AwareDatetime + correction_observed_at: WireDatetime + created_at: WireDatetime diff --git a/src/adcp/types/domains/core/reporting_adjustment_receipt.py b/src/adcp/types/domains/core/reporting_adjustment_receipt.py index 371f3a110..e3a7f0f5c 100644 --- a/src/adcp/types/domains/core/reporting_adjustment_receipt.py +++ b/src/adcp/types/domains/core/reporting_adjustment_receipt.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/reporting_adjustment_receipt.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field class Status(StrEnum): @@ -56,5 +56,5 @@ class ReportingAdjustmentReceipt(AdCPBaseModel): rejection_codes: Annotated[ list[ReportingAdjustmentRejectionCode] | None, Field(min_length=1), NoExplicitNull ] = None - observed_at: AwareDatetime - received_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + observed_at: WireDatetime + received_at: Annotated[WireDatetime | None, NoExplicitNull] = None diff --git a/src/adcp/types/domains/core/reporting_canonical_content_digest.py b/src/adcp/types/domains/core/reporting_canonical_content_digest.py index 84d8f7272..d8e639b08 100644 --- a/src/adcp/types/domains/core/reporting_canonical_content_digest.py +++ b/src/adcp/types/domains/core/reporting_canonical_content_digest.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/reporting_canonical_content_digest.json -# timestamp: 2026-10-03T15:15:43+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, WireAnyUrl +from pydantic import ConfigDict, Field class ReportingCanonicalContentDigest(AdCPBaseModel): @@ -19,7 +19,7 @@ class ReportingCanonicalContentDigest(AdCPBaseModel): value: Annotated[str, Field(pattern='^[A-Fa-f0-9]{64}$')] canonicalization_id: Annotated[str, Field(max_length=128, min_length=1)] canonicalization_uri: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Location of the exact immutable canonicalization contract. Consumers verify canonicalization_sha256 before applying it.' ), diff --git a/src/adcp/types/domains/core/reporting_consumer_status.py b/src/adcp/types/domains/core/reporting_consumer_status.py index fde8eed4e..293c525ae 100644 --- a/src/adcp/types/domains/core/reporting_consumer_status.py +++ b/src/adcp/types/domains/core/reporting_consumer_status.py @@ -1,22 +1,22 @@ # generated by datamodel-codegen: # filename: core/reporting_consumer_status.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field class Period(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - start: AwareDatetime - end: AwareDatetime + start: WireDatetime + end: WireDatetime source_timezone: Annotated[str, Field(min_length=1)] @@ -122,7 +122,7 @@ class ReportingConsumerStatus(AdCPBaseModel): ), ] status_as_of: Annotated[ - AwareDatetime, + WireDatetime, Field( description='When the consumer established this status. For received, this is when the named revision first became consumable to this consumer; sellers use it as buyer-attributed arrival evidence rather than silently substituting publication time.' ), @@ -161,14 +161,14 @@ class ReportingConsumerStatus(AdCPBaseModel): NoExplicitNull, ] = None seller_ledger_as_of: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ledger_as_of echoed from seller_ledger_snapshot_id. Present if and only if seller_ledger_snapshot_id is present.' ), NoExplicitNull, ] = None recorded_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the seller durably recorded this immutable statement.'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/reporting_coverage.py b/src/adcp/types/domains/core/reporting_coverage.py index 9491e1b06..658eb5469 100644 --- a/src/adcp/types/domains/core/reporting_coverage.py +++ b/src/adcp/types/domains/core/reporting_coverage.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/reporting_coverage.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field class Status(StrEnum): @@ -55,7 +55,7 @@ class ReportingCoverage(AdCPBaseModel): extra='forbid', ) status: Status - evaluated_at: AwareDatetime + evaluated_at: WireDatetime media_buy_ids: Annotated[ list[ReportingMediaBuyId], Field( diff --git a/src/adcp/types/domains/core/reporting_delivery_capabilities.py b/src/adcp/types/domains/core/reporting_delivery_capabilities.py index 11a95495f..c2c3da151 100644 --- a/src/adcp/types/domains/core/reporting_delivery_capabilities.py +++ b/src/adcp/types/domains/core/reporting_delivery_capabilities.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/reporting_delivery_capabilities.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, EmailStr, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireEmail +from pydantic import ConfigDict, Field, StrictBool from . import reporting_delivery_offering, reporting_reliability_statistics @@ -21,14 +21,14 @@ class OperationsContact(AdCPBaseModel): regex_engine="python-re", ) url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='HTTPS page a human uses to open or track a reporting issue, such as a support portal or status page. Same hardened origin shape as the offering document URIs: never an IP literal, userinfo URL, loopback host, AdCP task endpoint, webhook target, or credentialed link.' ), NoExplicitNull, ] = None email: Annotated[ - EmailStr | None, + WireEmail | None, Field( description='Monitored operations mailbox for reporting escalations. A role address, not an individual.', max_length=254, diff --git a/src/adcp/types/domains/core/reporting_delivery_config.py b/src/adcp/types/domains/core/reporting_delivery_config.py index 04a60c6b7..82692fdee 100644 --- a/src/adcp/types/domains/core/reporting_delivery_config.py +++ b/src/adcp/types/domains/core/reporting_delivery_config.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/reporting_delivery_config.json -# timestamp: 2026-10-21T11:25:44+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -11,8 +11,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field, StrictBool from ..enums import reporting_finality from . import ( @@ -160,7 +160,7 @@ class ReportingDeliveryConfiguration(AdCPBaseModel): None ) revocation_effective_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Optional requested cutoff for deactivation. No new publication may begin after the applied cutoff; historical access is limited to the contracted recovery window.' ), diff --git a/src/adcp/types/domains/core/reporting_delivery_config_state.py b/src/adcp/types/domains/core/reporting_delivery_config_state.py index 4209ddd31..f1f6ad96d 100644 --- a/src/adcp/types/domains/core/reporting_delivery_config_state.py +++ b/src/adcp/types/domains/core/reporting_delivery_config_state.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reporting_delivery_config_state.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field from . import reporting_coverage, reporting_delivery_config, reporting_status_issue @@ -33,8 +33,8 @@ class Setup(AdCPBaseModel): min_length=1, ), ] - url: Annotated[AnyUrl | None, NoExplicitNull] = None - expires_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + url: Annotated[WireAnyUrl | None, NoExplicitNull] = None + expires_at: Annotated[WireDatetime | None, NoExplicitNull] = None class ReportingDeliveryConfigLifecycleState(StrEnum): @@ -60,18 +60,18 @@ class ReportingDeliveryConfigurationState(AdCPBaseModel): ), NoExplicitNull, ] = None - validated_at: Annotated[AwareDatetime | None, NoExplicitNull] = None - activated_at: Annotated[AwareDatetime | None, NoExplicitNull] = None - deactivated_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + validated_at: Annotated[WireDatetime | None, NoExplicitNull] = None + activated_at: Annotated[WireDatetime | None, NoExplicitNull] = None + deactivated_at: Annotated[WireDatetime | None, NoExplicitNull] = None publication_stopped_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Applied schedule boundary at or after deactivation. No obligation whose period starts at or after this cutoff is created; earlier obligations remain owed through their SLA and recovery lifecycle.' ), NoExplicitNull, ] = None seller_managed_access_ends_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='End of historical access to a producer-hosted share/resource for a still-authorized principal after voluntary deactivation. Inapplicable to data already written into a buyer-owned destination.' ), diff --git a/src/adcp/types/domains/core/reporting_delivery_offering.py b/src/adcp/types/domains/core/reporting_delivery_offering.py index 8123b8d84..6b04cd66d 100644 --- a/src/adcp/types/domains/core/reporting_delivery_offering.py +++ b/src/adcp/types/domains/core/reporting_delivery_offering.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reporting_delivery_offering.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..enums import reporting_finality from . import ( @@ -29,7 +29,7 @@ class ReportingProfile(AdCPBaseModel): id: Annotated[str, Field(max_length=128, min_length=1, pattern='^[A-Za-z0-9_.:-]{1,128}$')] version: Annotated[str, Field(max_length=64, min_length=1)] schema_uri: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Authenticated seller/provider or AdCP-registry HTTPS origin only; never an IP literal, userinfo URL, redirect target, or mutable validation authority.' ), @@ -78,7 +78,7 @@ class ReportingProfile(AdCPBaseModel): 'application/vnd.adcp.reporting-canonicalization+json' ) canonicalization_uri: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Retrievable exact canonicalization contract on the authenticated seller/provider or AdCP-registry origin. SDKs apply the same bounded, redirect-free SSRF controls as schema_uri and verify canonicalization_sha256 before use.' ), @@ -186,7 +186,7 @@ class ReportingDeliveryOffering(AdCPBaseModel): ), ] report_definition_uri: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Retrievable immutable reporting-report-definition.json document on the authenticated seller/provider or AdCP-registry origin.' ), diff --git a/src/adcp/types/domains/core/reporting_delivery_ready_webhook.py b/src/adcp/types/domains/core/reporting_delivery_ready_webhook.py index 5aa2ee923..88e71f3fe 100644 --- a/src/adcp/types/domains/core/reporting_delivery_ready_webhook.py +++ b/src/adcp/types/domains/core/reporting_delivery_ready_webhook.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reporting_delivery_ready_webhook.json -# timestamp: 2026-10-03T16:00:16+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field from ..enums import reporting_finality from . import reporting_delivery_offering @@ -42,7 +42,7 @@ class ReportingDeliveryReadyWebhook(AdCPBaseModel): ), ] notification_type: Literal['reporting.delivery_ready'] = 'reporting.delivery_ready' - fired_at: AwareDatetime + fired_at: WireDatetime subscriber_id: Annotated[ str, Field(max_length=64, min_length=1, pattern='^[A-Za-z0-9_.:-]{1,64}$') ] @@ -59,5 +59,5 @@ class ReportingDeliveryReadyWebhook(AdCPBaseModel): ] readiness: Readiness finality: reporting_finality.ReportingFinality - data_through: AwareDatetime | None + data_through: WireDatetime | None feed_purpose: reporting_delivery_offering.ReportingFeedPurpose diff --git a/src/adcp/types/domains/core/reporting_file_manifest.py b/src/adcp/types/domains/core/reporting_file_manifest.py index 6bc62f075..72d6d3c72 100644 --- a/src/adcp/types/domains/core/reporting_file_manifest.py +++ b/src/adcp/types/domains/core/reporting_file_manifest.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reporting_file_manifest.json -# timestamp: 2026-10-03T16:00:16+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field from . import reporting_control_total, reporting_file_compression, reporting_file_entry @@ -17,8 +17,8 @@ class Period(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - start: AwareDatetime - end: AwareDatetime + start: WireDatetime + end: WireDatetime source_timezone: Annotated[str, Field(min_length=1)] @@ -52,4 +52,4 @@ class ReportingFileManifest(AdCPBaseModel): total_size_bytes: Annotated[SchemaInt, Field(ge=0)] row_count: Annotated[SchemaInt, Field(ge=0)] control_totals: list[reporting_control_total.ReportingControlTotal] - created_at: AwareDatetime + created_at: WireDatetime diff --git a/src/adcp/types/domains/core/reporting_ledger_changed_webhook.py b/src/adcp/types/domains/core/reporting_ledger_changed_webhook.py index 55fb958a4..d3c43209b 100644 --- a/src/adcp/types/domains/core/reporting_ledger_changed_webhook.py +++ b/src/adcp/types/domains/core/reporting_ledger_changed_webhook.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reporting_ledger_changed_webhook.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..enums import reporting_finality from . import ext as ext_1 @@ -42,7 +42,7 @@ class ReportingLedgerChangedWebhook(AdCPBaseModel): ), ] notification_type: Literal['reporting.ledger_changed'] = 'reporting.ledger_changed' - fired_at: AwareDatetime + fired_at: WireDatetime subscriber_id: Annotated[ str, Field(max_length=64, min_length=1, pattern='^[A-Za-z0-9_.:-]{1,64}$') ] diff --git a/src/adcp/types/domains/core/reporting_materialization.py b/src/adcp/types/domains/core/reporting_materialization.py index 85762a0c6..41b2e238d 100644 --- a/src/adcp/types/domains/core/reporting_materialization.py +++ b/src/adcp/types/domains/core/reporting_materialization.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reporting_materialization.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field from . import reporting_delivery_offering, reporting_resource, reporting_verification @@ -78,11 +78,11 @@ class ReportingMaterialization(AdCPBaseModel): ), ] ready_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When consumer-path or destination verification completed.'), NoExplicitNull, ] = None - failed_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + failed_at: Annotated[WireDatetime | None, NoExplicitNull] = None failure_code: Annotated[ str | None, Field( @@ -97,4 +97,4 @@ class ReportingMaterialization(AdCPBaseModel): verification: Annotated[reporting_verification.ReportingVerification | None, NoExplicitNull] = ( None ) - created_at: AwareDatetime + created_at: WireDatetime diff --git a/src/adcp/types/domains/core/reporting_obligation.py b/src/adcp/types/domains/core/reporting_obligation.py index 22993ed06..804b62a17 100644 --- a/src/adcp/types/domains/core/reporting_obligation.py +++ b/src/adcp/types/domains/core/reporting_obligation.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reporting_obligation.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field from ..enums import reporting_finality, reporting_health from . import ( @@ -24,8 +24,8 @@ class Period(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - start: AwareDatetime - end: AwareDatetime + start: WireDatetime + end: WireDatetime source_timezone: Annotated[str, Field(min_length=1)] @@ -67,7 +67,7 @@ class ReportingObligation(AdCPBaseModel): ), ] scope_resolved_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='Instant at which the configured scope was resolved and frozen for this obligation. For all_media_buys, include every caller-authorized account media buy whose effective flight overlaps the half-open period and was known by this cutoff. Later-created or backdated buys do not rewrite this obligation.' ), @@ -79,7 +79,7 @@ class ReportingObligation(AdCPBaseModel): ), ] period: Period - expected_at: AwareDatetime + expected_at: WireDatetime schedule: Annotated[ reporting_schedule.ReportingSchedule, Field(description='Resolved immutable schedule generation that created this obligation.'), @@ -199,7 +199,7 @@ class ReportingObligation(AdCPBaseModel): ] = None issues: list[reporting_status_issue.ReportingStatusIssue] resource_retained_until: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Minimum time through which at least one verified materialization for a completed obligation remains readable. Managed-delivery only; Core revisions are retained per status_retention_days and readable through the existing API transports.' ), diff --git a/src/adcp/types/domains/core/reporting_receipt.py b/src/adcp/types/domains/core/reporting_receipt.py index 17703e45c..ac2342e61 100644 --- a/src/adcp/types/domains/core/reporting_receipt.py +++ b/src/adcp/types/domains/core/reporting_receipt.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/reporting_receipt.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field from . import ( reporting_canonical_content_digest, @@ -80,5 +80,5 @@ class ReportingReceipt(AdCPBaseModel): rejection_codes: Annotated[list[RejectionCode] | None, Field(min_length=1), NoExplicitNull] = ( None ) - observed_at: AwareDatetime - received_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + observed_at: WireDatetime + received_at: Annotated[WireDatetime | None, NoExplicitNull] = None diff --git a/src/adcp/types/domains/core/reporting_reliability_statistics.py b/src/adcp/types/domains/core/reporting_reliability_statistics.py index 8faa6a66a..6e0795d74 100644 --- a/src/adcp/types/domains/core/reporting_reliability_statistics.py +++ b/src/adcp/types/domains/core/reporting_reliability_statistics.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reporting_reliability_statistics.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field class AdjustmentMagnitudeItem(AdCPBaseModel): @@ -33,8 +33,8 @@ class Evidence(AdCPBaseModel): regex_engine="python-re", ) basis: Basis - generated_at: AwareDatetime - attestation_uri: Annotated[AnyUrl | None, NoExplicitNull] = None + generated_at: WireDatetime + attestation_uri: Annotated[WireAnyUrl | None, NoExplicitNull] = None attestation_sha256: Annotated[ str | None, Field(pattern='^[A-Fa-f0-9]{64}$'), NoExplicitNull ] = None @@ -52,8 +52,8 @@ class ReportingReliabilityMeasurementPeriod(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - start: AwareDatetime - end: AwareDatetime + start: WireDatetime + end: WireDatetime class ReportingReliabilityStatistics(AdCPBaseModel): diff --git a/src/adcp/types/domains/core/reporting_resource.py b/src/adcp/types/domains/core/reporting_resource.py index 0393eaa3c..02548a36a 100644 --- a/src/adcp/types/domains/core/reporting_resource.py +++ b/src/adcp/types/domains/core/reporting_resource.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/reporting_resource.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field from . import reporting_native_version_ref @@ -75,7 +75,7 @@ class ReportingResource(AdCPBaseModel): Field(description='How this descriptor selects the exact immutable materialization.'), ] expires_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='Mandatory finite lower-bound endpoint through which this exact resource remains resolvable; it cannot be earlier than the advertised retention contract.' ), diff --git a/src/adcp/types/domains/core/reporting_revision.py b/src/adcp/types/domains/core/reporting_revision.py index 3114f0d3a..04abbc93f 100644 --- a/src/adcp/types/domains/core/reporting_revision.py +++ b/src/adcp/types/domains/core/reporting_revision.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reporting_revision.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field from ..enums import reporting_finality from . import reporting_canonical_content_digest, reporting_control_total, reporting_coverage @@ -18,8 +18,8 @@ class Period(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - start: AwareDatetime - end: AwareDatetime + start: WireDatetime + end: WireDatetime source_timezone: Annotated[str, Field(min_length=1)] @@ -65,12 +65,12 @@ class ReportingRevision(AdCPBaseModel): pattern='^[A-Za-z0-9_.:-]{1,255}$', ), ] - report_definition_uri: AnyUrl + report_definition_uri: WireAnyUrl report_definition_sha256: Annotated[str, Field(pattern='^[A-Fa-f0-9]{64}$')] reporting_profile: Annotated[str, Field(max_length=128, min_length=1)] schema_version: Annotated[str, Field(max_length=64, min_length=1)] schema_uri: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Machine-readable schema on the authenticated seller/provider or AdCP-registry origin.' ), @@ -127,18 +127,18 @@ class ReportingRevision(AdCPBaseModel): NoExplicitNull, ] = None finalized_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the producer applied the declared finality basis to this official revision.' ), NoExplicitNull, ] = None observed_at: Annotated[ - AwareDatetime, + WireDatetime, Field(description='When the seller obtained or committed this source observation.'), ] data_through: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Latest event time conservatively included, or null when precision is unknown.' ), @@ -170,4 +170,4 @@ class ReportingRevision(AdCPBaseModel): canonical_content_digest: Annotated[ reporting_canonical_content_digest.ReportingCanonicalContentDigest | None, NoExplicitNull ] = None - created_at: AwareDatetime + created_at: WireDatetime diff --git a/src/adcp/types/domains/core/reporting_schedule.py b/src/adcp/types/domains/core/reporting_schedule.py index 28a05ae60..8524b4b26 100644 --- a/src/adcp/types/domains/core/reporting_schedule.py +++ b/src/adcp/types/domains/core/reporting_schedule.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reporting_schedule.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field class ReportingScheduleAlignment(StrEnum): @@ -32,7 +32,7 @@ class ReportingSchedule(AdCPBaseModel): ] alignment: ReportingScheduleAlignment period_anchor: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Required for billing_cycle alignment. This immutable instant anchors the recurring half-open billing periods so producer and consumer derive the same month, quarter, or other contractual cycle.' ), diff --git a/src/adcp/types/domains/core/reporting_schedule_offering.py b/src/adcp/types/domains/core/reporting_schedule_offering.py index b52b0aced..a07a0cd48 100644 --- a/src/adcp/types/domains/core/reporting_schedule_offering.py +++ b/src/adcp/types/domains/core/reporting_schedule_offering.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reporting_schedule_offering.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from . import reporting_schedule @@ -49,7 +49,7 @@ class ReportingScheduleOffering(AdCPBaseModel): ), NoExplicitNull, ] = None - period_anchor: Annotated[AwareDatetime | None, NoExplicitNull] = None + period_anchor: Annotated[WireDatetime | None, NoExplicitNull] = None period_timezone: Annotated[str | None, Field(max_length=255, min_length=1), NoExplicitNull] = ( None ) diff --git a/src/adcp/types/domains/core/reporting_status_changed_webhook.py b/src/adcp/types/domains/core/reporting_status_changed_webhook.py index cc98bb9d0..01407d9a8 100644 --- a/src/adcp/types/domains/core/reporting_status_changed_webhook.py +++ b/src/adcp/types/domains/core/reporting_status_changed_webhook.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reporting_status_changed_webhook.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field from ..enums import reporting_health from . import ext as ext_1 @@ -43,7 +43,7 @@ class ReportingStatusChangedWebhook(AdCPBaseModel): ), ] notification_type: Literal['reporting.status_changed'] = 'reporting.status_changed' - fired_at: AwareDatetime + fired_at: WireDatetime subscriber_id: Annotated[ str, Field( diff --git a/src/adcp/types/domains/core/reporting_status_issue.py b/src/adcp/types/domains/core/reporting_status_issue.py index 873a23820..5024607b3 100644 --- a/src/adcp/types/domains/core/reporting_status_issue.py +++ b/src/adcp/types/domains/core/reporting_status_issue.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reporting_status_issue.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field from . import reporting_coverage, reporting_delivery_offering @@ -76,7 +76,7 @@ class ReportingStatusIssue(AdCPBaseModel): code: Code severity: ReportingStatusSeverity opened_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the seller first observed this logical condition, carried unchanged across every re-emission until the issue is retired. It anchors the escalation clock advertised as consumer_mismatch_escalation_seconds and lets a consumer age an issue without keeping its own first-seen table. Required when code is CONSUMER_STATUS_MISMATCH.' ), @@ -149,6 +149,6 @@ class ReportingStatusIssue(AdCPBaseModel): package_ids: Annotated[ list[reporting_coverage.ReportingPackageId] | None, Field(min_length=1), NoExplicitNull ] = None - period_start: Annotated[AwareDatetime | None, NoExplicitNull] = None - period_end: Annotated[AwareDatetime | None, NoExplicitNull] = None - expected_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + period_start: Annotated[WireDatetime | None, NoExplicitNull] = None + period_end: Annotated[WireDatetime | None, NoExplicitNull] = None + expected_at: Annotated[WireDatetime | None, NoExplicitNull] = None diff --git a/src/adcp/types/domains/core/reporting_verification.py b/src/adcp/types/domains/core/reporting_verification.py index 2aad46853..1aba7425d 100644 --- a/src/adcp/types/domains/core/reporting_verification.py +++ b/src/adcp/types/domains/core/reporting_verification.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reporting_verification.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field from . import ( reporting_canonical_content_digest, @@ -61,7 +61,7 @@ class ReportingVerification(AdCPBaseModel): extra='forbid', ) verified_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='When the producer completed verification through the claimed consumer/destination path.' ), diff --git a/src/adcp/types/domains/core/reporting_webhook.py b/src/adcp/types/domains/core/reporting_webhook.py index 58d23feac..32ffe7163 100644 --- a/src/adcp/types/domains/core/reporting_webhook.py +++ b/src/adcp/types/domains/core/reporting_webhook.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/reporting_webhook.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..enums import auth_scheme, available_metric @@ -44,7 +44,7 @@ class ReportingWebhook(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - url: Annotated[AnyUrl, Field(description='Webhook endpoint URL for reporting notifications')] + url: Annotated[WireAnyUrl, Field(description='Webhook endpoint URL for reporting notifications')] token: Annotated[ str | None, Field( diff --git a/src/adcp/types/domains/core/response_payload_jws_envelope.py b/src/adcp/types/domains/core/response_payload_jws_envelope.py index 17e1f4f20..ca86594fd 100644 --- a/src/adcp/types/domains/core/response_payload_jws_envelope.py +++ b/src/adcp/types/domains/core/response_payload_jws_envelope.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/response_payload_jws_envelope.json -# timestamp: 2026-10-03T16:00:16+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field class Task(StrEnum): @@ -33,7 +33,7 @@ class ResponsePayload(AdCPBaseModel): ), ] agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical URL of the responding brand agent entry whose response-signing key verifies this envelope.' ), diff --git a/src/adcp/types/domains/core/rights_attestation_evaluation.py b/src/adcp/types/domains/core/rights_attestation_evaluation.py index 74c7452d4..0ed4445ff 100644 --- a/src/adcp/types/domains/core/rights_attestation_evaluation.py +++ b/src/adcp/types/domains/core/rights_attestation_evaluation.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/rights_attestation_evaluation.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from . import brand_ref from . import ext as ext_1 @@ -22,7 +22,7 @@ class Subject(AdCPBaseModel): type: Literal['agent'] = 'agent' resource_type: Literal['https://adcontextprotocol.org/claims/subjects/rights-grant'] = 'https://adcontextprotocol.org/claims/subjects/rights-grant' agent_url: Annotated[ - AnyUrl, Field(description='Canonical HTTPS endpoint of the agent the claim concerns.') + WireAnyUrl, Field(description='Canonical HTTPS endpoint of the agent the claim concerns.') ] ext: Annotated[ext_1.ExtensionObject | None, NoExplicitNull] = None @@ -39,7 +39,7 @@ class Subject19(AdCPBaseModel): ), ] = 'https://adcontextprotocol.org/claims/subjects/rights-grant' namespace: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Absolute URI identifying the namespace in which id is unique. This may be an AdCP agent endpoint, a catalog origin, or a domain-specific namespace URI.' ), @@ -121,7 +121,7 @@ class Issuer5(AdCPBaseModel): ] = 'agent' brand: brand_ref.BrandReference agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical HTTPS endpoint of the issuing agent. Evaluators compare it using AdCP URL canonicalization rules.' ), @@ -141,7 +141,7 @@ class Issuer6(AdCPBaseModel): ] = 'origin' brand: brand_ref.BrandReference origin: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical HTTPS origin with no path, query, fragment, or userinfo. This identifies the issuer; it does not authorize a fetch.' ), diff --git a/src/adcp/types/domains/core/rights_constraint.py b/src/adcp/types/domains/core/rights_constraint.py index 6f335c198..ddc8bc266 100644 --- a/src/adcp/types/domains/core/rights_constraint.py +++ b/src/adcp/types/domains/core/rights_constraint.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/rights_constraint.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool +from pydantic import ConfigDict, Field, StrictBool from ..enums import right_type as right_type_1 from ..enums import right_use @@ -19,7 +19,7 @@ class RightsAgent(AdCPBaseModel): - url: Annotated[AnyUrl, Field(description='MCP endpoint URL of the rights agent')] + url: Annotated[WireAnyUrl, Field(description='MCP endpoint URL of the rights agent')] id: Annotated[str, Field(description='Agent identifier')] @@ -64,7 +64,7 @@ class Subject(AdCPBaseModel): type: Literal['agent'] = 'agent' resource_type: Literal['https://adcontextprotocol.org/claims/subjects/rights-grant'] = 'https://adcontextprotocol.org/claims/subjects/rights-grant' agent_url: Annotated[ - AnyUrl, Field(description='Canonical HTTPS endpoint of the agent the claim concerns.') + WireAnyUrl, Field(description='Canonical HTTPS endpoint of the agent the claim concerns.') ] ext: Annotated[ext_1.ExtensionObject | None, NoExplicitNull] = None @@ -81,7 +81,7 @@ class Subject29(AdCPBaseModel): ), ] = 'https://adcontextprotocol.org/claims/subjects/rights-grant' namespace: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Absolute URI identifying the namespace in which id is unique. This may be an AdCP agent endpoint, a catalog origin, or a domain-specific namespace URI.' ), @@ -127,7 +127,7 @@ class Issuer8(AdCPBaseModel): ] = 'agent' brand: brand_ref.BrandReference agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical HTTPS endpoint of the issuing agent. Evaluators compare it using AdCP URL canonicalization rules.' ), @@ -147,7 +147,7 @@ class Issuer9(AdCPBaseModel): ] = 'origin' brand: brand_ref.BrandReference origin: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Canonical HTTPS origin with no path, query, fragment, or userinfo. This identifies the issuer; it does not authorize a fetch.' ), @@ -228,12 +228,12 @@ class RightsConstraint(AdCPBaseModel): NoExplicitNull, ] = None valid_from: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Start of the rights validity period'), NoExplicitNull, ] = None valid_until: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='End of the rights validity period. Creative should not be served after this time.' ), @@ -309,7 +309,7 @@ class RightsConstraint(AdCPBaseModel): NoExplicitNull, ] = None verification_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( deprecated=True, description='DEPRECATED legacy informational locator. It is not proof of grant status, a revocation source, a credential locator, or permission to make a network request. Receivers MUST NOT fetch it during rights evaluation, and HTTP status codes have no AdCP authorization meaning. Rights authorization uses attestation_refs and a verifier-of-record evaluation under evaluator-owned policy.', diff --git a/src/adcp/types/domains/core/seller_agent_ref.py b/src/adcp/types/domains/core/seller_agent_ref.py index c1b0e0e1a..f8813d241 100644 --- a/src/adcp/types/domains/core/seller_agent_ref.py +++ b/src/adcp/types/domains/core/seller_agent_ref.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/seller_agent_ref.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field class SellerAgentReference(AdCPBaseModel): @@ -15,7 +15,7 @@ class SellerAgentReference(AdCPBaseModel): extra='forbid', ) agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="The seller agent's API endpoint URL as declared in the property publisher's adagents.json `authorized_agents[].url`. MUST use the `https://` scheme. Receivers compare this URL against the `authorized_agents` list using the AdCP URL canonicalization rules — not byte-equality — and reject mismatches with `seller_not_authorized`. See docs/reference/url-canonicalization." ), diff --git a/src/adcp/types/domains/core/signal_coverage_forecast.py b/src/adcp/types/domains/core/signal_coverage_forecast.py index 5a1b4ea92..0bccccb74 100644 --- a/src/adcp/types/domains/core/signal_coverage_forecast.py +++ b/src/adcp/types/domains/core/signal_coverage_forecast.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/signal_coverage_forecast.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Dict, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat +from pydantic import ConfigDict, Field, StrictFloat from ..enums import forecast_method from . import date_range as date_range_1 @@ -180,12 +180,12 @@ class SignalCoverageForecast(AdCPBaseModel): ), ] generated_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When this coverage forecast was computed.'), NoExplicitNull, ] = None valid_until: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When this coverage forecast expires.'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/signal_definition.py b/src/adcp/types/domains/core/signal_definition.py index 68ccb87eb..78921a286 100644 --- a/src/adcp/types/domains/core/signal_definition.py +++ b/src/adcp/types/domains/core/signal_definition.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/signal_definition.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime, WireEmail from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, AwareDatetime, ConfigDict, EmailStr, Field, StrictBool, StrictFloat +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..enums import audience_resolution_method, audience_subject_type from ..enums import consent_basis as consent_basis_1 @@ -88,7 +88,7 @@ class Taxonomy(AdCPBaseModel): extra='forbid', ) ref: Annotated[ - AnyUrl, Field(description='URI identifying the taxonomy or taxonomy documentation.') + WireAnyUrl, Field(description='URI identifying the taxonomy or taxonomy documentation.') ] version: Annotated[ str | None, @@ -287,12 +287,12 @@ class Channel(AdCPBaseModel): list[Right], Field(description='Rights supported by this channel.', min_length=1) ] url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='HTTPS URL for submitting the rights request.'), NoExplicitNull, ] = None email: Annotated[ - EmailStr | None, + WireEmail | None, Field(description='Email address for submitting the rights request.'), NoExplicitNull, ] = None @@ -340,7 +340,7 @@ class DataSubjectRights(AdCPBaseModel): NoExplicitNull, ] = None ccpa_opt_out_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description="US-specific 'Do Not Sell or Share' opt-out URL where required."), NoExplicitNull, ] = None @@ -459,7 +459,7 @@ class SignalDefinition(AdCPBaseModel): NoExplicitNull, ] = None criteria_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional URL to a longer-form methodology or criteria document. This is a disclosure pointer; buyers should not branch programmatically on the linked content.' ), @@ -584,7 +584,7 @@ class SignalDefinition(AdCPBaseModel): NoExplicitNull, ] = None last_updated: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When this definition record was last updated. This indicates freshness of the definition record, not an attestation that the underlying data or model was refreshed at that time.' ), diff --git a/src/adcp/types/domains/core/signal_definition_enrichment.py b/src/adcp/types/domains/core/signal_definition_enrichment.py index 62b7af392..1cdfa1d30 100644 --- a/src/adcp/types/domains/core/signal_definition_enrichment.py +++ b/src/adcp/types/domains/core/signal_definition_enrichment.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/signal_definition_enrichment.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime, WireEmail from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, AwareDatetime, ConfigDict, EmailStr, Field, StrictBool +from pydantic import ConfigDict, Field, StrictBool from ..enums import consent_basis as consent_basis_1 from ..enums import restricted_attribute @@ -46,7 +46,7 @@ class Taxonomy(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - ref: AnyUrl + ref: WireAnyUrl version: Annotated[str | None, NoExplicitNull] = None segtax: Annotated[SchemaInt | None, Field(ge=1), NoExplicitNull] = None etag: Annotated[str | None, NoExplicitNull] = None @@ -189,8 +189,8 @@ class Channel(AdCPBaseModel): extra='forbid', ) rights: Annotated[list[Right], Field(min_length=1)] - url: Annotated[AnyUrl | None, NoExplicitNull] = None - email: Annotated[EmailStr | None, NoExplicitNull] = None + url: Annotated[WireAnyUrl | None, NoExplicitNull] = None + email: Annotated[WireEmail | None, NoExplicitNull] = None languages: Annotated[list[str] | None, NoExplicitNull] = None countries: Annotated[list[Country] | None, NoExplicitNull] = None @@ -209,7 +209,7 @@ class DataSubjectRights(AdCPBaseModel): ] = None channels: Annotated[list[Channel], Field(min_length=1)] response_sla_days: Annotated[SchemaInt | None, Field(ge=1, le=90), NoExplicitNull] = None - ccpa_opt_out_url: Annotated[AnyUrl | None, NoExplicitNull] = None + ccpa_opt_out_url: Annotated[WireAnyUrl | None, NoExplicitNull] = None class Modeling(AdCPBaseModel): @@ -254,7 +254,7 @@ class SignalDefinitionEnrichment(AdCPBaseModel): NoExplicitNull, ] = None segmentation_criteria: Annotated[str | None, Field(max_length=500), NoExplicitNull] = None - criteria_url: Annotated[AnyUrl | None, NoExplicitNull] = None + criteria_url: Annotated[WireAnyUrl | None, NoExplicitNull] = None data_sources: Annotated[list[DataSource] | None, Field(min_length=1), NoExplicitNull] = None methodology: Annotated[Methodology | None, NoExplicitNull] = None audience_expansion: Annotated[StrictBool | None, NoExplicitNull] = None @@ -287,7 +287,7 @@ class SignalDefinitionEnrichment(AdCPBaseModel): NoExplicitNull, ] = None last_updated: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When this definition record was last updated. This indicates freshness of the definition record, not an attestation that the underlying data or model was refreshed at that time.' ), diff --git a/src/adcp/types/domains/core/signal_id.py b/src/adcp/types/domains/core/signal_id.py index 85041b060..27ddfad8a 100644 --- a/src/adcp/types/domains/core/signal_id.py +++ b/src/adcp/types/domains/core/signal_id.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/signal_id.json -# timestamp: 2026-10-02T23:18:44+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, WireAnyUrl +from pydantic import ConfigDict, Field class SignalId8(AdCPBaseModel): @@ -47,7 +47,7 @@ class SignalId9(AdCPBaseModel): ), ] = 'agent' agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="URL of the signal source that provides this signal (e.g., 'https://signals.example/.well-known/adcp/signals')" ), diff --git a/src/adcp/types/domains/core/signal_listing.py b/src/adcp/types/domains/core/signal_listing.py index 6e65b568a..2d4cbe200 100644 --- a/src/adcp/types/domains/core/signal_listing.py +++ b/src/adcp/types/domains/core/signal_listing.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/signal_listing.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictFloat, model_validator +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictFloat, model_validator from ..enums import restricted_attribute, signal_value_type from . import demographic_predicate as demographic_predicate_1 @@ -60,14 +60,14 @@ class SignalListing(AdCPBaseModel): NoExplicitNull, ] = None methodology_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional link to published methodology, media-kit, or data documentation. For data_provider and signal_source refs, this SHOULD match or supplement the referenced definition.' ), NoExplicitNull, ] = None last_updated: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When this listing record was last updated. This indicates freshness of the listing record, not an attestation that the underlying data or model was refreshed at that time.' ), diff --git a/src/adcp/types/domains/core/signal_modeling_disclosure.py b/src/adcp/types/domains/core/signal_modeling_disclosure.py index ea50f557b..896a737e2 100644 --- a/src/adcp/types/domains/core/signal_modeling_disclosure.py +++ b/src/adcp/types/domains/core/signal_modeling_disclosure.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/signal_modeling_disclosure.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool class Audience(StrEnum): @@ -44,7 +44,7 @@ class Jurisdiction(AdCPBaseModel): NoExplicitNull, ] = None disclosure_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="Optional URL to the provider's canonical disclosure or methodology page for this jurisdiction." ), diff --git a/src/adcp/types/domains/core/signal_ref.py b/src/adcp/types/domains/core/signal_ref.py index 888ba70ba..f3dab705f 100644 --- a/src/adcp/types/domains/core/signal_ref.py +++ b/src/adcp/types/domains/core/signal_ref.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/signal_ref.json -# timestamp: 2026-10-02T23:18:44+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, WireAnyUrl +from pydantic import ConfigDict, Field class SignalRef1(AdCPBaseModel): @@ -66,7 +66,7 @@ class SignalRef3(AdCPBaseModel): ), ] = 'signal_source' signal_source_url: Annotated[ - AnyUrl, Field(description='URL of the signal source that issues this source-native signal.') + WireAnyUrl, Field(description='URL of the signal source that issues this source-native signal.') ] signal_id: Annotated[ str, diff --git a/src/adcp/types/domains/core/special.py b/src/adcp/types/domains/core/special.py index 2ec0ed2b6..0f30f7323 100644 --- a/src/adcp/types/domains/core/special.py +++ b/src/adcp/types/domains/core/special.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/special.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..enums import special_category @@ -25,10 +25,10 @@ class Special(AdCPBaseModel): NoExplicitNull, ] = None starts: Annotated[ - AwareDatetime | None, Field(description='When the event starts (ISO 8601)'), NoExplicitNull + WireDatetime | None, Field(description='When the event starts (ISO 8601)'), NoExplicitNull ] = None ends: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the event ends (ISO 8601). Omit for single-day events.'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/start_timing.py b/src/adcp/types/domains/core/start_timing.py index 3992e2387..57414695b 100644 --- a/src/adcp/types/domains/core/start_timing.py +++ b/src/adcp/types/domains/core/start_timing.py @@ -1,16 +1,17 @@ # generated by datamodel-codegen: # filename: core/start_timing.json -# timestamp: 2026-10-02T23:18:44+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from pydantic import AwareDatetime, Field +from adcp.types.base import WireDatetime +from pydantic import Field StartTiming = Annotated[ - Literal['asap'] | AwareDatetime, + Literal['asap'] | WireDatetime, Field( description="Campaign start timing: 'asap' or ISO 8601 date-time", title='Start Timing' ), diff --git a/src/adcp/types/domains/core/store_item.py b/src/adcp/types/domains/core/store_item.py index 2b1999cbe..3b6b6cabf 100644 --- a/src/adcp/types/domains/core/store_item.py +++ b/src/adcp/types/domains/core/store_item.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/store_item.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictFloat from . import catchment from . import ext as ext_1 @@ -87,7 +87,7 @@ class StoreItem(AdCPBaseModel): NoExplicitNull, ] = None url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='Store-specific page URL (e.g., store locator detail page).'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/talent.py b/src/adcp/types/domains/core/talent.py index 41bffc402..f7a728322 100644 --- a/src/adcp/types/domains/core/talent.py +++ b/src/adcp/types/domains/core/talent.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: core/talent.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..enums import talent_role @@ -22,7 +22,7 @@ class Talent(AdCPBaseModel): ] name: Annotated[str, Field(description="Person's name as credited on the collection")] brand_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="URL to this person's brand.json entry. Enables buyer agents to evaluate the talent's brand identity and associations." ), diff --git a/src/adcp/types/domains/core/tasks_get_response.py b/src/adcp/types/domains/core/tasks_get_response.py index 6a0bfee66..c9bb0a70f 100644 --- a/src/adcp/types/domains/core/tasks_get_response.py +++ b/src/adcp/types/domains/core/tasks_get_response.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/tasks_get_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..enums import adcp_protocol, task_status from ..enums import task_type as task_type_1 @@ -77,7 +77,7 @@ class HistoryItem(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - timestamp: Annotated[AwareDatetime, Field(description='When this exchange occurred (ISO 8601)')] + timestamp: Annotated[WireDatetime, Field(description='When this exchange occurred (ISO 8601)')] type: Annotated[ Type, Field(description='Whether this was a request from client or response from server') ] @@ -95,13 +95,13 @@ class TasksGetResponse(AdcpVersionEnvelope, ProtocolEnvelope): ] status: Annotated[task_status.TaskStatus, Field(description='Current task status')] created_at: Annotated[ - AwareDatetime, Field(description='When the task was initially created (ISO 8601)') + WireDatetime, Field(description='When the task was initially created (ISO 8601)') ] updated_at: Annotated[ - AwareDatetime, Field(description='When the task was last updated (ISO 8601)') + WireDatetime, Field(description='When the task was last updated (ISO 8601)') ] completed_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the task completed (ISO 8601, only for completed/failed/canceled tasks)' ), diff --git a/src/adcp/types/domains/core/tasks_list_request.py b/src/adcp/types/domains/core/tasks_list_request.py index 2b2dc2036..295ea24b7 100644 --- a/src/adcp/types/domains/core/tasks_list_request.py +++ b/src/adcp/types/domains/core/tasks_list_request.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/tasks_list_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool from ..enums import adcp_protocol, sort_direction, task_status from ..enums import task_type as task_type_1 @@ -52,22 +52,22 @@ class Filters(AdCPBaseModel): NoExplicitNull, ] = None created_after: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Filter tasks created after this date (ISO 8601)'), NoExplicitNull, ] = None created_before: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Filter tasks created before this date (ISO 8601)'), NoExplicitNull, ] = None updated_after: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Filter tasks last updated after this date (ISO 8601)'), NoExplicitNull, ] = None updated_before: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Filter tasks last updated before this date (ISO 8601)'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/core/tasks_list_response.py b/src/adcp/types/domains/core/tasks_list_response.py index 9bc088955..38bd45b2f 100644 --- a/src/adcp/types/domains/core/tasks_list_response.py +++ b/src/adcp/types/domains/core/tasks_list_response.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/tasks_list_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field, StrictBool from ..enums import task_status from ..enums import task_type as task_type_1 @@ -98,13 +98,13 @@ class Task(AdCPBaseModel): domain: Annotated[Domain, Field(description='AdCP domain this task belongs to')] status: Annotated[task_status.TaskStatus, Field(description='Current task status')] created_at: Annotated[ - AwareDatetime, Field(description='When the task was initially created (ISO 8601)') + WireDatetime, Field(description='When the task was initially created (ISO 8601)') ] updated_at: Annotated[ - AwareDatetime, Field(description='When the task was last updated (ISO 8601)') + WireDatetime, Field(description='When the task was last updated (ISO 8601)') ] completed_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the task completed (ISO 8601, only for completed/failed/canceled tasks)' ), diff --git a/src/adcp/types/domains/core/transformer.py b/src/adcp/types/domains/core/transformer.py index c694305ec..9758c9f7d 100644 --- a/src/adcp/types/domains/core/transformer.py +++ b/src/adcp/types/domains/core/transformer.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: core/transformer.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, ConfigDict, Field, StrictBool, model_validator +from pydantic import ConfigDict, Field, StrictBool, model_validator from ..enums import channels from ..formats.canonical import ( @@ -41,7 +41,7 @@ class BrandAgent(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - url: Annotated[AnyUrl, Field(description='MCP endpoint URL of the brand agent.')] + url: Annotated[WireAnyUrl, Field(description='MCP endpoint URL of the brand agent.')] id: Annotated[str, Field(description='Agent identifier.')] @@ -163,7 +163,7 @@ class InputFormat1(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -276,7 +276,7 @@ class InputFormat2(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -389,7 +389,7 @@ class InputFormat3(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -502,7 +502,7 @@ class InputFormat4(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -615,7 +615,7 @@ class InputFormat5(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -728,7 +728,7 @@ class InputFormat6(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -841,7 +841,7 @@ class InputFormat7(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -954,7 +954,7 @@ class InputFormat8(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1067,7 +1067,7 @@ class InputFormat9(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1180,7 +1180,7 @@ class InputFormat10(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1293,7 +1293,7 @@ class InputFormat11(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1406,7 +1406,7 @@ class InputFormat12(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1519,7 +1519,7 @@ class InputFormat13(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1632,7 +1632,7 @@ class InputFormat14(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1745,7 +1745,7 @@ class InputFormat15(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), @@ -1858,7 +1858,7 @@ class InputFormat16(AdCPBaseModel): NoExplicitNull, ] = None sample_render_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional public HTTPS page where a human can inspect a sample render of this declaration using assets chosen by the party publishing the enclosing declaration. Consumers MUST identify that source correctly: publisher or community mirror for `adagents.json` `formats[]`, seller for product or inline-placement declarations, and creative agent for `creative.supported_formats`. Informational only: this is not a renderer endpoint, buyer-asset preview, validation result, creative approval, proof of publisher acceptance, or guarantee of live delivery. Declaring parties SHOULD keep the URL stable while the declaration is active.' ), diff --git a/src/adcp/types/domains/core/vehicle_item.py b/src/adcp/types/domains/core/vehicle_item.py index 546ddbf55..6930237c5 100644 --- a/src/adcp/types/domains/core/vehicle_item.py +++ b/src/adcp/types/domains/core/vehicle_item.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/vehicle_item.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field, StrictFloat from . import ext as ext_1 from . import offering_asset_group @@ -119,10 +119,10 @@ class VehicleItem(AdCPBaseModel): Location | None, Field(description='Dealer or vehicle location.'), NoExplicitNull ] = None image_url: Annotated[ - AnyUrl | None, Field(description='Primary vehicle image URL.'), NoExplicitNull + WireAnyUrl | None, Field(description='Primary vehicle image URL.'), NoExplicitNull ] = None url: Annotated[ - AnyUrl | None, Field(description='Vehicle listing page URL.'), NoExplicitNull + WireAnyUrl | None, Field(description='Vehicle listing page URL.'), NoExplicitNull ] = None tags: Annotated[ list[str] | None, diff --git a/src/adcp/types/domains/core/verification_token_claims.py b/src/adcp/types/domains/core/verification_token_claims.py index 71a1ceb59..ec36bf51e 100644 --- a/src/adcp/types/domains/core/verification_token_claims.py +++ b/src/adcp/types/domains/core/verification_token_claims.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/verification_token_claims.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field class Role(StrEnum): @@ -35,12 +35,12 @@ class AgenticadvertisingOrgVerificationTokenClaims(AdCPBaseModel): extra='allow', ) iss: Literal['https://aao.org'] = 'https://aao.org' - sub: AnyUrl + sub: WireAnyUrl aud: Literal['aao-verification'] = 'aao-verification' jti: Annotated[str, Field(min_length=1)] iat: Annotated[SchemaInt, Field(ge=0)] exp: Annotated[SchemaInt, Field(ge=0)] - agent_url: AnyUrl + agent_url: WireAnyUrl role: Role verified_specialisms: Annotated[list[str], Field(min_length=1)] verification_modes: Annotated[list[VerificationTokenMode], Field(min_length=1)] @@ -52,7 +52,7 @@ class AgenticadvertisingOrgVerificationTokenClaims(AdCPBaseModel): NoExplicitNull, ] = None first_failing_spec_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Start of the current Strict Spec failure episode. Omitted when no Strict Spec failure clock is active. The registry remains authoritative for real-time status.' ), diff --git a/src/adcp/types/domains/core/webhook_activity_record.py b/src/adcp/types/domains/core/webhook_activity_record.py index 499a48330..95856cbe9 100644 --- a/src/adcp/types/domains/core/webhook_activity_record.py +++ b/src/adcp/types/domains/core/webhook_activity_record.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/webhook_activity_record.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field from ..enums import notification_type as notification_type_1 from . import ext as ext_1 @@ -50,13 +50,13 @@ class WebhookActivityRecord(AdCPBaseModel): NoExplicitNull, ] = None fired_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the seller initiated the HTTP request for this attempt.' ), ] completed_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="ISO 8601 timestamp when the seller observed the response (or terminal timeout / connection error — for `timeout` and `connection_error` outcomes, `completed_at` is set to the moment the seller declared the attempt terminal). Explicitly `null` when the attempt is still in flight or queued for retry (status `pending`); MUST be set as `null` rather than omitted so callers can distinguish 'still in flight' from 'field missing'." ), @@ -89,7 +89,7 @@ class WebhookActivityRecord(AdCPBaseModel): ), ] url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Target URL for this fire. Query string and fragment MUST be stripped before surfacing — buyers commonly stash bearer tokens in the query string and sellers MUST NOT echo those back through this debug surface. Sellers SHOULD additionally redact path segments matching obvious secret patterns (e.g., a path segment that is high-entropy random material or matches a UUID / token format). Buyers matching this against their own configured URL should compare by origin + path; query strings will not match and that mismatch is expected.' ), diff --git a/src/adcp/types/domains/core/webhook_challenge.py b/src/adcp/types/domains/core/webhook_challenge.py index 41f1a47e7..f2851097b 100644 --- a/src/adcp/types/domains/core/webhook_challenge.py +++ b/src/adcp/types/domains/core/webhook_challenge.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: core/webhook_challenge.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..enums import notification_type @@ -72,7 +72,7 @@ class WebhookChallenge(AdCPBaseModel): ), ] seller_agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Exact seller agent URL whose RFC 9421 webhook profile key signs this challenge and that will send subsequent webhooks.' ), diff --git a/src/adcp/types/domains/core/wholesale_feed_event.py b/src/adcp/types/domains/core/wholesale_feed_event.py index 16e2a5c1c..0a2703adb 100644 --- a/src/adcp/types/domains/core/wholesale_feed_event.py +++ b/src/adcp/types/domains/core/wholesale_feed_event.py @@ -1,16 +1,15 @@ # generated by datamodel-codegen: # filename: core/wholesale_feed_event.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from collections.abc import Sequence from typing import Annotated, Literal -from uuid import UUID -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime, WireUuid +from pydantic import ConfigDict, Field, StrictFloat from ..enums import signal_catalog_type, signal_value_type from . import canonical_pricing_option @@ -122,14 +121,14 @@ class Payload(AdCPBaseModel): NoExplicitNull, ] = None effective_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the price change takes effect. A value in the future is a pre-announcement: consumers MAY warm caches but MUST NOT bind decisions until the effective time has passed. See specs/wholesale-feed-webhooks.md §`effective_at` and pre-announce.' ), NoExplicitNull, ] = None retracts_event_id: Annotated[ - UUID | None, + WireUuid | None, Field( description="Optional. When this event retracts a prior pre-announced *.priced (per the §`effective_at` and pre-announce rules), set this to the event_id of the announcement being retracted. Lets consumers detect retraction deterministically by event_id lookup rather than maintaining per-entity 'latest-event' state. When present, the retraction MUST carry effective_at <= the retracted event's effective_at." ), @@ -140,7 +139,7 @@ class Payload(AdCPBaseModel): class WholesaleFeedEvent3(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier for this wholesale feed change. MUST equal the enclosing webhook notification_id. UUID v7 is RECOMMENDED so receivers can detect obvious out-of-order delivery; missed or distrusted pushes are repaired through list_products / get_signals.' ), @@ -162,7 +161,7 @@ class WholesaleFeedEvent3(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the agent emitted the event. Advisory — consumers MUST order by event_id (UUID v7), not by created_at, to avoid clock-skew artifacts.' ), @@ -186,7 +185,7 @@ class Payload20(AdCPBaseModel): class WholesaleFeedEvent4(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier for this wholesale feed change. MUST equal the enclosing webhook notification_id. UUID v7 is RECOMMENDED so receivers can detect obvious out-of-order delivery; missed or distrusted pushes are repaired through list_products / get_signals.' ), @@ -208,7 +207,7 @@ class WholesaleFeedEvent4(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the agent emitted the event. Advisory — consumers MUST order by event_id (UUID v7), not by created_at, to avoid clock-skew artifacts.' ), @@ -233,14 +232,14 @@ class Payload23(AdCPBaseModel): ] previous_pricing_option_ids: Annotated[list[str] | None, NoExplicitNull] = None effective_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the price change takes effect. A value in the future is a pre-announcement: consumers MAY warm caches but MUST NOT bind decisions until the effective time has passed. See specs/wholesale-feed-webhooks.md §`effective_at` and pre-announce.' ), NoExplicitNull, ] = None retracts_event_id: Annotated[ - UUID | None, + WireUuid | None, Field( description='Optional. When this event retracts a prior pre-announced *.priced, set this to the event_id of the announcement being retracted. See product.priced for the full retraction contract.' ), @@ -251,7 +250,7 @@ class Payload23(AdCPBaseModel): class WholesaleFeedEvent7(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier for this wholesale feed change. MUST equal the enclosing webhook notification_id. UUID v7 is RECOMMENDED so receivers can detect obvious out-of-order delivery; missed or distrusted pushes are repaired through list_products / get_signals.' ), @@ -273,7 +272,7 @@ class WholesaleFeedEvent7(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the agent emitted the event. Advisory — consumers MUST order by event_id (UUID v7), not by created_at, to avoid clock-skew artifacts.' ), @@ -298,7 +297,7 @@ class Payload24(AdCPBaseModel): class WholesaleFeedEvent8(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier for this wholesale feed change. MUST equal the enclosing webhook notification_id. UUID v7 is RECOMMENDED so receivers can detect obvious out-of-order delivery; missed or distrusted pushes are repaired through list_products / get_signals.' ), @@ -320,7 +319,7 @@ class WholesaleFeedEvent8(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the agent emitted the event. Advisory — consumers MUST order by event_id (UUID v7), not by created_at, to avoid clock-skew artifacts.' ), @@ -364,7 +363,7 @@ class Payload25(AdCPBaseModel): class WholesaleFeedEvent9(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier for this wholesale feed change. MUST equal the enclosing webhook notification_id. UUID v7 is RECOMMENDED so receivers can detect obvious out-of-order delivery; missed or distrusted pushes are repaired through list_products / get_signals.' ), @@ -386,7 +385,7 @@ class WholesaleFeedEvent9(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the agent emitted the event. Advisory — consumers MUST order by event_id (UUID v7), not by created_at, to avoid clock-skew artifacts.' ), @@ -469,7 +468,7 @@ class Payload21(AdCPBaseModel): class WholesaleFeedEvent5(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier for this wholesale feed change. MUST equal the enclosing webhook notification_id. UUID v7 is RECOMMENDED so receivers can detect obvious out-of-order delivery; missed or distrusted pushes are repaired through list_products / get_signals.' ), @@ -491,7 +490,7 @@ class WholesaleFeedEvent5(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the agent emitted the event. Advisory — consumers MUST order by event_id (UUID v7), not by created_at, to avoid clock-skew artifacts.' ), @@ -528,7 +527,7 @@ class Payload22(AdCPBaseModel): class WholesaleFeedEvent6(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier for this wholesale feed change. MUST equal the enclosing webhook notification_id. UUID v7 is RECOMMENDED so receivers can detect obvious out-of-order delivery; missed or distrusted pushes are repaired through list_products / get_signals.' ), @@ -550,7 +549,7 @@ class WholesaleFeedEvent6(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the agent emitted the event. Advisory — consumers MUST order by event_id (UUID v7), not by created_at, to avoid clock-skew artifacts.' ), @@ -592,7 +591,7 @@ class Payload17(AdCPBaseModel): class WholesaleFeedEvent1(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier for this wholesale feed change. MUST equal the enclosing webhook notification_id. UUID v7 is RECOMMENDED so receivers can detect obvious out-of-order delivery; missed or distrusted pushes are repaired through list_products / get_signals.' ), @@ -614,7 +613,7 @@ class WholesaleFeedEvent1(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the agent emitted the event. Advisory — consumers MUST order by event_id (UUID v7), not by created_at, to avoid clock-skew artifacts.' ), @@ -654,7 +653,7 @@ class Payload18(AdCPBaseModel): class WholesaleFeedEvent2(AdCPBaseModel): event_id: Annotated[ - UUID, + WireUuid, Field( description='Stable logical event identifier for this wholesale feed change. MUST equal the enclosing webhook notification_id. UUID v7 is RECOMMENDED so receivers can detect obvious out-of-order delivery; missed or distrusted pushes are repaired through list_products / get_signals.' ), @@ -676,7 +675,7 @@ class WholesaleFeedEvent2(AdCPBaseModel): ), ] created_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the agent emitted the event. Advisory — consumers MUST order by event_id (UUID v7), not by created_at, to avoid clock-skew artifacts.' ), diff --git a/src/adcp/types/domains/core/wholesale_feed_webhook.py b/src/adcp/types/domains/core/wholesale_feed_webhook.py index eb768c8c7..d1796abba 100644 --- a/src/adcp/types/domains/core/wholesale_feed_webhook.py +++ b/src/adcp/types/domains/core/wholesale_feed_webhook.py @@ -1,15 +1,14 @@ # generated by datamodel-codegen: # filename: core/wholesale_feed_webhook.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from uuid import UUID -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime, WireUuid +from pydantic import ConfigDict, Field from . import ext as ext_1 from . import wholesale_feed_event @@ -51,7 +50,7 @@ class WholesaleFeedWebhook(AdCPBaseModel): ), ] notification_id: Annotated[ - UUID, + WireUuid, Field( description='Stable identifier for this logical wholesale feed event. MUST equal event.event_id. Re-emissions of the same logical event reuse this value under a new idempotency_key.' ), @@ -63,7 +62,7 @@ class WholesaleFeedWebhook(AdCPBaseModel): ), ] fired_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the seller initiated this webhook fire. Distinct from event.created_at, which is when the seller observed or recorded the feed change.' ), diff --git a/src/adcp/types/domains/creative/audit_observation.py b/src/adcp/types/domains/creative/audit_observation.py index b67ba72bb..77b7e691c 100644 --- a/src/adcp/types/domains/creative/audit_observation.py +++ b/src/adcp/types/domains/creative/audit_observation.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: creative/audit_observation.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import ext as ext_1 @@ -37,7 +37,7 @@ class Details(AdCPBaseModel): extra='forbid', ) agent_url: Annotated[ - AnyUrl, Field(description='Governance agent URL that produced the observation.') + WireAnyUrl, Field(description='Governance agent URL that produced the observation.') ] feature_id: Annotated[ str | None, @@ -60,7 +60,7 @@ class Details(AdCPBaseModel): NoExplicitNull, ] = None substituted_for: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Buyer-nominated verifier URL when the seller or orchestrator used a different on-list governance agent.' ), diff --git a/src/adcp/types/domains/creative/creative_assignment_changed_webhook.py b/src/adcp/types/domains/creative/creative_assignment_changed_webhook.py index 947fce518..70d9df215 100644 --- a/src/adcp/types/domains/creative/creative_assignment_changed_webhook.py +++ b/src/adcp/types/domains/creative/creative_assignment_changed_webhook.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: creative/creative_assignment_changed_webhook.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..core import ext as ext_1 @@ -36,7 +36,7 @@ class CreativeAssignmentChangedWebhook(AdCPBaseModel): ), ] notification_type: Literal['creative.assignment_changed'] = 'creative.assignment_changed' - fired_at: AwareDatetime + fired_at: WireDatetime subscriber_id: Annotated[ str, Field(max_length=64, min_length=1, pattern='^[A-Za-z0-9_.:-]{1,64}$') ] @@ -45,5 +45,5 @@ class CreativeAssignmentChangedWebhook(AdCPBaseModel): package_id: str creative_id: str change_kind: ChangeKind - observed_at: AwareDatetime + observed_at: WireDatetime ext: Annotated[ext_1.ExtensionObject | None, NoExplicitNull] = None diff --git a/src/adcp/types/domains/creative/creative_feature_result.py b/src/adcp/types/domains/creative/creative_feature_result.py index f4f63795e..548a55d73 100644 --- a/src/adcp/types/domains/creative/creative_feature_result.py +++ b/src/adcp/types/domains/creative/creative_feature_result.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: creative/creative_feature_result.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import ext as ext_1 @@ -41,10 +41,10 @@ class CreativeFeatureResult(AdCPBaseModel): NoExplicitNull, ] = None measured_at: Annotated[ - AwareDatetime | None, Field(description='When this feature was evaluated'), NoExplicitNull + WireDatetime | None, Field(description='When this feature was evaluated'), NoExplicitNull ] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When this evaluation expires and should be refreshed'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/creative/creative_purged_webhook.py b/src/adcp/types/domains/creative/creative_purged_webhook.py index 979e10e3b..a2771c424 100644 --- a/src/adcp/types/domains/creative/creative_purged_webhook.py +++ b/src/adcp/types/domains/creative/creative_purged_webhook.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: creative/creative_purged_webhook.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..core import ext as ext_1 from ..enums import creative_event_reason_code @@ -50,7 +50,7 @@ class CreativePurgedWebhook(AdCPBaseModel): Literal['creative.purged'], Field(description='Fixed notification type discriminator.') ] = 'creative.purged' fired_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the seller initiated this fire. Sellers MUST fire `creative.purged` immediately after purge — coalescence is not permitted on this notification type.' ), @@ -81,7 +81,7 @@ class CreativePurgedWebhook(AdCPBaseModel): ), ] purged_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the seller observed the destruction. Distinct from `fired_at`.' ), diff --git a/src/adcp/types/domains/creative/creative_status_changed_webhook.py b/src/adcp/types/domains/creative/creative_status_changed_webhook.py index 1ba5c03a9..693579543 100644 --- a/src/adcp/types/domains/creative/creative_status_changed_webhook.py +++ b/src/adcp/types/domains/creative/creative_status_changed_webhook.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: creative/creative_status_changed_webhook.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..core import creative_revision_id from ..core import ext as ext_1 @@ -35,7 +35,7 @@ class Transition(AdCPBaseModel): ] to: Annotated[creative_status.CreativeStatus, Field(description='New status.')] observed_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description="ISO 8601 timestamp when the seller observed the transition. Distinct from `fired_at` — `fired_at` reflects when the webhook was emitted, which may lag `observed_at` by up to the seller's coalescence window. **Ordering with `media-buy.impairment`:** when a creative transition also causes a media-buy impairment (e.g., `approved → suspended` or `approved → rejected` while assignments exist), the `creative.status_changed` and `impairment` fires are not ordered — buyers MUST NOT assume one arrives before the other. Reconcile via the snapshot (`list_creatives` and `get_media_buys`) when the two fires reference the same `creative_id`." ), @@ -76,7 +76,7 @@ class CreativeStatusChangedWebhook(AdCPBaseModel): ), ] = 'creative.status_changed' fired_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description="ISO 8601 timestamp when the seller initiated this fire. Distinct from when the transition was observed (`transition.observed_at`) — fires MAY be coalesced or delayed up to the seller's declared coalescence window for `creative.status_changed`." ), diff --git a/src/adcp/types/domains/creative/get_creative_delivery_response.py b/src/adcp/types/domains/creative/get_creative_delivery_response.py index 25127fa9f..78fb59c5a 100644 --- a/src/adcp/types/domains/creative/get_creative_delivery_response.py +++ b/src/adcp/types/domains/creative/get_creative_delivery_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: creative/get_creative_delivery_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from collections.abc import Sequence from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool from ..core import context as context_1 from ..core import creative_variant, delivery_metrics, error @@ -25,8 +25,8 @@ class ReportingPeriod(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - start: Annotated[AwareDatetime, Field(description='ISO 8601 start timestamp')] - end: Annotated[AwareDatetime, Field(description='ISO 8601 end timestamp')] + start: Annotated[WireDatetime, Field(description='ISO 8601 start timestamp')] + end: Annotated[WireDatetime, Field(description='ISO 8601 end timestamp')] timezone: Annotated[ str | None, Field( diff --git a/src/adcp/types/domains/creative/get_creative_features_terminal_success.py b/src/adcp/types/domains/creative/get_creative_features_terminal_success.py index f742bd712..c8afb4904 100644 --- a/src/adcp/types/domains/creative/get_creative_features_terminal_success.py +++ b/src/adcp/types/domains/creative/get_creative_features_terminal_success.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: creative/get_creative_features_terminal_success.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictFloat from ..core import context as context_1 from ..core import creative_consumption @@ -32,7 +32,7 @@ class GetCreativeFeaturesSuccess(AdCPBaseModel): Field(description='Feature values for the evaluated creative'), ] detail_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="URL to the vendor's full assessment report. The vendor controls what information is disclosed and access control." ), diff --git a/src/adcp/types/domains/creative/list_creative_formats_response.py b/src/adcp/types/domains/creative/list_creative_formats_response.py index 1da0e9eb8..3c7f54a49 100644 --- a/src/adcp/types/domains/creative/list_creative_formats_response.py +++ b/src/adcp/types/domains/creative/list_creative_formats_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: creative/list_creative_formats_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..core import context as context_1 from ..core import error @@ -22,7 +22,7 @@ class CreativeAgent(AdCPBaseModel): agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="Base URL for the creative agent (e.g., 'https://reference.example.com', 'https://dco.example.com')." ), diff --git a/src/adcp/types/domains/creative/list_creatives_response.py b/src/adcp/types/domains/creative/list_creatives_response.py index c3e438ffd..22abb8b0c 100644 --- a/src/adcp/types/domains/creative/list_creatives_response.py +++ b/src/adcp/types/domains/creative/list_creatives_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: creative/list_creatives_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -10,8 +10,8 @@ from collections.abc import Sequence from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, RootModel, StrictBool, StringConstraints, model_validator +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, RootModel, StrictBool, StringConstraints, model_validator from ..core import account as account_1 from ..core import context as context_1 @@ -84,7 +84,7 @@ class Indicator(AdCPBaseModel): ), ] detected_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the seller first detected the current uninterrupted occurrence of this indicator. Keep this value stable while the condition remains present. If the condition clears and is later detected again, use the new detection time. Optional because some upstream platforms expose current assessments without an original detection timestamp.' ), @@ -112,7 +112,7 @@ class Snapshot(AdCPBaseModel): extra='allow', ) as_of: Annotated[ - AwareDatetime, Field(description='When this snapshot was captured by the platform') + WireDatetime, Field(description='When this snapshot was captured by the platform') ] staleness_seconds: Annotated[ SchemaInt, @@ -129,7 +129,7 @@ class Snapshot(AdCPBaseModel): ), ] last_served: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Last time this creative served an impression. Absent when the creative has never served.' ), @@ -146,7 +146,7 @@ class Purge(AdCPBaseModel): Field(description='Always `soft` on tombstones — hard purges do not surface on this read.'), ] = 'soft' at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 timestamp when the creative was destroyed. Matches the `purged_at` field on the corresponding `creative.purged` webhook fire.' ), @@ -237,7 +237,7 @@ class AssignedPackage(IndicatorBearingResourceState): ), NoExplicitNull, ] = None - assigned_date: Annotated[AwareDatetime, Field(description='When this assignment was created')] + assigned_date: Annotated[WireDatetime, Field(description='When this assignment was created')] approval_status: Annotated[ creative_approval_status.CreativeApprovalStatus | None, Field( @@ -261,7 +261,7 @@ class AssignedPackage(IndicatorBearingResourceState): NoExplicitNull, ] = None indicators_as_of: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the seller last completed the evaluation represented by indicators for this relationship. Required whenever indicators is present, including an empty array.' ), @@ -371,8 +371,8 @@ class Creative(AdCPBaseModel): status: Annotated[ creative_status.CreativeStatus, Field(description='Current approval status of the creative') ] - created_date: Annotated[AwareDatetime, Field(description='When the creative was created')] - updated_date: Annotated[AwareDatetime, Field(description='When the creative was last modified')] + created_date: Annotated[WireDatetime, Field(description='When the creative was created')] + updated_date: Annotated[WireDatetime, Field(description='When the creative was last modified')] assets: Annotated[ dict[Annotated[str, StringConstraints(pattern=r'^[a-z0-9_]+$')], asset_union.AssetVariant | Assets] | None, Field( diff --git a/src/adcp/types/domains/creative/preview_creative_response.py b/src/adcp/types/domains/creative/preview_creative_response.py index d5734aea2..c595f84d7 100644 --- a/src/adcp/types/domains/creative/preview_creative_response.py +++ b/src/adcp/types/domains/creative/preview_creative_response.py @@ -8,7 +8,8 @@ from typing import Annotated, Literal, TypeAlias -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StringConstraints +from adcp.types.base import WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StringConstraints from ..core.version_envelope import AdcpVersionEnvelope from . import preview_render as preview_render_1 @@ -52,8 +53,8 @@ class Preview2(AdcpVersionEnvelope): class Response(AdcpVersionEnvelope): model_config = ConfigDict(extra='allow') previews: Annotated[list[Preview2], Field(min_length=1)] - interactive_url: AnyUrl | None = None - expires_at: AwareDatetime | None = None + interactive_url: WireAnyUrl | None = None + expires_at: WireDatetime | None = None class Result(AdcpVersionEnvelope): @@ -76,8 +77,8 @@ class PreviewCreativeResponse1(AdcpResponse, AdcpVersionEnvelope, ProtocolEnvelo response_type: Literal['single'] = 'single' previews: Annotated[list[Preview], Field(min_length=1)] quality_used: creative_quality_1.CreativeQuality | None = None - interactive_url: AnyUrl | None = None - expires_at: AwareDatetime | None = None + interactive_url: WireAnyUrl | None = None + expires_at: WireDatetime | None = None context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None @@ -97,7 +98,7 @@ class PreviewCreativeResponse3(AdcpResponse, AdcpVersionEnvelope, ProtocolEnvelo creative_id: str | None = None previews: Annotated[list[Preview3], Field(min_length=1)] manifest: creative_manifest_1.CreativeManifest | None = None - expires_at: AwareDatetime | None = None + expires_at: WireDatetime | None = None context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None diff --git a/src/adcp/types/domains/creative/preview_render.py b/src/adcp/types/domains/creative/preview_render.py index a3284dfe4..5de96e7e6 100644 --- a/src/adcp/types/domains/creative/preview_render.py +++ b/src/adcp/types/domains/creative/preview_render.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: creative/preview_render.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import preview_renderer_metadata @@ -52,7 +52,7 @@ class PreviewRender1(AdCPBaseModel): Literal['url'], Field(description='Discriminator indicating preview_url is provided') ] = 'url' preview_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Untrusted URL to an HTML page that renders this piece. Consumers MUST load it only in a cross-origin iframe with an empty sandbox token set and a caller-enforced restrictive CSP. Provider embedding metadata is advisory and MUST NOT loosen that policy.' ), @@ -133,7 +133,7 @@ class PreviewRender3(AdCPBaseModel): ), ] = 'both' preview_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Untrusted URL to an HTML page that renders this piece. Consumers MUST load it only in a cross-origin iframe with an empty sandbox token set and a caller-enforced restrictive CSP. Provider embedding metadata is advisory and MUST NOT loosen that policy.' ), diff --git a/src/adcp/types/domains/creative/sync_creatives_response.py b/src/adcp/types/domains/creative/sync_creatives_response.py index 20cdbd188..eb162676a 100644 --- a/src/adcp/types/domains/creative/sync_creatives_response.py +++ b/src/adcp/types/domains/creative/sync_creatives_response.py @@ -8,7 +8,8 @@ from typing import Annotated, Literal, TypeAlias -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StringConstraints +from adcp.types.base import WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StringConstraints from ..core.version_envelope import AdcpVersionEnvelope from ..core import account as account_1 @@ -37,8 +38,8 @@ class Creative(AdcpVersionEnvelope): errors: list[error_1.Error] | None = None warnings: list[str] | None = None macro_resolution_results: list[macro_resolution_result_1.MacroResolutionResult] | None = None - preview_url: AnyUrl | None = None - expires_at: AwareDatetime | None = None + preview_url: WireAnyUrl | None = None + expires_at: WireDatetime | None = None assigned_to: list[str] | None = None assignment_errors: dict[Annotated[str, StringConstraints(pattern='^[a-zA-Z0-9_-]+$')], str] | None = None diff --git a/src/adcp/types/domains/creative/validate_input_request.py b/src/adcp/types/domains/creative/validate_input_request.py index 5373a681c..10e6d10e5 100644 --- a/src/adcp/types/domains/creative/validate_input_request.py +++ b/src/adcp/types/domains/creative/validate_input_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: creative/validate_input_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..core import account_ref, brand_ref, creative_manifest @@ -37,7 +37,7 @@ class Targets2(AdCPBaseModel): class Targets3(AdCPBaseModel): kind: Literal['third_party_format'] = 'third_party_format' id: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='URI-form format identifier referencing a third-party format definition (e.g., `https://creativevendor.example/formats/image_300x250@sha256:...`). Validators fetch the definition (digest-pinned, cached) and validate the manifest against it.' ), diff --git a/src/adcp/types/domains/error_details/account_setup_required.py b/src/adcp/types/domains/error_details/account_setup_required.py index a32396035..9cbd905bf 100644 --- a/src/adcp/types/domains/error_details/account_setup_required.py +++ b/src/adcp/types/domains/error_details/account_setup_required.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: error_details/account_setup_required.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field class AccountSetupRequiredDetails(AdCPBaseModel): @@ -15,7 +15,7 @@ class AccountSetupRequiredDetails(AdCPBaseModel): extra='allow', ) setup_url: Annotated[ - AnyUrl | None, Field(description='URL where account setup can be completed'), NoExplicitNull + WireAnyUrl | None, Field(description='URL where account setup can be completed'), NoExplicitNull ] = None setup_steps: Annotated[ list[str] | None, diff --git a/src/adcp/types/domains/error_details/authorization_required.py b/src/adcp/types/domains/error_details/authorization_required.py index da78aeb09..0801c9651 100644 --- a/src/adcp/types/domains/error_details/authorization_required.py +++ b/src/adcp/types/domains/error_details/authorization_required.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: error_details/authorization_required.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..core import downstream_connection_requirement @@ -31,7 +31,7 @@ class AuthorizationRequiredDetails(AdCPBaseModel): NoExplicitNull, ] = None authorization_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='General recovery URL when there is a single obvious authorization step or when the seller has its own connection-management page.' ), diff --git a/src/adcp/types/domains/error_details/creative_rejected.py b/src/adcp/types/domains/error_details/creative_rejected.py index fe18694f7..a431792c6 100644 --- a/src/adcp/types/domains/error_details/creative_rejected.py +++ b/src/adcp/types/domains/error_details/creative_rejected.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: error_details/creative_rejected.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field class CreativeRejectedDetails(AdCPBaseModel): @@ -18,7 +18,7 @@ class CreativeRejectedDetails(AdCPBaseModel): str | None, Field(description='Identifier for the violated policy'), NoExplicitNull ] = None policy_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL where the full policy can be reviewed'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/error_details/governance_agent_not_accepted.py b/src/adcp/types/domains/error_details/governance_agent_not_accepted.py index f9e072682..6e72e2b12 100644 --- a/src/adcp/types/domains/error_details/governance_agent_not_accepted.py +++ b/src/adcp/types/domains/error_details/governance_agent_not_accepted.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: error_details/governance_agent_not_accepted.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..governance import accepted_governance_agents as accepted_governance_agents_1 @@ -18,7 +18,7 @@ class GovernanceAgentNotAcceptedDetails1(AdCPBaseModel): ) disclosure: Literal['disclosed'] = 'disclosed' attempted_agent_origin: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Parsed HTTPS origin only. The seller MUST strip userinfo, path, query, and fragment and MUST NOT echo the raw candidate URL.' ), diff --git a/src/adcp/types/domains/error_details/policy_violation.py b/src/adcp/types/domains/error_details/policy_violation.py index a512732bc..b815700ae 100644 --- a/src/adcp/types/domains/error_details/policy_violation.py +++ b/src/adcp/types/domains/error_details/policy_violation.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: error_details/policy_violation.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field class Origin(StrEnum): @@ -44,7 +44,7 @@ class PolicyViolationDetails(AdCPBaseModel): NoExplicitNull, ] = None policy_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL where the full policy can be reviewed'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/error_details/stale_response.py b/src/adcp/types/domains/error_details/stale_response.py index b9fcb53f9..fbaa26d02 100644 --- a/src/adcp/types/domains/error_details/stale_response.py +++ b/src/adcp/types/domains/error_details/stale_response.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: error_details/stale_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field class Upstream(AdCPBaseModel): @@ -15,7 +15,7 @@ class Upstream(AdCPBaseModel): extra='allow', ) url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='URL of the unreachable upstream, when the upstream is HTTP-addressable. Sellers MUST omit when the upstream is an internal (non-URL) dependency.' ), diff --git a/src/adcp/types/domains/error_details/vendor_error_codes.py b/src/adcp/types/domains/error_details/vendor_error_codes.py index f6440abee..bacd3181c 100644 --- a/src/adcp/types/domains/error_details/vendor_error_codes.py +++ b/src/adcp/types/domains/error_details/vendor_error_codes.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: error_details/vendor_error_codes.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import Field class Recovery(StrEnum): @@ -25,7 +25,7 @@ class Codes(AdCPBaseModel): class Vendors(AdCPBaseModel): name: Annotated[str, Field(description='Full vendor name')] url: Annotated[ - AnyUrl | None, Field(description='Vendor website or documentation URL'), NoExplicitNull + WireAnyUrl | None, Field(description='Vendor website or documentation URL'), NoExplicitNull ] = None codes: Annotated[ dict[str, Codes], diff --git a/src/adcp/types/domains/extensions/extension_meta.py b/src/adcp/types/domains/extensions/extension_meta.py index 766691f9e..7d6903c2f 100644 --- a/src/adcp/types/domains/extensions/extension_meta.py +++ b/src/adcp/types/domains/extensions/extension_meta.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: extensions/extension_meta.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import Field class AdcpExtensionFileSchema(AdCPBaseModel): @@ -32,7 +32,7 @@ class AdcpExtensionFileSchema(AdCPBaseModel): NoExplicitNull, ] = None docs_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL to documentation for implementors of this extension'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/governance/accepted_governance_agents.py b/src/adcp/types/domains/governance/accepted_governance_agents.py index 07468ee00..adac694cc 100644 --- a/src/adcp/types/domains/governance/accepted_governance_agents.py +++ b/src/adcp/types/domains/governance/accepted_governance_agents.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: governance/accepted_governance_agents.json -# timestamp: 2026-10-04T00:59:59+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Any, Annotated, Literal -from adcp.types.base import AdCPBaseModel, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field, RootModel +from adcp.types.base import AdCPBaseModel, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field, RootModel class AnyOf1(AdCPBaseModel): @@ -17,7 +17,7 @@ class AnyOf1(AdCPBaseModel): ) kind: Literal['agent_url'] = 'agent_url' agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Exact canonical agent endpoint without userinfo, query, or fragment. Redirect targets, DNS aliases, and URLs asserted by the candidate do not satisfy this matcher.' ), @@ -35,7 +35,7 @@ class AnyOf2(AdCPBaseModel): ) kind: Literal['verification'] = 'verification' registry: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Seller-configured trusted verification registry without userinfo, query, or fragment. The candidate cannot supply or override this URL; fetches use the registry-resolution security contract.' ), diff --git a/src/adcp/types/domains/governance/check_governance_request.py b/src/adcp/types/domains/governance/check_governance_request.py index a1a4e98cb..e68cc3f0b 100644 --- a/src/adcp/types/domains/governance/check_governance_request.py +++ b/src/adcp/types/domains/governance/check_governance_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: governance/check_governance_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictFloat, StringConstraints +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictFloat, StringConstraints from ..core import brand_ref, business_entity, canonical_proposal from ..core import context as context_1 @@ -42,8 +42,8 @@ class ReportingPeriod(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - start: AwareDatetime - end: AwareDatetime + start: WireDatetime + end: WireDatetime class Pacing(StrEnum): @@ -120,7 +120,7 @@ class DeliveryMetrics(AdCPBaseModel): NoExplicitNull, ] = None issued_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the seller issued the canonical statement.'), NoExplicitNull, ] = None @@ -219,7 +219,7 @@ class DeliveryMetrics2(AdCPBaseModel): NoExplicitNull, ] = None issued_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the seller issued the canonical statement.'), NoExplicitNull, ] = None @@ -291,7 +291,7 @@ class Subject(AdCPBaseModel): type: Literal['agent'] = 'agent' resource_type: Literal['https://adcontextprotocol.org/claims/subjects/signal'] = 'https://adcontextprotocol.org/claims/subjects/signal' agent_url: Annotated[ - AnyUrl, Field(description='Canonical HTTPS endpoint of the agent the claim concerns.') + WireAnyUrl, Field(description='Canonical HTTPS endpoint of the agent the claim concerns.') ] ext: Annotated[ext_1.ExtensionObject | None, NoExplicitNull] = None @@ -308,7 +308,7 @@ class Subject6(AdCPBaseModel): ), ] = 'https://adcontextprotocol.org/claims/subjects/signal' namespace: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Absolute URI identifying the namespace in which id is unique. This may be an AdCP agent endpoint, a catalog origin, or a domain-specific namespace URI.' ), @@ -386,7 +386,7 @@ class CheckGovernanceRequest2(AdcpVersionEnvelope): NoExplicitNull, ] = None caller: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Claimed URL of the agent making the request. The transport credential MUST resolve to an agent URL; the governance agent requires an exact match and uses only that resolved URL for authorization, audit, and signed context issuance. On intent checks the authenticated buyer must be the plan owner or hold an active delegation, while approved_sellers is evaluated against the target service that becomes the token audience. On execution checks the authenticated caller MUST equal that preserved audience. An unresolved body assertion never grants plan access or authorization.' ), @@ -399,7 +399,7 @@ class CheckGovernanceRequest2(AdcpVersionEnvelope): NoExplicitNull, ] = purchase_type_1.PurchaseType.media_buy target_agent: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Exact agent URL of the downstream service that will receive the governed task. Required on intent checks and copied byte-for-byte into the signed governance_context aud claim. This routing and authorization field is not part of payload: payload remains exactly the downstream task arguments. A consultation re-check MUST use the same target_agent.' ), @@ -543,7 +543,7 @@ class CheckGovernanceRequest3(CheckGovernanceRequest1, CheckGovernanceRequest2): NoExplicitNull, ] = None caller: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Claimed URL of the agent making the request. The transport credential MUST resolve to an agent URL; the governance agent requires an exact match and uses only that resolved URL for authorization, audit, and signed context issuance. On intent checks the authenticated buyer must be the plan owner or hold an active delegation, while approved_sellers is evaluated against the target service that becomes the token audience. On execution checks the authenticated caller MUST equal that preserved audience. An unresolved body assertion never grants plan access or authorization.' ), @@ -556,7 +556,7 @@ class CheckGovernanceRequest3(CheckGovernanceRequest1, CheckGovernanceRequest2): NoExplicitNull, ] = purchase_type_1.PurchaseType.media_buy target_agent: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Exact agent URL of the downstream service that will receive the governed task. Required on intent checks and copied byte-for-byte into the signed governance_context aud claim. This routing and authorization field is not part of payload: payload remains exactly the downstream task arguments. A consultation re-check MUST use the same target_agent.' ), diff --git a/src/adcp/types/domains/governance/check_governance_response.py b/src/adcp/types/domains/governance/check_governance_response.py index 81cc41cd2..02fdeb409 100644 --- a/src/adcp/types/domains/governance/check_governance_response.py +++ b/src/adcp/types/domains/governance/check_governance_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: governance/check_governance_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat, model_validator +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictFloat, model_validator from ..core import context as context_1 from ..core import ext as ext_1 @@ -47,8 +47,8 @@ class ReportingPeriod(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - start: AwareDatetime - end: AwareDatetime + start: WireDatetime + end: WireDatetime class CanonicalPayload(AdCPBaseModel): @@ -66,7 +66,7 @@ class DeliveryStatement(AdCPBaseModel): statement_id: str statement_digest: Annotated[str, Field(pattern='^sha256:[a-f0-9]{64}$')] sequence: Annotated[SchemaInt, Field(ge=1)] - issued_at: AwareDatetime + issued_at: WireDatetime seller_reference: str reporting_period: ReportingPeriod cumulative_spend: Annotated[StrictFloat, Field(ge=0.0)] @@ -239,14 +239,14 @@ def _status_to_verdict(cls, data: Any) -> Any: NoExplicitNull, ] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="When this approval expires. In the cross-role shape, present only when verdict is 'approved'. Deprecated legacy conditions responses may also carry it for 3.x compatibility. The caller must act before this time or re-call check_governance. A lapsed approval is no approval." ), NoExplicitNull, ] = None next_check: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the seller should next call check_governance with delivery metrics. Present when the governance agent expects ongoing delivery reporting.' ), diff --git a/src/adcp/types/domains/governance/get_plan_audit_logs_response.py b/src/adcp/types/domains/governance/get_plan_audit_logs_response.py index ed50527f8..f340ae8b6 100644 --- a/src/adcp/types/domains/governance/get_plan_audit_logs_response.py +++ b/src/adcp/types/domains/governance/get_plan_audit_logs_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: governance/get_plan_audit_logs_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import attestation_evaluation, attestation_reference from ..core import context as context_1 @@ -147,7 +147,7 @@ class Escalation(AdCPBaseModel): NoExplicitNull, ] = None resolved_at: Annotated[ - AwareDatetime | None, Field(description='ISO 8601 resolution timestamp.'), NoExplicitNull + WireDatetime | None, Field(description='ISO 8601 resolution timestamp.'), NoExplicitNull ] = None @@ -342,8 +342,8 @@ class ReportingPeriod(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - start: AwareDatetime - end: AwareDatetime + start: WireDatetime + end: WireDatetime class CanonicalPayload(AdCPBaseModel): @@ -361,7 +361,7 @@ class DeliveryStatement(AdCPBaseModel): statement_id: str statement_digest: Annotated[str, Field(pattern='^sha256:[a-f0-9]{64}$')] sequence: Annotated[SchemaInt, Field(ge=1)] - issued_at: AwareDatetime + issued_at: WireDatetime seller_reference: str cumulative_spend: Annotated[StrictFloat, Field(ge=0.0)] currency: Annotated[str, Field(pattern='^[A-Z]{3}$')] @@ -414,7 +414,7 @@ class Delivery(AdCPBaseModel): ), NoExplicitNull, ] = None - observed_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + observed_at: Annotated[WireDatetime | None, NoExplicitNull] = None seller_statement_id: Annotated[ str | None, Field( @@ -484,7 +484,7 @@ class Evidence(AdCPBaseModel): evidence_id: str evidence_type: EvidenceType digest: Annotated[str, Field(pattern='^sha256:[a-f0-9]{64}$')] - issued_at: AwareDatetime + issued_at: WireDatetime class DeliveryReportingPeriod(ReportingPeriod): @@ -583,7 +583,7 @@ class Entry(AdCPBaseModel): ) id: Annotated[str, Field(description='Entry identifier.')] type: Annotated[Type, Field(description='Entry type.')] - timestamp: Annotated[AwareDatetime, Field(description='ISO 8601 timestamp.')] + timestamp: Annotated[WireDatetime, Field(description='ISO 8601 timestamp.')] plan_id: Annotated[ str | None, Field( @@ -592,7 +592,7 @@ class Entry(AdCPBaseModel): NoExplicitNull, ] = None caller: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='URL of the agent that made the request. Resolved from the credentials used on the governance callback.' ), @@ -701,7 +701,7 @@ class Entry(AdCPBaseModel): str | None, Field(description='Seller-supplied adjustment reason.'), NoExplicitNull ] = None effective_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description="When the seller's adjustment became effective."), NoExplicitNull, ] = None @@ -785,8 +785,8 @@ class Entry(AdCPBaseModel): adjustment_state: Annotated[AdjustmentState | None, NoExplicitNull] = None verified_amount: Annotated[StrictFloat | None, Field(ge=0.0), NoExplicitNull] = None evidence: Annotated[Evidence | None, NoExplicitNull] = None - reviewed_by: Annotated[AnyUrl | None, NoExplicitNull] = None - reviewed_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + reviewed_by: Annotated[WireAnyUrl | None, NoExplicitNull] = None + reviewed_at: Annotated[WireDatetime | None, NoExplicitNull] = None review_reason: Annotated[str | None, NoExplicitNull] = None diff --git a/src/adcp/types/domains/governance/policy_entry.py b/src/adcp/types/domains/governance/policy_entry.py index 5ef9b3597..f08102075 100644 --- a/src/adcp/types/domains/governance/policy_entry.py +++ b/src/adcp/types/domains/governance/policy_entry.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: governance/policy_entry.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool from ..core import ext as ext_1 from ..enums import channels as channels_1 @@ -169,7 +169,7 @@ class PolicyEntry(AdCPBaseModel): NoExplicitNull, ] = None source_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='Link to the source regulation, standard, or legislation.'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/governance/report_plan_adjustment_request.py b/src/adcp/types/domains/governance/report_plan_adjustment_request.py index d77016249..e7167849d 100644 --- a/src/adcp/types/domains/governance/report_plan_adjustment_request.py +++ b/src/adcp/types/domains/governance/report_plan_adjustment_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: governance/report_plan_adjustment_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictFloat from ..core import context as context_1 from ..core import ext as ext_1 @@ -75,7 +75,7 @@ class Evidence(AdCPBaseModel): ] evidence_type: EvidenceType digest: Annotated[str, Field(pattern='^sha256:[a-f0-9]{64}$')] - issued_at: AwareDatetime + issued_at: WireDatetime class ReportPlanAdjustmentRequest(AdcpRequest, AdcpVersionEnvelope): @@ -149,7 +149,7 @@ class ReportPlanAdjustmentRequest(AdcpRequest, AdcpVersionEnvelope): NoExplicitNull, ] = None effective_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description="When the seller's adjustment became effective."), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/governance/report_plan_outcome_request.py b/src/adcp/types/domains/governance/report_plan_outcome_request.py index 053d3bf36..85cee5374 100644 --- a/src/adcp/types/domains/governance/report_plan_outcome_request.py +++ b/src/adcp/types/domains/governance/report_plan_outcome_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: governance/report_plan_outcome_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import context as context_1 from ..core import ext as ext_1 @@ -37,8 +37,8 @@ class ReportingPeriod(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - start: AwareDatetime - end: AwareDatetime + start: WireDatetime + end: WireDatetime class Delivery(AdCPBaseModel): @@ -59,7 +59,7 @@ class Delivery(AdCPBaseModel): description='Whether the buyer is forwarding the seller statement it received or reporting an independently measured observation. A forwarded seller statement is not independent evidence.' ), ] - observed_at: AwareDatetime + observed_at: WireDatetime seller_statement_id: Annotated[ str | None, Field( @@ -126,7 +126,7 @@ class SellerResponse(AdCPBaseModel): NoExplicitNull, ] = None creative_deadline: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='ISO 8601 deadline for creative submission.'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/governance/sync_plans_request.py b/src/adcp/types/domains/governance/sync_plans_request.py index bbc9df3e5..a25cb3607 100644 --- a/src/adcp/types/domains/governance/sync_plans_request.py +++ b/src/adcp/types/domains/governance/sync_plans_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: governance/sync_plans_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import brand_ref from ..core import context as context_1 @@ -161,8 +161,8 @@ class Flight(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - start: Annotated[AwareDatetime, Field(description='Flight start (ISO 8601).')] - end: Annotated[AwareDatetime, Field(description='Flight end (ISO 8601).')] + start: Annotated[WireDatetime, Field(description='Flight start (ISO 8601).')] + end: Annotated[WireDatetime, Field(description='Flight end (ISO 8601).')] class BudgetLimit(AdCPBaseModel): @@ -177,7 +177,7 @@ class Delegation(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - agent_url: Annotated[AnyUrl, Field(description='URL of the delegated agent.')] + agent_url: Annotated[WireAnyUrl, Field(description='URL of the delegated agent.')] authority: Annotated[ delegation_authority.DelegationAuthority, Field(description='Authority level granted to this agent.'), @@ -197,7 +197,7 @@ class Delegation(AdCPBaseModel): NoExplicitNull, ] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When this delegation expires. After expiration, the governance agent denies actions from this agent.' ), @@ -347,7 +347,7 @@ class Plan(AdCPBaseModel): NoExplicitNull, ] = None approved_sellers: Annotated[ - list[AnyUrl] | None, + list[WireAnyUrl] | None, Field(description='List of approved seller agent URLs. null means any seller.'), ] = None delegations: Annotated[ diff --git a/src/adcp/types/domains/manifest_schema.py b/src/adcp/types/domains/manifest_schema.py index e2148cfd2..b1bf5dd03 100644 --- a/src/adcp/types/domains/manifest_schema.py +++ b/src/adcp/types/domains/manifest_schema.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: manifest.schema.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool +from pydantic import ConfigDict, Field, StrictBool class Protocol(StrEnum): @@ -294,7 +294,7 @@ class AdcpManifest(AdCPBaseModel): ), ] generated_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO-8601 timestamp the manifest was generated. SDKs MAY use this for cache invalidation.' ), diff --git a/src/adcp/types/domains/media_buy/accept_proposal_request.py b/src/adcp/types/domains/media_buy/accept_proposal_request.py index 2096c59c6..ee2e1a16c 100644 --- a/src/adcp/types/domains/media_buy/accept_proposal_request.py +++ b/src/adcp/types/domains/media_buy/accept_proposal_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/accept_proposal_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictFloat from ..core import canonical_account_ref from ..core import context as context_1 @@ -34,7 +34,7 @@ class IoAcceptance(AdCPBaseModel): extra='forbid', ) io_id: Annotated[str, Field(min_length=1)] - accepted_at: AwareDatetime + accepted_at: WireDatetime signatory: Annotated[str, Field(max_length=250, min_length=1)] signature_id: Annotated[str | None, Field(min_length=1), NoExplicitNull] = None diff --git a/src/adcp/types/domains/media_buy/accept_proposal_response.py b/src/adcp/types/domains/media_buy/accept_proposal_response.py index bdee58017..a69ab8e45 100644 --- a/src/adcp/types/domains/media_buy/accept_proposal_response.py +++ b/src/adcp/types/domains/media_buy/accept_proposal_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/accept_proposal_response.json -# timestamp: 2026-10-21T11:25:44+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..core import canonical_media_buy_action from ..core import context as context_1 @@ -123,7 +123,7 @@ class AcceptProposalResponse1(AdCPBaseModel): ] = None revision: Annotated[SchemaInt, Field(ge=1)] media_buy_status: Annotated[media_buy_status_1.MediaBuyStatus | None, NoExplicitNull] = None - confirmed_at: AwareDatetime | None = None + confirmed_at: WireDatetime | None = None accepted_proposal: AcceptedProposal purchase_bindings: Annotated[ list[PurchaseBinding], diff --git a/src/adcp/types/domains/media_buy/acceptance_policy_catalog.py b/src/adcp/types/domains/media_buy/acceptance_policy_catalog.py index 44f2aa74a..e503d3212 100644 --- a/src/adcp/types/domains/media_buy/acceptance_policy_catalog.py +++ b/src/adcp/types/domains/media_buy/acceptance_policy_catalog.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: media_buy/acceptance_policy_catalog.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, model_validator +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, model_validator from ..core import ext as ext_1 from . import acceptance_policy_profile, acceptance_policy_profile_ref @@ -21,7 +21,7 @@ class AcceptancePolicyCatalog(AdCPBaseModel): extra='forbid', ) catalog_version: Annotated[str, Field(min_length=1)] - generated_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + generated_at: Annotated[WireDatetime | None, NoExplicitNull] = None profiles: Annotated[ list[acceptance_policy_profile.AcceptancePolicyProfile] | None, Field(min_length=1), diff --git a/src/adcp/types/domains/media_buy/acceptance_policy_requirement.py b/src/adcp/types/domains/media_buy/acceptance_policy_requirement.py index 7715e6fad..1b3204f40 100644 --- a/src/adcp/types/domains/media_buy/acceptance_policy_requirement.py +++ b/src/adcp/types/domains/media_buy/acceptance_policy_requirement.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: media_buy/acceptance_policy_requirement.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field from ..core import ext as ext_1 from ..enums import restricted_attribute @@ -162,8 +162,8 @@ class AcceptancePolicyRequirement15(AdCPBaseModel): extra='forbid', ) kind: Literal['time_restriction'] = 'time_restriction' - starts_at: Annotated[AwareDatetime | None, NoExplicitNull] = None - ends_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + starts_at: Annotated[WireDatetime | None, NoExplicitNull] = None + ends_at: Annotated[WireDatetime | None, NoExplicitNull] = None description: Annotated[str | None, Field(min_length=1), NoExplicitNull] = None diff --git a/src/adcp/types/domains/media_buy/acceptance_policy_rule.py b/src/adcp/types/domains/media_buy/acceptance_policy_rule.py index 7832dfdd8..9c77491aa 100644 --- a/src/adcp/types/domains/media_buy/acceptance_policy_rule.py +++ b/src/adcp/types/domains/media_buy/acceptance_policy_rule.py @@ -1,15 +1,15 @@ # generated by datamodel-codegen: # filename: media_buy/acceptance_policy_rule.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field from ..core import ext as ext_1 from . import acceptance_policy_requirement @@ -123,6 +123,6 @@ class AcceptancePolicyRule(AdCPBaseModel): ), NoExplicitNull, ] = None - effective_at: Annotated[AwareDatetime | None, NoExplicitNull] = None - expires_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + effective_at: Annotated[WireDatetime | None, NoExplicitNull] = None + expires_at: Annotated[WireDatetime | None, NoExplicitNull] = None ext: Annotated[ext_1.ExtensionObject | None, NoExplicitNull] = None diff --git a/src/adcp/types/domains/media_buy/build_creative_response.py b/src/adcp/types/domains/media_buy/build_creative_response.py index e7f676575..2e21be8fe 100644 --- a/src/adcp/types/domains/media_buy/build_creative_response.py +++ b/src/adcp/types/domains/media_buy/build_creative_response.py @@ -8,7 +8,8 @@ from typing import Annotated, Any, Literal, TypeAlias -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StringConstraints +from adcp.types.base import WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StringConstraints from ..core.version_envelope import AdcpVersionEnvelope from ..core import context as context_1 @@ -42,8 +43,8 @@ class Preview2(AdcpVersionEnvelope): class Preview(AdcpVersionEnvelope): model_config = ConfigDict(extra='allow') previews: Annotated[list[Preview2], Field(min_length=1)] - interactive_url: AnyUrl | None = None - expires_at: AwareDatetime + interactive_url: WireAnyUrl | None = None + expires_at: WireDatetime class Input2(AdcpVersionEnvelope): @@ -65,8 +66,8 @@ class Preview4(AdcpVersionEnvelope): class Preview3(AdcpVersionEnvelope): model_config = ConfigDict(extra='allow') previews: Annotated[list[Preview4], Field(min_length=1)] - interactive_url: AnyUrl | None = None - expires_at: AwareDatetime + interactive_url: WireAnyUrl | None = None + expires_at: WireDatetime class CatalogItemRef(AdcpVersionEnvelope): @@ -140,7 +141,7 @@ class BuildCreativeResponse1(AdcpResponse, AdcpVersionEnvelope, ProtocolEnvelope build_variant_id: str | None = None recipe_hash: str | None = None sandbox: bool | None = None - expires_at: AwareDatetime | None = None + expires_at: WireDatetime | None = None preview: Preview | None = None preview_error: error_1.Error | None = None pricing_option_id: str | None = None @@ -162,7 +163,7 @@ class BuildCreativeResponse3(AdcpResponse, AdcpVersionEnvelope, ProtocolEnvelope model_config = ConfigDict(extra='allow') creative_manifests: Annotated[list[creative_manifest_1.CreativeManifest], Field(min_length=1)] sandbox: bool | None = None - expires_at: AwareDatetime | None = None + expires_at: WireDatetime | None = None preview: Preview3 | None = None preview_error: error_1.Error | None = None pricing_option_id: str | None = None @@ -187,7 +188,7 @@ class BuildCreativeResponse4(AdcpResponse, AdcpVersionEnvelope, ProtocolEnvelope budget_status: Literal['complete', 'capped'] | None = None errors: list[error_1.Error] | None = None sandbox: bool | None = None - expires_at: AwareDatetime | None = None + expires_at: WireDatetime | None = None context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None @@ -196,7 +197,7 @@ class BuildCreativeResponse5(AdcpResponse, AdcpVersionEnvelope, ProtocolEnvelope model_config = ConfigDict(extra='allow') mode: Literal['estimate'] = 'estimate' estimate: Estimate - expires_at: AwareDatetime | None = None + expires_at: WireDatetime | None = None context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None diff --git a/src/adcp/types/domains/media_buy/buy_products_request.py b/src/adcp/types/domains/media_buy/buy_products_request.py index 148df0d5d..a518799c3 100644 --- a/src/adcp/types/domains/media_buy/buy_products_request.py +++ b/src/adcp/types/domains/media_buy/buy_products_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/buy_products_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import ( bidding_policy, @@ -154,7 +154,7 @@ class BuyProductsRequest(AdcpRequest, AdcpVersionEnvelope): canonical_budget_allocation.CanonicalBudgetAllocation | None, NoExplicitNull ] = None start_time: start_timing.StartTiming - end_time: AwareDatetime + end_time: WireDatetime pacing: Annotated[ pacing_1.Pacing | None, Field( diff --git a/src/adcp/types/domains/media_buy/buy_products_response.py b/src/adcp/types/domains/media_buy/buy_products_response.py index 2db9dd14b..a9c88c420 100644 --- a/src/adcp/types/domains/media_buy/buy_products_response.py +++ b/src/adcp/types/domains/media_buy/buy_products_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/buy_products_response.json -# timestamp: 2026-10-21T11:25:44+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..core import canonical_media_buy_action from ..core import context as context_1 @@ -123,7 +123,7 @@ class BuyProductsResponse1(AdCPBaseModel): ] = None revision: Annotated[SchemaInt, Field(ge=1)] media_buy_status: Annotated[media_buy_status_1.MediaBuyStatus | None, NoExplicitNull] = None - confirmed_at: AwareDatetime | None = None + confirmed_at: WireDatetime | None = None accepted_proposal: AcceptedProposal purchase_bindings: Annotated[ list[PurchaseBinding], diff --git a/src/adcp/types/domains/media_buy/change_term_constraints.py b/src/adcp/types/domains/media_buy/change_term_constraints.py index d4f93a7d5..3fc2d120d 100644 --- a/src/adcp/types/domains/media_buy/change_term_constraints.py +++ b/src/adcp/types/domains/media_buy/change_term_constraints.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: media_buy/change_term_constraints.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat, model_validator +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field, StrictFloat, model_validator from ..core import duration @@ -103,12 +103,12 @@ class MediaBuyChangeTermConstraints2(AdCPBaseModel): NoExplicitNull, ] = None earliest_result: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Earliest resulting start or end timestamp accepted for this action.'), NoExplicitNull, ] = None latest_result: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Latest resulting start or end timestamp accepted for this action.'), NoExplicitNull, ] = None @@ -140,8 +140,8 @@ class MediaBuyChangeTermConstraints4(AdCPBaseModel): ), NoExplicitNull, ] = None - earliest_effective_at: Annotated[AwareDatetime | None, NoExplicitNull] = None - latest_effective_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + earliest_effective_at: Annotated[WireDatetime | None, NoExplicitNull] = None + latest_effective_at: Annotated[WireDatetime | None, NoExplicitNull] = None @model_validator(mode='after') diff --git a/src/adcp/types/domains/media_buy/commercial_terms.py b/src/adcp/types/domains/media_buy/commercial_terms.py index e77ed00f9..9201e56a5 100644 --- a/src/adcp/types/domains/media_buy/commercial_terms.py +++ b/src/adcp/types/domains/media_buy/commercial_terms.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: media_buy/commercial_terms.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat, model_validator +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field, StrictFloat, model_validator from ..core import ( bidding_policy, @@ -39,7 +39,7 @@ class CancellationTerms(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - effective_at: AwareDatetime + effective_at: WireDatetime fee: Annotated[Fee | None, NoExplicitNull] = None reason: Annotated[str | None, Field(max_length=500, min_length=1), NoExplicitNull] = None @@ -86,7 +86,7 @@ class CommercialTerms(AdCPBaseModel): ), ] start_time: start_timing.StartTiming - end_time: AwareDatetime + end_time: WireDatetime total_budget: Annotated[TotalBudget | None, NoExplicitNull] = None daily_budget_cap: Annotated[ StrictFloat | None, diff --git a/src/adcp/types/domains/media_buy/control_media_buy_response.py b/src/adcp/types/domains/media_buy/control_media_buy_response.py index 4571fdcc1..9ea2d455a 100644 --- a/src/adcp/types/domains/media_buy/control_media_buy_response.py +++ b/src/adcp/types/domains/media_buy/control_media_buy_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/control_media_buy_response.json -# timestamp: 2026-10-21T11:25:44+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,9 +9,9 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field from ..core import canonical_media_buy_action from ..core import context as context_1 @@ -74,7 +74,7 @@ class ControlMediaBuyResponse1(AdcpResponse, AdCPBaseModel): media_buy_id: Annotated[str, Field(min_length=1)] revision: Annotated[SchemaInt, Field(ge=1)] media_buy_status: Annotated[media_buy_status_1.MediaBuyStatus | None, NoExplicitNull] = None - implementation_date: AwareDatetime | None = None + implementation_date: WireDatetime | None = None affected_package_ids: Annotated[list[AffectedPackageId] | None, NoExplicitNull] = None available_actions: Annotated[ list[canonical_media_buy_action.CanonicalMediaBuyAction] | None, NoExplicitNull diff --git a/src/adcp/types/domains/media_buy/create_media_buy_request.py b/src/adcp/types/domains/media_buy/create_media_buy_request.py index 2d168f6c8..2b169447a 100644 --- a/src/adcp/types/domains/media_buy/create_media_buy_request.py +++ b/src/adcp/types/domains/media_buy/create_media_buy_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/create_media_buy_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -10,8 +10,8 @@ from collections.abc import Sequence from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat, model_validator +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat, model_validator from ..core import account_ref, bidding_policy, brand_ref from ..core import budget_allocation as budget_allocation_1 @@ -54,7 +54,7 @@ class IoAcceptance(AdCPBaseModel): str, Field(description="The io_id from the proposal's insertion_order being accepted") ] accepted_at: Annotated[ - AwareDatetime, Field(description='ISO 8601 timestamp when the IO was accepted') + WireDatetime, Field(description='ISO 8601 timestamp when the IO was accepted') ] signatory: Annotated[ str, @@ -108,7 +108,7 @@ class ArtifactWebhook(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - url: Annotated[AnyUrl, Field(description='Webhook endpoint URL for artifact delivery')] + url: Annotated[WireAnyUrl, Field(description='Webhook endpoint URL for artifact delivery')] token: Annotated[ str | None, Field( @@ -303,7 +303,7 @@ class CreateMediaBuyRequest(AdcpRequest, AdcpVersionEnvelope): ] = None start_time: start_timing.StartTiming end_time: Annotated[ - AwareDatetime, Field(description='Campaign end date/time in ISO 8601 format') + WireDatetime, Field(description='Campaign end date/time in ISO 8601 format') ] pacing: Annotated[ pacing_1.Pacing | None, diff --git a/src/adcp/types/domains/media_buy/create_media_buy_response.py b/src/adcp/types/domains/media_buy/create_media_buy_response.py index d9cd4d726..b81452666 100644 --- a/src/adcp/types/domains/media_buy/create_media_buy_response.py +++ b/src/adcp/types/domains/media_buy/create_media_buy_response.py @@ -8,7 +8,8 @@ from typing import Annotated, Any, Literal, TypeAlias -from pydantic import AwareDatetime, ConfigDict, Field, StringConstraints, model_validator +from adcp.types.base import WireDatetime +from pydantic import ConfigDict, Field, StringConstraints, model_validator from ..core.version_envelope import AdcpVersionEnvelope from ..core import account as account_1 @@ -38,8 +39,8 @@ class CreateMediaBuyResponse1(AdcpResponse, AdcpVersionEnvelope, ProtocolEnvelop account: account_1.Account | None = None invoice_recipient: business_entity_1.BusinessEntity | None = None media_buy_status: media_buy_status_1.MediaBuyStatus | None = None - confirmed_at: AwareDatetime | None - creative_deadline: AwareDatetime | None = None + confirmed_at: WireDatetime | None + creative_deadline: WireDatetime | None = None revision: Annotated[int, Field(ge=1)] currency: Annotated[str, StringConstraints(pattern='^[A-Z]{3}$')] | None = None total_budget: Annotated[float, Field(ge=0)] | None = None diff --git a/src/adcp/types/domains/media_buy/get_media_buy_delivery_response.py b/src/adcp/types/domains/media_buy/get_media_buy_delivery_response.py index ecb618592..26f7e5452 100644 --- a/src/adcp/types/domains/media_buy/get_media_buy_delivery_response.py +++ b/src/adcp/types/domains/media_buy/get_media_buy_delivery_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/get_media_buy_delivery_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -10,8 +10,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import attribution_window as attribution_window_1 from ..core import ( @@ -69,13 +69,13 @@ class ReportingPeriod(AdCPBaseModel): extra='allow', ) start: Annotated[ - AwareDatetime, + WireDatetime, Field( description="Inclusive RFC 3339 start instant. For a date-bounded request, the start of start_date in the reporting timezone, written with that zone's UTC offset or as the equivalent UTC instant (e.g., 2026-04-15T00:00:00-04:00 or 2026-04-15T04:00:00Z for America/New_York; 2026-04-15T00:00:00Z for UTC)." ), ] end: Annotated[ - AwareDatetime, + WireDatetime, Field( description='Exclusive RFC 3339 end instant. For a date-bounded request, the start of end_date in the reporting timezone (e.g., 2026-04-16T00:00:00-04:00 for a report covering 2026-04-15 in America/New_York).' ), @@ -461,7 +461,7 @@ class BySpotItem(DeliveryMetrics): NoExplicitNull, ] = None aired_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='RFC 3339 timestamp when the spot actually aired. Its presence proves an airing; it is not inferred from impressions.' ), @@ -506,13 +506,13 @@ class ByPackageItem1(DeliveryMetrics): class Window(AdCPBaseModel): window_start: Annotated[ - AwareDatetime, + WireDatetime, Field( description='ISO 8601 start of the window slice (inclusive). Daily, weekly, monthly, and quarterly slices start on reporting-timezone calendar boundaries (see reporting_period.timezone), which are UTC midnights only when that timezone is UTC. Weekly slices start Monday 00:00 in the reporting timezone (ISO 8601 weeks). Quarterly slices start on 1 January, 1 April, 1 July and 1 October.' ), ] window_end: Annotated[ - AwareDatetime, + WireDatetime, Field( description="ISO 8601 end of the window slice (exclusive). The next row's window_start equals this value when slices are contiguous." ), @@ -597,7 +597,7 @@ class ByPackageItem(DeliveryMetrics): NoExplicitNull, ] = None finalized_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="ISO 8601 timestamp at which this package's data became final. Present only when `is_final: true`. Anchors reconciliation and (when later defined) dispute-window clocks against the buy's `measurement_terms.billing_measurement.measurement_window`." ), @@ -1200,7 +1200,7 @@ class MediaBuyDelivery(AdCPBaseModel): ), ] expected_availability: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When delayed data is expected to be available (only present when status is reporting_delayed)' ), @@ -1221,7 +1221,7 @@ class MediaBuyDelivery(AdCPBaseModel): NoExplicitNull, ] = None finalized_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="ISO 8601 timestamp at which this row became final. Present only when `is_final: true`. Anchors the buyer's reconciliation and (when later defined) dispute-window clocks against the buy's `measurement_terms.billing_measurement`. Computed as the latest `finalized_at` across the row's packages for the reconciliation window." ), @@ -1293,7 +1293,7 @@ class GetMediaBuyDeliveryResponse(AdcpResponse, AdcpVersionEnvelope, ProtocolEnv NoExplicitNull, ] = None next_expected_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="ISO 8601 timestamp for next expected notification (only present in webhook deliveries when notification_type is not 'final')" ), diff --git a/src/adcp/types/domains/media_buy/get_media_buys_response.py b/src/adcp/types/domains/media_buy/get_media_buys_response.py index f11e593c3..a11535b2a 100644 --- a/src/adcp/types/domains/media_buy/get_media_buys_response.py +++ b/src/adcp/types/domains/media_buy/get_media_buys_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/get_media_buys_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -10,8 +10,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import account as account_1 from ..core import bidding_policy @@ -68,7 +68,7 @@ class Cancellation(AdCPBaseModel): extra='forbid', ) canceled_at: Annotated[ - AwareDatetime, Field(description='ISO 8601 timestamp when this media buy was canceled.') + WireDatetime, Field(description='ISO 8601 timestamp when this media buy was canceled.') ] canceled_by: Annotated[ canceled_by_1.CanceledBy, Field(description='Which party initiated the cancellation.') @@ -87,7 +87,7 @@ class HistoryItem(AdCPBaseModel): revision: Annotated[ SchemaInt, Field(description='Revision number after this change was applied.', ge=1) ] - timestamp: Annotated[AwareDatetime, Field(description='When this change occurred.')] + timestamp: Annotated[WireDatetime, Field(description='When this change occurred.')] actor: Annotated[ str | None, Field( @@ -137,7 +137,7 @@ class Cancellation1(AdCPBaseModel): extra='forbid', ) canceled_at: Annotated[ - AwareDatetime, Field(description='ISO 8601 timestamp when this package was canceled.') + WireDatetime, Field(description='ISO 8601 timestamp when this package was canceled.') ] canceled_by: Annotated[ canceled_by_1.CanceledBy, @@ -167,7 +167,7 @@ class Snapshot(AdCPBaseModel): extra='allow', ) as_of: Annotated[ - AwareDatetime, + WireDatetime, Field(description='ISO 8601 timestamp when this snapshot was captured by the platform'), ] staleness_seconds: Annotated[ @@ -234,7 +234,7 @@ class Indicator(AdCPBaseModel): ), ] detected_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the seller first detected the current uninterrupted occurrence of this indicator. Keep this value stable while the condition remains present. If the condition clears and is later detected again, use the new detection time. Optional because some upstream platforms expose current assessments without an original detection timestamp.' ), @@ -269,7 +269,7 @@ class Indicator1(AdCPBaseModel): ), ] detected_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the seller first detected the current uninterrupted occurrence of this indicator. Keep this value stable while the condition remains present. If the condition clears and is later detected again, use the new detection time. Optional because some upstream platforms expose current assessments without an original detection timestamp.' ), @@ -304,7 +304,7 @@ class Indicator2(AdCPBaseModel): ), ] detected_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the seller first detected the current uninterrupted occurrence of this indicator. Keep this value stable while the condition remains present. If the condition clears and is later detected again, use the new detection time. Optional because some upstream platforms expose current assessments without an original detection timestamp.' ), @@ -364,7 +364,7 @@ class CreativeApproval(IndicatorBearingResourceState): NoExplicitNull, ] = None indicators_as_of: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the seller last completed the evaluation represented by indicators for this relationship. Required whenever indicators is present, including an empty array.' ), @@ -535,14 +535,14 @@ class Package(IndicatorBearingResourceState): NoExplicitNull, ] = None start_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 flight start time for this package. Use to determine whether the package is within its scheduled flight before interpreting delivery status.' ), NoExplicitNull, ] = None end_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='ISO 8601 flight end time for this package'), NoExplicitNull, ] = None @@ -564,7 +564,7 @@ class Package(IndicatorBearingResourceState): NoExplicitNull, ] = None creative_deadline: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="ISO 8601 timestamp for creative upload or change deadline for this package. After this deadline, creative changes are rejected. When absent, the media buy's creative_deadline applies." ), @@ -700,7 +700,7 @@ class MediaBuy(IndicatorBearingResourceState): ] = None status: media_buy_status.MediaBuyStatus status_as_of: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp indicating when the seller last refreshed the returned media-buy-level `status` from its source of truth. Use this to interpret cached or rolled-up list statuses, especially for curator/storefront aggregators where one buyer-facing buy maps to multiple upstream legs. For rolled-up statuses, this timestamp MUST NOT be later than the oldest upstream status observation that could affect the returned roll-up, so it never overstates freshness. Omit or return null to make no freshness assertion; buyers MUST NOT infer that an omitted or null value means the status is live. This is distinct from `updated_at`, which records when the media buy was last modified.' ), @@ -781,26 +781,26 @@ class MediaBuy(IndicatorBearingResourceState): NoExplicitNull, ] = None start_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 flight start time for this media buy (earliest package start_time). Avoids requiring buyers to compute min(packages[].start_time).' ), NoExplicitNull, ] = None end_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 flight end time for this media buy (latest package end_time). Avoids requiring buyers to compute max(packages[].end_time).' ), NoExplicitNull, ] = None creative_deadline: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='ISO 8601 timestamp for creative upload deadline'), NoExplicitNull, ] = None confirmed_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp when the seller committed to this media buy. May be null until seller commitment occurs in deferred/manual approval flows. Once populated, remains stable through later pause, resume, activation, completion, cancellation, and reporting transitions.' ), @@ -818,10 +818,10 @@ class MediaBuy(IndicatorBearingResourceState): ), ] created_at: Annotated[ - AwareDatetime | None, Field(description='Creation timestamp'), NoExplicitNull + WireDatetime | None, Field(description='Creation timestamp'), NoExplicitNull ] = None updated_at: Annotated[ - AwareDatetime | None, Field(description='Last update timestamp'), NoExplicitNull + WireDatetime | None, Field(description='Last update timestamp'), NoExplicitNull ] = None context: Annotated[ context_1.ContextObject | None, diff --git a/src/adcp/types/domains/media_buy/get_products_async_response_submitted.py b/src/adcp/types/domains/media_buy/get_products_async_response_submitted.py index 1699e6a90..90a6ac3aa 100644 --- a/src/adcp/types/domains/media_buy/get_products_async_response_submitted.py +++ b/src/adcp/types/domains/media_buy/get_products_async_response_submitted.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: media_buy/get_products_async_response_submitted.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..core import context as context_1 from ..core import error @@ -39,7 +39,7 @@ class GetProductsSubmitted(AdCPBaseModel): NoExplicitNull, ] = None estimated_completion: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Estimated completion time for the search'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/media_buy/get_reporting_status_request.py b/src/adcp/types/domains/media_buy/get_reporting_status_request.py index 140437f82..0e8cac33a 100644 --- a/src/adcp/types/domains/media_buy/get_reporting_status_request.py +++ b/src/adcp/types/domains/media_buy/get_reporting_status_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/get_reporting_status_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,9 +9,9 @@ from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field from ..core import canonical_account_ref from ..core import context as context_1 @@ -30,8 +30,8 @@ class Period(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - start: AwareDatetime - end: AwareDatetime + start: WireDatetime + end: WireDatetime class ReportingStatusView(StrEnum): diff --git a/src/adcp/types/domains/media_buy/get_reporting_status_response.py b/src/adcp/types/domains/media_buy/get_reporting_status_response.py index 6a7922c9a..db7e5bb10 100644 --- a/src/adcp/types/domains/media_buy/get_reporting_status_response.py +++ b/src/adcp/types/domains/media_buy/get_reporting_status_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/get_reporting_status_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool, model_validator +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, model_validator from ..core import context as context_1 from ..core import error @@ -75,8 +75,8 @@ class Scope(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - period_start: AwareDatetime - period_end: AwareDatetime + period_start: WireDatetime + period_end: WireDatetime scope_closed: Annotated[ StrictBool, Field(description='True only when no new obligation can enter this evaluated scope.'), @@ -99,7 +99,7 @@ class Scope(AdCPBaseModel): feed_purposes: list[reporting_delivery_offering.ReportingFeedPurpose] finality: list[reporting_finality.ReportingFinality] ledger_retained_from: Annotated[ - AwareDatetime, + WireDatetime, Field( description='Earliest period boundary for which anti-entropy metadata is retained for every selected configuration generation.' ), @@ -127,7 +127,7 @@ class GetReportingStatusResponse(AdcpResponse, AdcpVersionEnvelope, ProtocolEnve NoExplicitNull, ] = None ledger_as_of: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Exclusive observation boundary for ledger_snapshot_id. Revisions committed later appear only in a later reconciliation.' ), @@ -163,13 +163,13 @@ class GetReportingStatusResponse(AdcpResponse, AdcpVersionEnvelope, ProtocolEnve NoExplicitNull, ] = None data_through: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Conservative latest included event time across satisfied obligations in scope, or null when unavailable/unknown.' ), ] = None next_expected_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Next obligation due time for an open scope. In a complete summary (view: summary, health: complete), the nearest future period start, strictly after ledger_as_of, across all active committed configuration generations in scope.delivery_config_generations. Sellers MUST populate it when such a scheduled period exists outside the closed evaluated scope, and omit it when none exists. This complete-scope projection applies only to summary responses. It is derived from the configuration schedule and does not represent an open obligation in the evaluated scope; its presence does not indicate that the scope is still open. Projecting it MUST NOT create, expose, lease, count, or alter an obligation whose period has not closed, or change obligation_counts, scope, or coverage. Obligation expected_at remains period.end plus schedule.delivery_sla; get_media_buy_delivery.next_expected_at remains the next webhook notification time.' ), diff --git a/src/adcp/types/domains/media_buy/legacy_purchase_continuation_input.py b/src/adcp/types/domains/media_buy/legacy_purchase_continuation_input.py index 922139c42..9f6784e24 100644 --- a/src/adcp/types/domains/media_buy/legacy_purchase_continuation_input.py +++ b/src/adcp/types/domains/media_buy/legacy_purchase_continuation_input.py @@ -1,14 +1,13 @@ # generated by datamodel-codegen: # filename: media_buy/legacy_purchase_continuation_input.json -# timestamp: 2026-10-04T00:53:27+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any -from uuid import UUID -from adcp.types.base import AdCPBaseModel +from adcp.types.base import AdCPBaseModel, WireUuid from adcp.types._scalar import ScalarStr from pydantic import ConfigDict, Field, field_validator @@ -31,7 +30,7 @@ class CompatibilityPurchaseCoordinatorInput(AdCPBaseModel): extra='forbid', ) idempotency_key: Annotated[ - UUID, + WireUuid, Field( description='Replay identity for this logical coordinator operation. Exact retries resume the durable operation record instead of redeeming the continuation again.' ), diff --git a/src/adcp/types/domains/media_buy/list_creative_formats_response.py b/src/adcp/types/domains/media_buy/list_creative_formats_response.py index 945755279..15417b218 100644 --- a/src/adcp/types/domains/media_buy/list_creative_formats_response.py +++ b/src/adcp/types/domains/media_buy/list_creative_formats_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/list_creative_formats_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictBool from ..core import context as context_1 from ..core import error @@ -29,7 +29,7 @@ class Source(StrEnum): class CreativeAgent(AdCPBaseModel): agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="Base URL for the creative agent (e.g., 'https://reference.example.com', 'https://dco.example.com')." ), diff --git a/src/adcp/types/domains/media_buy/media_buy_commitment_response.py b/src/adcp/types/domains/media_buy/media_buy_commitment_response.py index 869389c64..ee6d14b81 100644 --- a/src/adcp/types/domains/media_buy/media_buy_commitment_response.py +++ b/src/adcp/types/domains/media_buy/media_buy_commitment_response.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: media_buy/media_buy_commitment_response.json -# timestamp: 2026-10-21T11:25:44+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field from ..core import canonical_media_buy_action from ..core import context as context_1 @@ -109,7 +109,7 @@ class MediaBuyCommitmentResponse1(AdCPBaseModel): ] = None revision: Annotated[SchemaInt, Field(ge=1)] media_buy_status: Annotated[media_buy_status_1.MediaBuyStatus | None, NoExplicitNull] = None - confirmed_at: AwareDatetime | None = None + confirmed_at: WireDatetime | None = None accepted_proposal: AcceptedProposal purchase_bindings: Annotated[ list[PurchaseBinding], diff --git a/src/adcp/types/domains/media_buy/media_buy_delivery_webhook_result.py b/src/adcp/types/domains/media_buy/media_buy_delivery_webhook_result.py index aad82084f..932a920dc 100644 --- a/src/adcp/types/domains/media_buy/media_buy_delivery_webhook_result.py +++ b/src/adcp/types/domains/media_buy/media_buy_delivery_webhook_result.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: media_buy/media_buy_delivery_webhook_result.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import attribution_window as attribution_window_1 from ..core import context as context_1 @@ -32,8 +32,8 @@ class ReportingPeriod(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - start: AwareDatetime - end: AwareDatetime + start: WireDatetime + end: WireDatetime timezone: Annotated[ str | None, Field( @@ -91,7 +91,7 @@ class ByPackageItem(DeliveryMetrics): ] = None paused: Annotated[StrictBool | None, NoExplicitNull] = None is_final: Annotated[StrictBool | None, NoExplicitNull] = None - finalized_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + finalized_at: Annotated[WireDatetime | None, NoExplicitNull] = None measurement_window: Annotated[str | None, Field(max_length=50), NoExplicitNull] = None supersedes_window: Annotated[str | None, Field(max_length=50), NoExplicitNull] = None metric_values: Annotated[ @@ -124,7 +124,7 @@ class MediaBuyDelivery(AdCPBaseModel): ), ] expected_availability: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When delayed data is expected to be available. Present when status is reporting_delayed.' ), @@ -143,7 +143,7 @@ class MediaBuyDelivery(AdCPBaseModel): NoExplicitNull, ] = None finalized_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Timestamp when this row became final. Present only when is_final is true.' ), @@ -195,7 +195,7 @@ class MediaBuyDeliveryWebhookResult(AdCPBaseModel): NoExplicitNull, ] = None next_expected_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp for the next expected notification. Omitted on final notifications.' ), diff --git a/src/adcp/types/domains/media_buy/package_request.py b/src/adcp/types/domains/media_buy/package_request.py index 366f00c3d..a879e2ef1 100644 --- a/src/adcp/types/domains/media_buy/package_request.py +++ b/src/adcp/types/domains/media_buy/package_request.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: media_buy/package_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from collections.abc import Sequence from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat, model_validator +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat, model_validator from ..core import audience_evidence_pin from ..core import audience_evidence_requirements as audience_evidence_requirements_1 @@ -208,14 +208,14 @@ class PackageRequest(AdcpVersionEnvelope): NoExplicitNull, ] = None start_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="Flight start date/time for this package in ISO 8601 format. When omitted, the package inherits the media buy's start_time. Must fall within the media buy's date range." ), NoExplicitNull, ] = None end_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="Flight end date/time for this package in ISO 8601 format. When omitted, the package inherits the media buy's end_time. Must fall within the media buy's date range." ), diff --git a/src/adcp/types/domains/media_buy/package_update.py b/src/adcp/types/domains/media_buy/package_update.py index f3ec451d4..6e9f4908d 100644 --- a/src/adcp/types/domains/media_buy/package_update.py +++ b/src/adcp/types/domains/media_buy/package_update.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: media_buy/package_update.json -# timestamp: 2026-10-21T11:25:44+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import bidding_policy, catalog from ..core import context as context_1 @@ -112,14 +112,14 @@ class PackageUpdate(AdCPBaseModel): ), ] = None start_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="Updated flight start date/time for this package in ISO 8601 format. Must fall within the media buy's date range." ), NoExplicitNull, ] = None end_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description="Updated flight end date/time for this package in ISO 8601 format. Must fall within the media buy's date range." ), diff --git a/src/adcp/types/domains/media_buy/product_purchase.py b/src/adcp/types/domains/media_buy/product_purchase.py index 49f84e9fe..f0481ef89 100644 --- a/src/adcp/types/domains/media_buy/product_purchase.py +++ b/src/adcp/types/domains/media_buy/product_purchase.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: media_buy/product_purchase.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat +from pydantic import ConfigDict, Field, StrictFloat from ..core import ( audience_evidence_pin, @@ -86,14 +86,14 @@ class ProductPurchase(AdCPBaseModel): StrictFloat | None, Field(ge=0.0, title='Product Purchase Impressions'), NoExplicitNull ] = None start_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Resolved package flight start. On direct-purchase input, omission inherits the MediaBuy start; accepted proposal snapshots carry the resolved timestamp.' ), NoExplicitNull, ] = None end_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Resolved package flight end. On direct-purchase input, omission inherits the MediaBuy end; accepted proposal snapshots carry the resolved timestamp.' ), diff --git a/src/adcp/types/domains/media_buy/product_purchase_input.py b/src/adcp/types/domains/media_buy/product_purchase_input.py index 76c19ffbc..68ca489db 100644 --- a/src/adcp/types/domains/media_buy/product_purchase_input.py +++ b/src/adcp/types/domains/media_buy/product_purchase_input.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: media_buy/product_purchase_input.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat +from pydantic import ConfigDict, Field, StrictFloat from ..core import ( audience_evidence_pin, @@ -86,14 +86,14 @@ class ProductPurchaseInput(AdCPBaseModel): StrictFloat | None, Field(ge=0.0, title='Product Purchase Impressions'), NoExplicitNull ] = None start_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Resolved package flight start. On direct-purchase input, omission inherits the MediaBuy start; accepted proposal snapshots carry the resolved timestamp.' ), NoExplicitNull, ] = None end_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Resolved package flight end. On direct-purchase input, omission inherits the MediaBuy end; accepted proposal snapshots carry the resolved timestamp.' ), diff --git a/src/adcp/types/domains/media_buy/proposal_refinement.py b/src/adcp/types/domains/media_buy/proposal_refinement.py index daa42b41c..99df08868 100644 --- a/src/adcp/types/domains/media_buy/proposal_refinement.py +++ b/src/adcp/types/domains/media_buy/proposal_refinement.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: media_buy/proposal_refinement.json -# timestamp: 2026-10-21T12:09:21+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field, StrictFloat from ..core import product_change_map from . import product_discovery_criteria, proposal_budget_constraint @@ -43,8 +43,8 @@ class Flight(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - start_no_later_than: Annotated[AwareDatetime | None, NoExplicitNull] = None - end_no_earlier_than: Annotated[AwareDatetime | None, NoExplicitNull] = None + start_no_later_than: Annotated[WireDatetime | None, NoExplicitNull] = None + end_no_earlier_than: Annotated[WireDatetime | None, NoExplicitNull] = None class Constraints(AdCPBaseModel): diff --git a/src/adcp/types/domains/media_buy/provide_performance_feedback_response.py b/src/adcp/types/domains/media_buy/provide_performance_feedback_response.py index 14ebe83cd..ff0ce73cc 100644 --- a/src/adcp/types/domains/media_buy/provide_performance_feedback_response.py +++ b/src/adcp/types/domains/media_buy/provide_performance_feedback_response.py @@ -8,7 +8,8 @@ from typing import Annotated, Literal, TypeAlias -from pydantic import AwareDatetime, ConfigDict, Field, StringConstraints +from adcp.types.base import WireDatetime +from pydantic import ConfigDict, Field, StringConstraints from ..core.version_envelope import AdcpVersionEnvelope from ..core import context as context_1 @@ -23,8 +24,8 @@ class ProvidePerformanceFeedbackResponse1(AdcpResponse, AdcpVersionEnvelope, Pro feedback_id: Annotated[str, StringConstraints(min_length=1)] | None = None application_status: Literal['accepted', 'applied', 'not_applied'] | None = None status_reason: Annotated[str, StringConstraints(max_length=500)] | None = None - received_at: AwareDatetime | None = None - applied_at: AwareDatetime | None = None + received_at: WireDatetime | None = None + applied_at: WireDatetime | None = None sandbox: bool | None = None context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None diff --git a/src/adcp/types/domains/media_buy/refine_proposals_response.py b/src/adcp/types/domains/media_buy/refine_proposals_response.py index bb2621ddc..135548ecb 100644 --- a/src/adcp/types/domains/media_buy/refine_proposals_response.py +++ b/src/adcp/types/domains/media_buy/refine_proposals_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/refine_proposals_response.json -# timestamp: 2026-10-21T11:25:44+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,9 +9,9 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field, StrictFloat +from pydantic import ConfigDict, Field, StrictFloat from ..core import canonical_delivery_forecast, canonical_product, canonical_proposal from ..core import context as context_1 @@ -105,9 +105,9 @@ class Proposal(AdCPBaseModel): title='Proposal Status', ), ] = 'draft' - accepted_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + accepted_at: Annotated[WireDatetime | None, NoExplicitNull] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='For a draft, the indicative-terms freshness deadline. For a committed proposal, the inventory-hold deadline.' ), @@ -178,9 +178,9 @@ class Proposal3(AdCPBaseModel): title='Proposal Status', ), ] = 'committed' - accepted_at: Annotated[AwareDatetime | None, NoExplicitNull] = None + accepted_at: Annotated[WireDatetime | None, NoExplicitNull] = None expires_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='For a draft, the indicative-terms freshness deadline. For a committed proposal, the inventory-hold deadline.' ), diff --git a/src/adcp/types/domains/media_buy/request_proposals_response.py b/src/adcp/types/domains/media_buy/request_proposals_response.py index 5f7b864b6..d70869cae 100644 --- a/src/adcp/types/domains/media_buy/request_proposals_response.py +++ b/src/adcp/types/domains/media_buy/request_proposals_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/request_proposals_response.json -# timestamp: 2026-10-21T11:25:44+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,9 +9,9 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime from adcp.types._scalar import ScalarStr -from pydantic import AwareDatetime, ConfigDict, Field +from pydantic import ConfigDict, Field from ..core import canonical_product from ..core import context as context_1 @@ -119,7 +119,7 @@ class PurchaseContinuation1(AdCPBaseModel): ), ] continuation_expires_at: Annotated[ - AwareDatetime, + WireDatetime, Field(description='Absolute expiry of the single-use compatibility continuation.'), ] source_adcp_version: Annotated[ @@ -195,7 +195,7 @@ class Proposal(CanonicalProposal): ), ] = 'draft' expires_at: Annotated[ - AwareDatetime, + WireDatetime, Field( description='For a draft, the indicative-terms freshness deadline. For a committed proposal, the inventory-hold deadline.' ), diff --git a/src/adcp/types/domains/media_buy/sync_audiences_response.py b/src/adcp/types/domains/media_buy/sync_audiences_response.py index 02bc2dd2c..27dd8f243 100644 --- a/src/adcp/types/domains/media_buy/sync_audiences_response.py +++ b/src/adcp/types/domains/media_buy/sync_audiences_response.py @@ -8,7 +8,8 @@ from typing import Annotated, Literal, TypeAlias -from pydantic import AwareDatetime, ConfigDict, Field, StringConstraints +from adcp.types.base import WireDatetime +from pydantic import ConfigDict, Field, StringConstraints from ..core.version_envelope import AdcpVersionEnvelope from ..core import brand_ref as brand_ref_1 @@ -51,7 +52,7 @@ class Audience(AdcpVersionEnvelope): matched_count: Annotated[int, Field(ge=0)] | None = None effective_match_rate: Annotated[float, Field(ge=0, le=1)] | None = None match_breakdown: Annotated[list[MatchBreakdown], Field(min_length=1)] | None = None - last_synced_at: AwareDatetime | None = None + last_synced_at: WireDatetime | None = None source: Source | None = None minimum_size: Annotated[int, Field(ge=1)] | None = None errors: list[error_1.Error] | None = None diff --git a/src/adcp/types/domains/media_buy/sync_catalogs_response.py b/src/adcp/types/domains/media_buy/sync_catalogs_response.py index f6767bf71..d4b52a790 100644 --- a/src/adcp/types/domains/media_buy/sync_catalogs_response.py +++ b/src/adcp/types/domains/media_buy/sync_catalogs_response.py @@ -8,7 +8,8 @@ from typing import Annotated, Any, Literal, TypeAlias -from pydantic import AwareDatetime, ConfigDict, Field, StringConstraints +from adcp.types.base import WireDatetime +from pydantic import ConfigDict, Field, StringConstraints from ..core.version_envelope import AdcpVersionEnvelope from ..core import catalog_item_availability_state as catalog_item_availability_state_1 @@ -40,8 +41,8 @@ class Catalog(AdcpVersionEnvelope): items_pending: Annotated[int, Field(ge=0)] | None = None items_rejected: Annotated[int, Field(ge=0)] | None = None item_issues: list[ItemIssue] | None = None - last_synced_at: AwareDatetime | None = None - next_fetch_at: AwareDatetime | None = None + last_synced_at: WireDatetime | None = None + next_fetch_at: WireDatetime | None = None changes: list[str] | None = None errors: list[error_1.Error] | None = None warnings: list[str] | None = None diff --git a/src/adcp/types/domains/media_buy/update_media_buy_request.py b/src/adcp/types/domains/media_buy/update_media_buy_request.py index cfb718333..a583135d9 100644 --- a/src/adcp/types/domains/media_buy/update_media_buy_request.py +++ b/src/adcp/types/domains/media_buy/update_media_buy_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/update_media_buy_request.json -# timestamp: 2026-10-21T11:25:44+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from collections.abc import Sequence from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import account_ref, bidding_policy from ..core import budget_allocation as budget_allocation_1 @@ -95,7 +95,7 @@ class UpdateMediaBuyRequest(AdcpRequest, AdcpVersionEnvelope): ] = None start_time: Annotated[start_timing.StartTiming | None, NoExplicitNull] = None end_time: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='New end date/time in ISO 8601 format'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/media_buy/update_media_buy_response.py b/src/adcp/types/domains/media_buy/update_media_buy_response.py index abc2f3484..872a35d66 100644 --- a/src/adcp/types/domains/media_buy/update_media_buy_response.py +++ b/src/adcp/types/domains/media_buy/update_media_buy_response.py @@ -10,7 +10,8 @@ from typing import Annotated, Any, Literal, TypeAlias -from pydantic import AwareDatetime, ConfigDict, Field, StringConstraints, model_validator +from adcp.types.base import WireDatetime +from pydantic import ConfigDict, Field, StringConstraints, model_validator from ..core.version_envelope import AdcpVersionEnvelope from ..core import business_entity as business_entity_1 @@ -44,7 +45,7 @@ class UpdateMediaBuyResponse1(AdcpResponse, AdcpVersionEnvelope, ProtocolEnvelop budget_allocation: Any | None = None pacing: pacing_1.Pacing | None = None bidding: Any | None = None - implementation_date: AwareDatetime | None = None + implementation_date: WireDatetime | None = None invoice_recipient: business_entity_1.BusinessEntity | None = None affected_packages: Sequence[package_1.Package] | None = None valid_actions: list[media_buy_valid_action_1.MediaBuyValidAction] | None = None diff --git a/src/adcp/types/domains/property/authorization_result.py b/src/adcp/types/domains/property/authorization_result.py index 6f17f6337..e8e7699aa 100644 --- a/src/adcp/types/domains/property/authorization_result.py +++ b/src/adcp/types/domains/property/authorization_result.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: property/authorization_result.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field class Status(StrEnum): @@ -41,7 +41,7 @@ class AuthorizationResult(AdCPBaseModel): NoExplicitNull, ] = None sales_agent_url: Annotated[ - AnyUrl | None, Field(description='The sales agent URL that was validated'), NoExplicitNull + WireAnyUrl | None, Field(description='The sales agent URL that was validated'), NoExplicitNull ] = None violation: Annotated[ Violation | None, diff --git a/src/adcp/types/domains/property/delivery_record.py b/src/adcp/types/domains/property/delivery_record.py index 05ba8887e..baf8899c0 100644 --- a/src/adcp/types/domains/property/delivery_record.py +++ b/src/adcp/types/domains/property/delivery_record.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: property/delivery_record.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl +from pydantic import ConfigDict, Field from ..core import ext as ext_1 from ..core import identifier as identifier_1 @@ -32,7 +32,7 @@ class DeliveryRecord(AdCPBaseModel): NoExplicitNull, ] = None sales_agent_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="URL of the sales agent that sold this inventory. If provided, authorization is validated against the property's adagents.json." ), diff --git a/src/adcp/types/domains/property/get_property_list_response.py b/src/adcp/types/domains/property/get_property_list_response.py index be376c68d..ea78d2bd5 100644 --- a/src/adcp/types/domains/property/get_property_list_response.py +++ b/src/adcp/types/domains/property/get_property_list_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: property/get_property_list_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from typing import Annotated -from adcp.types.base import NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..core import context as context_1 from ..core import ext as ext_1 @@ -39,10 +39,10 @@ class GetPropertyListResponse(AdcpResponse, AdcpVersionEnvelope, ProtocolEnvelop ] = None pagination: Annotated[pagination_response.PaginationResponse | None, NoExplicitNull] = None resolved_at: Annotated[ - AwareDatetime | None, Field(description='When the list was resolved'), NoExplicitNull + WireDatetime | None, Field(description='When the list was resolved'), NoExplicitNull ] = None cache_valid_until: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='Cache expiration timestamp. Re-fetch the list after this time to get updated identifiers.' ), diff --git a/src/adcp/types/domains/property/property_feature_definition.py b/src/adcp/types/domains/property/property_feature_definition.py index d97ab887e..ad0b17000 100644 --- a/src/adcp/types/domains/property/property_feature_definition.py +++ b/src/adcp/types/domains/property/property_feature_definition.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: property/property_feature_definition.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field, StrictFloat from ..core import ext as ext_1 @@ -81,7 +81,7 @@ class PropertyFeatureDefinition(AdCPBaseModel): NoExplicitNull, ] = None methodology_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='URL to documentation explaining how this feature is calculated/measured' ), diff --git a/src/adcp/types/domains/property/property_feature_result.py b/src/adcp/types/domains/property/property_feature_result.py index e17b9b6d2..fcab98b9f 100644 --- a/src/adcp/types/domains/property/property_feature_result.py +++ b/src/adcp/types/domains/property/property_feature_result.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: property/property_feature_result.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..core import ext as ext_1 from ..core import property_id @@ -40,7 +40,7 @@ class PropertyFeatureResult(AdCPBaseModel): ), ] last_evaluated: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When features were last evaluated for this property'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/property/property_feature_value.py b/src/adcp/types/domains/property/property_feature_value.py index 9c48e4841..00dda78f2 100644 --- a/src/adcp/types/domains/property/property_feature_value.py +++ b/src/adcp/types/domains/property/property_feature_value.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: property/property_feature_value.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import ext as ext_1 @@ -35,12 +35,12 @@ class PropertyFeatureValue(AdCPBaseModel): NoExplicitNull, ] = None measured_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When this specific value was measured'), NoExplicitNull, ] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When this certification/value expires (for time-limited certifications)' ), diff --git a/src/adcp/types/domains/property/property_list.py b/src/adcp/types/domains/property/property_list.py index 8be313aa0..7ef37da83 100644 --- a/src/adcp/types/domains/property/property_list.py +++ b/src/adcp/types/domains/property/property_list.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: property/property_list.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field from ..core import account_ref, brand_ref, vendor_pricing_option from . import base_property_source, property_list_filters @@ -49,7 +49,7 @@ class PropertyList(AdCPBaseModel): NoExplicitNull, ] = None webhook_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL to receive notifications when the resolved list changes'), NoExplicitNull, ] = None @@ -62,10 +62,10 @@ class PropertyList(AdCPBaseModel): NoExplicitNull, ] = 24 created_at: Annotated[ - AwareDatetime | None, Field(description='When the list was created'), NoExplicitNull + WireDatetime | None, Field(description='When the list was created'), NoExplicitNull ] = None updated_at: Annotated[ - AwareDatetime | None, Field(description='When the list was last modified'), NoExplicitNull + WireDatetime | None, Field(description='When the list was last modified'), NoExplicitNull ] = None property_count: Annotated[ SchemaInt | None, diff --git a/src/adcp/types/domains/property/property_list_changed_webhook.py b/src/adcp/types/domains/property/property_list_changed_webhook.py index 4392f3bfd..4bf1b1834 100644 --- a/src/adcp/types/domains/property/property_list_changed_webhook.py +++ b/src/adcp/types/domains/property/property_list_changed_webhook.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: property/property_list_changed_webhook.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireDatetime +from pydantic import ConfigDict, Field from ..core import ext as ext_1 @@ -54,9 +54,9 @@ class PropertyListChangedWebhook(AdCPBaseModel): Field(description='Summary of changes to the resolved list'), NoExplicitNull, ] = None - resolved_at: Annotated[AwareDatetime, Field(description='When the list was re-resolved')] + resolved_at: Annotated[WireDatetime, Field(description='When the list was re-resolved')] cache_valid_until: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When the consumer should refresh from the governance agent'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/property/update_property_list_request.py b/src/adcp/types/domains/property/update_property_list_request.py index 76d4fad1b..6f81fe252 100644 --- a/src/adcp/types/domains/property/update_property_list_request.py +++ b/src/adcp/types/domains/property/update_property_list_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: property/update_property_list_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from typing import Annotated -from adcp.types.base import NoExplicitNull -from pydantic import AnyUrl, ConfigDict, Field +from adcp.types.base import NoExplicitNull, WireAnyUrl +from pydantic import ConfigDict, Field from ..core import account_ref, brand_ref from ..core import context as context_1 @@ -52,7 +52,7 @@ class UpdatePropertyListRequest(AdcpRequest, AdcpVersionEnvelope): NoExplicitNull, ] = None webhook_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Update the webhook URL for list change notifications (set to empty string to remove)' ), diff --git a/src/adcp/types/domains/property/validate_property_delivery_response.py b/src/adcp/types/domains/property/validate_property_delivery_response.py index 20f912b60..50f42df24 100644 --- a/src/adcp/types/domains/property/validate_property_delivery_response.py +++ b/src/adcp/types/domains/property/validate_property_delivery_response.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: property/validate_property_delivery_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import context as context_1 from ..core import ext as ext_1 @@ -73,7 +73,7 @@ class Aggregate(AdCPBaseModel): NoExplicitNull, ] = None methodology_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL explaining how this aggregate was calculated'), NoExplicitNull, ] = None @@ -147,10 +147,10 @@ class ValidatePropertyDeliveryResponse(AdcpVersionEnvelope, ProtocolEnvelope): ), ] validated_at: Annotated[ - AwareDatetime, Field(description='Timestamp when validation was performed') + WireDatetime, Field(description='Timestamp when validation was performed') ] list_resolved_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Timestamp of the property list resolution used for validation'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/protocol/get_adcp_capabilities_response.py b/src/adcp/types/domains/protocol/get_adcp_capabilities_response.py index ef884fd58..b170102f9 100644 --- a/src/adcp/types/domains/protocol/get_adcp_capabilities_response.py +++ b/src/adcp/types/domains/protocol/get_adcp_capabilities_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: protocol/get_adcp_capabilities_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -10,11 +10,9 @@ from adcp.types._str_enum import StrEnum from typing import Any, Annotated, Dict, Literal -from adcp.types.base import AdCPBaseModel, GeoPlaceSystemKey, NoExplicitNull +from adcp.types.base import AdCPBaseModel, GeoPlaceSystemKey, NoExplicitNull, WireAnyUrl, WireDatetime from adcp.types._scalar import ScalarInt, ScalarStr from pydantic import ( - AnyUrl, - AwareDatetime, ConfigDict, Field, RootModel, @@ -216,7 +214,7 @@ class CapabilityChanges(AdCPBaseModel): NoExplicitNull, ] = None last_modified: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp when the seller last changed the advertised capability document. This is the scoped replacement for legacy top-level `last_updated` when buyers need cache validation semantics.' ), @@ -491,7 +489,7 @@ class Account(AdCPBaseModel): NoExplicitNull, ] = False authorization_endpoint: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='OAuth authorization endpoint for obtaining operator-level credentials. Present when the seller supports OAuth for operator authentication. The agent directs the operator to this URL to authenticate and obtain a bearer token. If absent and require_operator_auth is true, operators obtain credentials out-of-band (e.g., seller portal, API key).' ), @@ -573,7 +571,7 @@ class AcceptancePolicyDiscovery(AdCPBaseModel): extra='forbid', ) catalog_url: Annotated[ - AnyUrl, + WireAnyUrl, Field(description='HTTPS document that validates against acceptance-policy-catalog.json.'), ] catalog_digest: Annotated[ @@ -1039,7 +1037,7 @@ class SignalActivation(AdCPBaseModel): ), ] claim_types: Annotated[ - list[AnyUrl], + list[WireAnyUrl], Field( description='Signal-quality claim identifiers accepted for this governed action. Values are open absolute URIs and MUST be a subset of adcp.attestations.accepted_claim_types.', min_length=1, @@ -1094,7 +1092,7 @@ class PropertyFeature(AdCPBaseModel): NoExplicitNull, ] = None methodology_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='URL to documentation explaining how this feature is calculated or measured. Helps buyers understand and compare methodologies across vendors.' ), @@ -1131,7 +1129,7 @@ class CreativeFeature(AdCPBaseModel): NoExplicitNull, ] = None methodology_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='URL to documentation explaining how this feature is calculated or measured.' ), @@ -1182,7 +1180,7 @@ class Transport1(AdCPBaseModel): extra='allow', ) type: Annotated[Type6, Field(description='Protocol transport type')] - url: Annotated[AnyUrl, Field(description='Agent endpoint URL for this transport')] + url: Annotated[WireAnyUrl, Field(description='Agent endpoint URL for this transport')] class Endpoint(AdCPBaseModel): @@ -1488,24 +1486,24 @@ class KeyOrigins(AdCPBaseModel): extra='forbid', ) governance_signing: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='Origin (scheme + host) serving the governance-signing JWKS.'), NoExplicitNull, ] = None request_signing: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='Origin (scheme + host) serving the request-signing JWKS.'), NoExplicitNull, ] = None webhook_signing: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Origin (scheme + host) serving the JWKS used for webhook delivery. Webhooks are signed with `adcp_use: "request-signing"` keys; the deprecated `adcp_use: "webhook-signing"` value remains accepted during the backward-compatibility window.' ), NoExplicitNull, ] = None tmp_signing: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Origin (scheme + host) serving the TMP-signing JWKS, when this operator participates in TMP.' ), @@ -1536,7 +1534,7 @@ class Identity(AdCPBaseModel): extra='allow', ) brand_json_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="HTTPS URL of the operator's brand.json (typically `https://{operator-domain}/.well-known/brand.json`). Trust-root pointer for this agent's signing keys. See [security.mdx §Discovering an agent's signing keys via `brand_json_url`](https://adcontextprotocol.org/docs/building/by-layer/L1/security#discovering-an-agents-signing-keys-via-brand_json_url) for the verifier algorithm and `x-adcp-validation` for structured constraints. Distinct from `sponsored_intelligence.brand_url`, which is a rendering pointer for SI agent visuals — verifiers MUST use this field for key discovery and MUST NOT fall back to `sponsored_intelligence.brand_url` as a trust-root pointer." ), @@ -1609,7 +1607,7 @@ class Accreditation(AdCPBaseModel): NoExplicitNull, ] = None evidence_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="Optional URL pointing at the accrediting body's public listing for this certification (the buyer's path to verify the claim independently)." ), @@ -2130,7 +2128,7 @@ class SponsoredIntelligence(AdCPBaseModel): Field(description='Modalities, components, and commerce capabilities'), ] brand_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='URL to brand.json with colors, fonts, logos, tone'), NoExplicitNull, ] = None @@ -2201,7 +2199,7 @@ class Metric(AdCPBaseModel): ), ] standard_reference: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional URI pointing at the published standard this metric IMPLEMENTS (e.g., IAB Attention Measurement Guidelines, MRC Viewable Impression Measurement, GARM emissions framework). Distinct from `accreditations[]` — `standard_reference` is what the metric is built against; `accreditations[]` is third-party certification that the implementation actually conforms. Buyer agents normalizing across vendors SHOULD apply the AdCP URL canonicalization rules before comparing — vendors implementing the same standard MAY use different URL forms for the same canonical document.' ), @@ -2230,7 +2228,7 @@ class Metric(AdCPBaseModel): NoExplicitNull, ] = None methodology_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="URL to the vendor's full methodology documentation for this metric. Buyers SHOULD link or fetch this when human review of the methodology is in scope (compliance, RFP review, accreditation audit). Field name mirrors `governance.property_features[].methodology_url`." ), @@ -2314,7 +2312,7 @@ class Execution(AdCPBaseModel): NoExplicitNull, ] = None axe_integrations: Annotated[ - list[AnyUrl] | None, + list[WireAnyUrl] | None, Field( deprecated=True, description='Deprecated. Legacy AXE integrations. Use trusted_match for new integrations.', @@ -2973,7 +2971,7 @@ class GetAdcpCapabilitiesResponse(AdcpResponse, AdcpVersionEnvelope, ProtocolEnv NoExplicitNull, ] = None last_updated: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='ISO 8601 timestamp of when capabilities were last updated. Buyers can use this for cache invalidation.' ), diff --git a/src/adcp/types/domains/protocol/get_task_status_response.py b/src/adcp/types/domains/protocol/get_task_status_response.py index 941006d3e..bc8ed4ad2 100644 --- a/src/adcp/types/domains/protocol/get_task_status_response.py +++ b/src/adcp/types/domains/protocol/get_task_status_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: protocol/get_task_status_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool, StrictFloat +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import context as context_1 from ..core import ext as ext_1 @@ -79,7 +79,7 @@ class HistoryItem(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - timestamp: Annotated[AwareDatetime, Field(description='When this exchange occurred (ISO 8601)')] + timestamp: Annotated[WireDatetime, Field(description='When this exchange occurred (ISO 8601)')] type: Annotated[ Type, Field(description='Whether this was a request from client or response from server') ] @@ -97,13 +97,13 @@ class GetTaskStatusResponse(AdcpResponse, AdcpVersionEnvelope, ProtocolEnvelope) ] status: Annotated[task_status.TaskStatus, Field(description='Current task status')] created_at: Annotated[ - AwareDatetime, Field(description='When the task was initially created (ISO 8601)') + WireDatetime, Field(description='When the task was initially created (ISO 8601)') ] updated_at: Annotated[ - AwareDatetime, Field(description='When the task was last updated (ISO 8601)') + WireDatetime, Field(description='When the task was last updated (ISO 8601)') ] completed_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the task completed (ISO 8601, only for completed/failed/canceled tasks)' ), diff --git a/src/adcp/types/domains/protocol/list_tasks_request.py b/src/adcp/types/domains/protocol/list_tasks_request.py index 7e028e47e..3c024126a 100644 --- a/src/adcp/types/domains/protocol/list_tasks_request.py +++ b/src/adcp/types/domains/protocol/list_tasks_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: protocol/list_tasks_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool from ..core import account_ref from ..core import context as context_1 @@ -62,22 +62,22 @@ class Filters(AdCPBaseModel): NoExplicitNull, ] = None created_after: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Filter tasks created after this date (ISO 8601)'), NoExplicitNull, ] = None created_before: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Filter tasks created before this date (ISO 8601)'), NoExplicitNull, ] = None updated_after: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Filter tasks last updated after this date (ISO 8601)'), NoExplicitNull, ] = None updated_before: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Filter tasks last updated before this date (ISO 8601)'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/protocol/list_tasks_response.py b/src/adcp/types/domains/protocol/list_tasks_response.py index 483a0bd97..b97580342 100644 --- a/src/adcp/types/domains/protocol/list_tasks_response.py +++ b/src/adcp/types/domains/protocol/list_tasks_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: protocol/list_tasks_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field, StrictBool from ..core import context as context_1 from ..core import ext as ext_1 @@ -100,13 +100,13 @@ class Task(AdCPBaseModel): domain: Annotated[Domain, Field(description='AdCP domain this task belongs to')] status: Annotated[task_status.TaskStatus, Field(description='Current task status')] created_at: Annotated[ - AwareDatetime, Field(description='When the task was initially created (ISO 8601)') + WireDatetime, Field(description='When the task was initially created (ISO 8601)') ] updated_at: Annotated[ - AwareDatetime, Field(description='When the task was last updated (ISO 8601)') + WireDatetime, Field(description='When the task was last updated (ISO 8601)') ] completed_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When the task completed (ISO 8601, only for completed/failed/canceled tasks)' ), diff --git a/src/adcp/types/domains/signals/get_signals_async_response_submitted.py b/src/adcp/types/domains/signals/get_signals_async_response_submitted.py index 255f65889..838ea874a 100644 --- a/src/adcp/types/domains/signals/get_signals_async_response_submitted.py +++ b/src/adcp/types/domains/signals/get_signals_async_response_submitted.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: signals/get_signals_async_response_submitted.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..core import context as context_1 from ..core import error @@ -39,7 +39,7 @@ class GetSignalsSubmitted(AdCPBaseModel): NoExplicitNull, ] = None estimated_completion: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Estimated completion time for the signal discovery task.'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/signals/get_signals_response.py b/src/adcp/types/domains/signals/get_signals_response.py index 0186bd4e7..f28cc7887 100644 --- a/src/adcp/types/domains/signals/get_signals_response.py +++ b/src/adcp/types/domains/signals/get_signals_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: signals/get_signals_response.json -# timestamp: 2026-10-21T11:25:44+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -10,9 +10,9 @@ from collections.abc import Sequence from typing import Annotated, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime, WireEmail from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, AwareDatetime, ConfigDict, EmailStr, Field, StrictBool, StrictFloat +from pydantic import ConfigDict, Field, StrictBool, StrictFloat from ..core import context as context_1 from ..core import demographic_predicate as demographic_predicate_1 @@ -66,7 +66,7 @@ class Taxonomy(AdCPBaseModel): model_config = ConfigDict( extra='forbid', ) - ref: AnyUrl + ref: WireAnyUrl version: Annotated[str | None, NoExplicitNull] = None segtax: Annotated[SchemaInt | None, Field(ge=1), NoExplicitNull] = None etag: Annotated[str | None, NoExplicitNull] = None @@ -209,8 +209,8 @@ class Channel(AdCPBaseModel): extra='forbid', ) rights: Annotated[list[Right], Field(min_length=1)] - url: Annotated[AnyUrl | None, NoExplicitNull] = None - email: Annotated[EmailStr | None, NoExplicitNull] = None + url: Annotated[WireAnyUrl | None, NoExplicitNull] = None + email: Annotated[WireEmail | None, NoExplicitNull] = None languages: Annotated[list[str] | None, NoExplicitNull] = None countries: Annotated[list[Country] | None, NoExplicitNull] = None @@ -229,7 +229,7 @@ class DataSubjectRights(AdCPBaseModel): ] = None channels: Annotated[list[Channel], Field(min_length=1)] response_sla_days: Annotated[SchemaInt | None, Field(ge=1, le=90), NoExplicitNull] = None - ccpa_opt_out_url: Annotated[AnyUrl | None, NoExplicitNull] = None + ccpa_opt_out_url: Annotated[WireAnyUrl | None, NoExplicitNull] = None class Scope(StrEnum): @@ -375,14 +375,14 @@ class Signal(AdCPBaseModel): NoExplicitNull, ] = None methodology_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description='Optional link to published methodology, media-kit, or data documentation. For data_provider and signal_source refs, this SHOULD match or supplement the referenced definition.' ), NoExplicitNull, ] = None last_updated: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When this definition record was last updated. This indicates freshness of the definition record, not an attestation that the underlying data or model was refreshed at that time.' ), @@ -413,7 +413,7 @@ class Signal(AdCPBaseModel): NoExplicitNull, ] = None segmentation_criteria: Annotated[str | None, Field(max_length=500), NoExplicitNull] = None - criteria_url: Annotated[AnyUrl | None, NoExplicitNull] = None + criteria_url: Annotated[WireAnyUrl | None, NoExplicitNull] = None data_sources: Annotated[list[DataSource] | None, Field(min_length=1), NoExplicitNull] = None methodology: Annotated[Methodology | None, NoExplicitNull] = None audience_expansion: Annotated[StrictBool | None, NoExplicitNull] = None diff --git a/src/adcp/types/domains/sponsored_intelligence/si_get_offering_response.py b/src/adcp/types/domains/sponsored_intelligence/si_get_offering_response.py index e09183a07..34d38180e 100644 --- a/src/adcp/types/domains/sponsored_intelligence/si_get_offering_response.py +++ b/src/adcp/types/domains/sponsored_intelligence/si_get_offering_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: sponsored_intelligence/si_get_offering_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -8,8 +8,8 @@ from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictBool from ..core import context as context_1 from ..core import error @@ -35,7 +35,7 @@ class Offering(AdCPBaseModel): str | None, Field(description='Short promotional tagline'), NoExplicitNull ] = None expires_at: Annotated[ - AwareDatetime | None, Field(description='When this offering expires'), NoExplicitNull + WireDatetime | None, Field(description='When this offering expires'), NoExplicitNull ] = None availability_status: Annotated[ offering_availability_status.OfferingAvailabilityStatus | None, @@ -50,9 +50,9 @@ class Offering(AdCPBaseModel): NoExplicitNull, ] = None image_url: Annotated[ - AnyUrl | None, Field(description='Hero image for the offering'), NoExplicitNull + WireAnyUrl | None, Field(description='Hero image for the offering'), NoExplicitNull ] = None - landing_url: Annotated[AnyUrl | None, Field(description='Landing page URL'), NoExplicitNull] = ( + landing_url: Annotated[WireAnyUrl | None, Field(description='Landing page URL'), NoExplicitNull] = ( None ) @@ -69,7 +69,7 @@ class MatchingProduct(AdCPBaseModel): original_price: Annotated[ str | None, Field(description='Original price if on sale'), NoExplicitNull ] = None - image_url: Annotated[AnyUrl | None, Field(description='Product image'), NoExplicitNull] = None + image_url: Annotated[WireAnyUrl | None, Field(description='Product image'), NoExplicitNull] = None availability_summary: Annotated[ str | None, Field( @@ -84,7 +84,7 @@ class MatchingProduct(AdCPBaseModel): ), NoExplicitNull, ] = None - url: Annotated[AnyUrl | None, Field(description='Product detail page URL'), NoExplicitNull] = ( + url: Annotated[WireAnyUrl | None, Field(description='Product detail page URL'), NoExplicitNull] = ( None ) @@ -112,7 +112,7 @@ class SiGetOfferingResponse(AdcpResponse, AdcpVersionEnvelope, ProtocolEnvelope) NoExplicitNull, ] = None checked_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When this offering information was retrieved'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/sponsored_intelligence/si_identity.py b/src/adcp/types/domains/sponsored_intelligence/si_identity.py index a9ca5f49d..043436762 100644 --- a/src/adcp/types/domains/sponsored_intelligence/si_identity.py +++ b/src/adcp/types/domains/sponsored_intelligence/si_identity.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: sponsored_intelligence/si_identity.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, EmailStr, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime, WireEmail +from pydantic import ConfigDict, Field, StrictBool class ConsentScopeEnum(StrEnum): @@ -24,7 +24,7 @@ class PrivacyPolicyAcknowledged(AdCPBaseModel): extra='allow', ) brand_policy_url: Annotated[ - AnyUrl | None, Field(description="URL to brand's privacy policy"), NoExplicitNull + WireAnyUrl | None, Field(description="URL to brand's privacy policy"), NoExplicitNull ] = None brand_policy_version: Annotated[ str | None, Field(description='Version of policy acknowledged'), NoExplicitNull @@ -46,7 +46,7 @@ class User(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - email: Annotated[EmailStr | None, Field(description="User's email address"), NoExplicitNull] = ( + email: Annotated[WireEmail | None, Field(description="User's email address"), NoExplicitNull] = ( None ) name: Annotated[str | None, Field(description="User's display name"), NoExplicitNull] = None @@ -69,7 +69,7 @@ class SiIdentity(AdCPBaseModel): StrictBool, Field(description='Whether user consented to share identity') ] consent_timestamp: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When consent was granted (ISO 8601)'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/sponsored_intelligence/si_sponsored_context.py b/src/adcp/types/domains/sponsored_intelligence/si_sponsored_context.py index 724a90e8e..944b3cfe7 100644 --- a/src/adcp/types/domains/sponsored_intelligence/si_sponsored_context.py +++ b/src/adcp/types/domains/sponsored_intelligence/si_sponsored_context.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: sponsored_intelligence/si_sponsored_context.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictBool from ..core import brand_ref from ..core import ext as ext_1 @@ -98,7 +98,7 @@ class DeclaredBy(AdCPBaseModel): extra='allow', ) agent_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field(description='HTTPS URL of the declaring agent or service.'), NoExplicitNull, ] = None @@ -158,7 +158,7 @@ class SiSponsoredContext(AdCPBaseModel): ), ] declared_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='When this sponsored-context declaration was made.'), NoExplicitNull, ] = None diff --git a/src/adcp/types/domains/sponsored_intelligence/si_sponsored_context_receipt.py b/src/adcp/types/domains/sponsored_intelligence/si_sponsored_context_receipt.py index 10b6bc467..2d702eaa8 100644 --- a/src/adcp/types/domains/sponsored_intelligence/si_sponsored_context_receipt.py +++ b/src/adcp/types/domains/sponsored_intelligence/si_sponsored_context_receipt.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: sponsored_intelligence/si_sponsored_context_receipt.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AwareDatetime, ConfigDict, Field +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireDatetime +from pydantic import ConfigDict, Field from ..core import ext as ext_1 from . import si_context_use, si_sponsored_context @@ -64,7 +64,7 @@ class HostReceipt(AdCPBaseModel): NoExplicitNull, ] = None received_at: Annotated[ - AwareDatetime, Field(description='When the host received the sponsored context.') + WireDatetime, Field(description='When the host received the sponsored context.') ] host_surface: Annotated[ str | None, diff --git a/src/adcp/types/domains/sponsored_intelligence/si_terminate_session_response.py b/src/adcp/types/domains/sponsored_intelligence/si_terminate_session_response.py index e05c15c20..e2137b026 100644 --- a/src/adcp/types/domains/sponsored_intelligence/si_terminate_session_response.py +++ b/src/adcp/types/domains/sponsored_intelligence/si_terminate_session_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: sponsored_intelligence/si_terminate_session_response.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations @@ -9,8 +9,8 @@ from adcp.types._str_enum import StrEnum from typing import Annotated, Any -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictBool from ..core import context as context_1 from ..core import error @@ -25,7 +25,7 @@ class AcpHandoff(AdCPBaseModel): extra='allow', ) checkout_url: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( description="Brand's ACP checkout endpoint. Hosts MUST validate this is HTTPS before opening." ), @@ -46,7 +46,7 @@ class AcpHandoff(AdCPBaseModel): NoExplicitNull, ] = None expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field( description='When this handoff data expires. Hosts should initiate checkout before this time.' ), diff --git a/src/adcp/types/domains/trusted_match/context_match_request.py b/src/adcp/types/domains/trusted_match/context_match_request.py index adadb8079..abcea5797 100644 --- a/src/adcp/types/domains/trusted_match/context_match_request.py +++ b/src/adcp/types/domains/trusted_match/context_match_request.py @@ -1,16 +1,15 @@ # generated by datamodel-codegen: # filename: trusted_match/context_match_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from uuid import UUID -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireUuid from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, ConfigDict, Field +from pydantic import ConfigDict, Field from ..content_standards import artifact as artifact_1 from ..core import property_id as property_id_1 @@ -192,7 +191,7 @@ class ContextMatchRequest(AdcpVersionEnvelope): extra='forbid', ) field_schema: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( alias='$schema', description='Optional schema URI for validation. Ignored at runtime.' ), @@ -216,7 +215,7 @@ class ContextMatchRequest(AdcpVersionEnvelope): ), ] property_rid: Annotated[ - UUID, + WireUuid, Field( description='Property catalog UUID (UUID v7). Globally unique, stable identifier assigned by the property catalog. The primary key for TMP matching and property list targeting.' ), @@ -238,7 +237,7 @@ class ContextMatchRequest(AdcpVersionEnvelope): ), ] seller_agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="API endpoint URL of the seller agent issuing this request. The provider uses this to resolve the active package set it has synced for this seller; when `package_ids` is omitted, evaluation occurs against that full set. If `seller_agent_url` does not match any seller the provider has synced packages for, the provider MUST return an empty offer set — it MUST NOT fall back to another seller's active set. The value identifies the asking seller, is identical for every user on a given placement, and carries no user identity, so it neither varies the request per user nor weakens the context/identity decorrelation boundary. Compared using the AdCP URL canonicalization rules, not byte-equality — see docs/reference/url-canonicalization. Consistent with `seller_agent_url` on the identity match request, `seller_agent.agent_url` on `AvailablePackage`, and `agent_url` in `adagents.json`." ), diff --git a/src/adcp/types/domains/trusted_match/identity_match_request.py b/src/adcp/types/domains/trusted_match/identity_match_request.py index ed7540657..f094c8294 100644 --- a/src/adcp/types/domains/trusted_match/identity_match_request.py +++ b/src/adcp/types/domains/trusted_match/identity_match_request.py @@ -1,14 +1,14 @@ # generated by datamodel-codegen: # filename: trusted_match/identity_match_request.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Any, Literal -from adcp.types.base import AdCPBaseModel, NoExplicitNull -from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, StrictBool +from adcp.types.base import AdCPBaseModel, NoExplicitNull, WireAnyUrl, WireDatetime +from pydantic import ConfigDict, Field, StrictBool from ..core import brand_ref from ..core.version_envelope import AdcpVersionEnvelope @@ -128,7 +128,7 @@ class Attestation(AdCPBaseModel): ), ] expires_at: Annotated[ - AwareDatetime | None, + WireDatetime | None, Field(description='Attestation validity horizon. Receivers MUST reject when past.'), NoExplicitNull, ] = None @@ -164,7 +164,7 @@ class IdentityMatchRequest(AdcpVersionEnvelope): extra='forbid', ) field_schema: Annotated[ - AnyUrl | None, + WireAnyUrl | None, Field( alias='$schema', description='Optional schema URI for validation. Ignored at runtime.' ), @@ -188,7 +188,7 @@ class IdentityMatchRequest(AdcpVersionEnvelope): ), ] seller_agent_url: Annotated[ - AnyUrl, + WireAnyUrl, Field( description="API endpoint URL of the seller agent issuing this request. The buyer's identity-match service uses this to resolve the active package set it has registered for this seller; when `package_ids` is omitted, evaluation occurs against that full set. If `seller_agent_url` does not match any seller for which the buyer has registered active packages, the buyer MUST return an empty `eligible_package_ids` set — it MUST NOT fall back to evaluating against another seller's active set. Compared using the AdCP URL canonicalization rules, not byte-equality — see docs/reference/url-canonicalization. Consistent with `seller_agent.agent_url` on `AvailablePackage` and `agent_url` in `adagents.json`." ), diff --git a/src/adcp/types/domains/trusted_match/provider_registration.py b/src/adcp/types/domains/trusted_match/provider_registration.py index bc1fad6be..6cbb09f08 100644 --- a/src/adcp/types/domains/trusted_match/provider_registration.py +++ b/src/adcp/types/domains/trusted_match/provider_registration.py @@ -1,16 +1,15 @@ # generated by datamodel-codegen: # filename: trusted_match/provider_registration.json -# timestamp: 2026-10-21T11:05:54+00:00 +# timestamp: 2026-10-25T19:32:54+00:00 from __future__ import annotations from adcp.types._str_enum import StrEnum from typing import Annotated, Literal -from uuid import UUID -from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt +from adcp.types.base import AdCPBaseModel, NoExplicitNull, SchemaInt, WireAnyUrl, WireUuid from adcp.types._scalar import ScalarStr -from pydantic import AnyUrl, ConfigDict, Field, StrictBool, field_validator, model_validator +from pydantic import ConfigDict, Field, StrictBool, field_validator, model_validator from ..enums import uid_type @@ -53,7 +52,7 @@ class TmpProviderRegistration1(AdCPBaseModel): ), ] endpoint: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Base URL the router calls. The router appends /context for Context Match and /identity for Identity Match. MUST be HTTPS in production, validated against the canonical reserved IPv4 and IPv6 ranges, with the TCP connection pinned to the validated IP (DNS re-resolution alone is insufficient against rebinding). Publishers comparing two provider registrations for the same `endpoint` MUST canonicalize both per the AdCP URL canonicalization rules; two registrations differing only in case, default port, or path-slash collapsing are the same provider. See docs/trusted-match/specification#provider-registration-security, docs/building/by-layer/L1/security#webhook-url-validation-ssrf, and docs/reference/url-canonicalization.' ), @@ -83,7 +82,7 @@ class TmpProviderRegistration1(AdCPBaseModel): NoExplicitNull, ] = None properties: Annotated[ - list[UUID] | None, + list[WireUuid] | None, Field( description='Property RIDs (UUID v7) this provider serves. When present, the router only sends requests from these properties to this provider. When absent, the provider serves all properties.', min_length=1, @@ -127,7 +126,7 @@ class TmpProviderRegistration1(AdCPBaseModel): @field_validator('endpoint') @classmethod - def _require_https_endpoint(cls, value: AnyUrl) -> AnyUrl: + def _require_https_endpoint(cls, value: WireAnyUrl) -> WireAnyUrl: if value.scheme != 'https': raise ValueError('endpoint must use https') return value @@ -156,7 +155,7 @@ class TmpProviderRegistration2(AdCPBaseModel): ), ] endpoint: Annotated[ - AnyUrl, + WireAnyUrl, Field( description='Base URL the router calls. The router appends /context for Context Match and /identity for Identity Match. MUST be HTTPS in production, validated against the canonical reserved IPv4 and IPv6 ranges, with the TCP connection pinned to the validated IP (DNS re-resolution alone is insufficient against rebinding). Publishers comparing two provider registrations for the same `endpoint` MUST canonicalize both per the AdCP URL canonicalization rules; two registrations differing only in case, default port, or path-slash collapsing are the same provider. See docs/trusted-match/specification#provider-registration-security, docs/building/by-layer/L1/security#webhook-url-validation-ssrf, and docs/reference/url-canonicalization.' ), @@ -187,7 +186,7 @@ class TmpProviderRegistration2(AdCPBaseModel): NoExplicitNull, ] = None properties: Annotated[ - list[UUID] | None, + list[WireUuid] | None, Field( description='Property RIDs (UUID v7) this provider serves. When present, the router only sends requests from these properties to this provider. When absent, the provider serves all properties.', min_length=1, @@ -231,7 +230,7 @@ class TmpProviderRegistration2(AdCPBaseModel): @field_validator('endpoint') @classmethod - def _require_https_endpoint(cls, value: AnyUrl) -> AnyUrl: + def _require_https_endpoint(cls, value: WireAnyUrl) -> WireAnyUrl: if value.scheme != 'https': raise ValueError('endpoint must use https') return value diff --git a/src/adcp/validation/schema_loader.py b/src/adcp/validation/schema_loader.py index 9e956c807..1ba5c3a1e 100644 --- a/src/adcp/validation/schema_loader.py +++ b/src/adcp/validation/schema_loader.py @@ -32,15 +32,15 @@ import warnings from copy import deepcopy from dataclasses import dataclass -from datetime import date from functools import lru_cache from importlib.resources import as_file, files from pathlib import Path from typing import Any, Literal, cast -from urllib.parse import quote, unquote, urldefrag, urljoin, urlparse, urlsplit +from urllib.parse import unquote, urldefrag, urljoin, urlparse, urlsplit from pydantic import AnyUrl, TypeAdapter, ValidationError +from adcp import _formats from adcp._deferred_adapters import deferred_adapter from adcp.validation.version import resolve_bundle_key @@ -67,13 +67,10 @@ def _uri_adapter() -> TypeAdapter[AnyUrl]: Direction = Literal["request", "sync", "submitted", "working", "input-required"] -_RFC3339_DATE_TIME = re.compile( - r"^\d{4}-\d{2}-\d{2}[Tt]" - r"(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d" - r"(?:\.\d+)?" - r"(?:[Zz]|[+-](?:[01]\d|2[0-3]):[0-5]\d)$", - re.ASCII, -) +# The strict ``date-time`` grammar lives in ``adcp._formats`` so the wire- +# preserving types (``adcp.types._wire``) apply the same check before keeping +# a string's spelling; the names below are what this module always exported. +_RFC3339_DATE_TIME = _formats.RFC3339_DATE_TIME def _is_rfc3339_date_time(instance: Any) -> bool: @@ -87,17 +84,7 @@ def _is_rfc3339_date_time(instance: Any) -> bool: """ if not isinstance(instance, str): return True - if _RFC3339_DATE_TIME.fullmatch(instance) is None: - return False - try: - # The grammar already checks time and offset ranges. Validate only - # the calendar here: Python 3.10's datetime parser accepts only three - # or six fractional digits, whereas RFC 3339 permits any positive - # number. Validation must neither coerce nor truncate the wire value. - date.fromisoformat(instance[:10]) - except ValueError: - return False - return True + return _formats.is_rfc3339_date_time(instance) # Hostname grammar from RFC 1123 section 2.1: dot-separated labels of @@ -115,30 +102,16 @@ def _is_rfc3339_date_time(instance: Any) -> bool: def _is_uri(instance: Any) -> bool: """Validate URI syntax while retaining AnyUrl's internationalized URLs. - Check the original syntax before applying the ``pydantic.AnyUrl`` parser - used by generated models. AnyUrl alone accepts malformed percent escapes - and normalizes whitespace/backslashes; that would bypass the explicit - ad-server-token branch in macro-bearing-url. Retaining its check also - prevents values passing schema validation but failing model validation. + Check the original syntax (``adcp._formats.is_uri_syntax``) before applying + the ``pydantic.AnyUrl`` parser used by generated models. AnyUrl alone + accepts malformed percent escapes and normalizes whitespace/backslashes; + that would bypass the explicit ad-server-token branch in + macro-bearing-url. Retaining its check also prevents values passing schema + validation but failing model validation. """ if not isinstance(instance, str): return True - from rfc3986_validator import validate_rfc3986 - - if any(char.isspace() or 0x7F <= ord(char) <= 0x9F for char in instance): - return False - try: - # Escape only non-ASCII characters for the RFC 3986 syntax check. - # Leave original ASCII untouched so normalization cannot hide malformed - # percent escapes or backslashes. AnyUrl still checks IDN validity. - syntax = ( - instance - if instance.isascii() - else "".join(char if char.isascii() else quote(char, safe="") for char in instance) - ) - except UnicodeEncodeError: - return False - if not validate_rfc3986(syntax): + if not _formats.is_uri_syntax(instance): return False try: _uri_adapter().validate_python(instance) diff --git a/tests/conformance/_round_trip.py b/tests/conformance/_round_trip.py new file mode 100644 index 000000000..28d156a2e --- /dev/null +++ b/tests/conformance/_round_trip.py @@ -0,0 +1,815 @@ +"""Support for the wire round-trip suite. + +A schema-valid wire document that passes through a generated model — +``Model.model_validate_json(doc).model_dump(mode="json")`` with the SDK's +default dump settings — must come back as the same JSON value. AdCP binds +bytes to that value: ``content_digest`` is SHA-256 over the RFC 8785 (JCS) +form of an object's declared fields, a ``context`` is "echoed unchanged", +and a buyer verifies a seller's digest over what it received. A model that +rewrites a field on the way through breaks every one of those contracts +without failing anything (#1610). + +This module builds the corpus and names what changed: + +* :func:`documents` collects schema-valid documents — every top-level + ``examples`` entry in the pinned bundle, a synthesized request for every + tool, and the checked-in differential fixtures — and :func:`variants` + derives from each of them the forms a normalizing type rewrites (a bare + URL origin, a ``+00:00`` offset with ``.000`` fractional seconds, an + upper-case UUID, ...), keeping only the ones the bundled validator accepts; +* :func:`round_trip` runs one document through its model and returns every + leaf-level :class:`Difference`, each with a :data:`CLASSES` name; +* :data:`DIGEST_COVERED` and :data:`ECHO_COVERED` list the schemas whose + bytes the protocol binds, where no difference of any class is acceptable. + +Comparison is under JCS, so key order and number formatting (``1`` vs +``1.0``) are already normalized away; what remains is a real rewrite. +""" + +from __future__ import annotations + +import copy +import importlib +import json +import re +from collections.abc import Iterator +from dataclasses import dataclass, field +from functools import cache +from pathlib import Path +from types import UnionType +from typing import Annotated, Any, Union, get_origin + +import rfc8785 +from pydantic import TypeAdapter, ValidationError + +import adcp.types._eager # noqa: F401 (see below) +from adcp.validation.schema_loader import ( + _ensure_state, + _make_ref_resolver, + get_named_validator, +) +from tests.conformance import _schema_parity as parity + +# The models graded here are the ones adopters get: ``adcp.types._eager`` runs +# the import-time patchers (``_ergonomic``, ``_forward_compat``) that add +# validators and coercions to the generated classes. Importing a domain module +# directly before that has happened grades an unpatched class, and the verdict +# then depends on test order (a mutual-exclusivity validator on +# ``PublisherPropertySelector`` refused a document in CI's full session that an +# isolated run accepted). + +REPO_ROOT = Path(__file__).resolve().parents[2] + +# --------------------------------------------------------------------------- +# Schemas whose bytes the protocol binds. +# --------------------------------------------------------------------------- + +#: Schemas that carry a digest or signature computed over their own declared +#: fields (or over a declared sub-object), read off the bundle's descriptions +#: at pin 3.2.4. A model for any of these must dump exactly what it parsed: +#: ``digest(model.model_dump(mode="json")) == model.``. +DIGEST_COVERED: frozenset[str] = frozenset( + { + # content_digest = SHA-256(JCS(entry minus content_digest, attestation_refs)) + "core/audience-evidence.json", + # The compact snapshot carries the same content_digest; a pin and a + # selection repeat it and a seller MUST match all four identity fields. + "core/canonical-audience-evidence.json", + "core/audience-evidence-pin.json", + "core/audience-evidence-selection.json", + "core/canonical-audience-evidence-selection.json", + # content_digest = SHA-256(JCS(rights_constraint minus content_digest, + # attestation_refs, verification_url)) + "core/rights-constraint.json", + "core/rights-attestation-evaluation.json", + # revision_content_digest = SHA-256(JCS(set minus $schema, creative_id, + # revision_id, revision_content_digest, name)) + "core/creative-representation-set.json", + # selected_output_digest is computed over the derived CreativeManifest + # minus a fixed exclusion list; the manifest is the digested object. + "core/representation-selection.json", + "core/creative-manifest.json", + # content_digest = SHA-256(JCS(profile minus content_digest)) + "media-buy/acceptance-policy-profile.json", + # terms_digest = SHA-256(JCS(commercial_terms)) + "core/canonical-proposal.json", + "media-buy/commercial-terms.json", + # tracker_execution_contract_digest = SHA-256(JCS(tracker_execution_contract)); + # product_snapshot_digest over the snapshot's product-binding preimage. + "core/package-format-snapshot.json", + # reference_digest = SHA-256(JCS(complete AttestationReference)) + "core/attestation-reference.json", + "core/attestation-evaluation.json", + # statement_digest = SHA-256(JCS({seller_reference, delivery_metrics})) + "governance/check-governance-request.json", + # runtime_attestation_binding_digest = SHA-256(JCS({evaluations, findings})) + "governance/check-governance-response.json", + # plan_hash = SHA-256(JCS(plan_payload)); the governance_context JWS + # carries runtime_attestation_binding_digest. + "governance/get-plan-audit-logs-response.json", + } +) + +#: Objects the protocol says are carried through unchanged: opaque buyer +#: data every response echoes, and the extension object. A model that holds +#: one must return the bytes it was given. +ECHO_COVERED: frozenset[str] = frozenset( + { + "core/context.json", + "core/ext.json", + } +) + +# --------------------------------------------------------------------------- +# Naming a difference. +# --------------------------------------------------------------------------- + +#: Every kind of rewrite the sweep distinguishes, with what produces it. +CLASSES: dict[str, str] = { + "url_normalized": ( + "a format: uri field is parsed as pydantic AnyUrl, which re-serializes " + "the URL it parsed (trailing slash on a bare origin, lower-cased host " + "and scheme, dropped default port, percent-encoding)" + ), + "datetime_reformatted": ( + "a format: date-time field is parsed as AwareDatetime and re-serialized " + "in pydantic's form (Z for +00:00, fractional seconds dropped when zero " + "or truncated to microseconds)" + ), + "date_reformatted": "a format: date field is re-serialized in a different form", + "uuid_normalized": "a format: uuid field is parsed as UUID and re-serialized lower-case", + "email_normalized": ( + "a format: email field is parsed as EmailStr, which lower-cases the domain" + ), + "alias_emitted": ( + "the dump writes the Python field name where the wire has a name that is " + "not an identifier ($schema, from, pass): model_dump() without by_alias" + ), + "default_emitted": ( + "the model emits a field the document omitted: a schema default the " + "generator wrote into the field, or a value the model computes" + ), + "null_dropped": ("an explicit null the schema admits is dropped by the dump's exclude_none"), + "field_dropped": ( + "a property the document carried is absent after the round trip: an " + "unknown field dropped by extra='ignore', or a union arm that does not " + "carry it" + ), + "value_rewritten": "a scalar value changed in a way no other class explains", + "shape_changed": "a value changed JSON type (object, array, scalar)", +} + + +@dataclass(frozen=True) +class Difference: + """One leaf the round trip changed.""" + + schema: str + document: str + pointer: str + kind: str + before: Any + after: Any + + def describe(self) -> str: + where = f"{self.schema} [{self.document}] {self.pointer or '/'}" + return f"{where}: {self.kind} {self.before!r} -> {self.after!r}" + + +@dataclass(frozen=True) +class Refusal: + """A schema-valid document the model does not accept.""" + + schema: str + document: str + error_types: frozenset[str] + message: str + + +@dataclass +class Document: + """A schema-valid wire document and the schema it is valid under.""" + + schema: str + name: str + payload: Any + #: The tool name, when the document is a task request. + tool: str | None = None + origin: str = "example" + extra: dict[str, Any] = field(default_factory=dict) + + +_URL = re.compile(r"^[A-Za-z][A-Za-z0-9+.-]*://\S+$") +_DATE_TIME = re.compile(r"^\d{4}-\d{2}-\d{2}[Tt ]\d{2}:\d{2}:\d{2}(\.\d+)?([Zz]|[+-]\d{2}:\d{2})$") +_DATE = re.compile(r"^\d{4}-\d{2}-\d{2}$") +_UUID = re.compile(r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$") +_EMAIL = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$") + + +def _canonical(value: Any) -> bytes: + return rfc8785.dumps(value) + + +def classify_change(before: Any, after: Any) -> str: + """Name the class of a changed leaf.""" + if isinstance(before, str) and isinstance(after, str): + if _URL.match(before): + return "url_normalized" + if _DATE_TIME.match(before): + return "datetime_reformatted" + if _DATE.match(before): + return "date_reformatted" + if _UUID.match(before): + return "uuid_normalized" + if _EMAIL.match(before): + return "email_normalized" + return "value_rewritten" + if isinstance(before, (dict, list)) or isinstance(after, (dict, list)): + return "shape_changed" + if type(before) is not type(after) and not ( + isinstance(before, (int, float)) and isinstance(after, (int, float)) + ): + return "shape_changed" + return "value_rewritten" + + +def differences(schema: str, name: str, payload: Any, dumped: Any) -> list[Difference]: + """Every leaf-level difference between ``payload`` and ``dumped`` under JCS. + + Walks both values together. A property present on one side only is + reported once, at the property, not once per descendant; a property whose + Python field name was emitted instead of its wire alias (``field_schema`` + for ``$schema``) is reported as one ``alias_emitted`` difference. + """ + if _canonical(payload) == _canonical(dumped): + return [] + found: list[Difference] = [] + _walk(schema, name, "", payload, dumped, found) + return found + + +def _walk( + schema: str, name: str, pointer: str, before: Any, after: Any, found: list[Difference] +) -> None: + if isinstance(before, dict) and isinstance(after, dict): + gone = {key for key in before if key not in after} + new = {key for key in after if key not in before} + for key in sorted(gone): + alias = _ALIASES.get(key) + if alias is not None and alias in new: + new.discard(alias) + found.append( + Difference(schema, name, f"{pointer}/{key}", "alias_emitted", key, alias) + ) + _walk(schema, name, f"{pointer}/{key}", before[key], after[alias], found) + continue + kind = "null_dropped" if before[key] is None else "field_dropped" + found.append(Difference(schema, name, f"{pointer}/{key}", kind, before[key], None)) + for key in sorted(new): + found.append( + Difference(schema, name, f"{pointer}/{key}", "default_emitted", None, after[key]) + ) + for key in before: + if key in after: + _walk(schema, name, f"{pointer}/{key}", before[key], after[key], found) + return + if isinstance(before, list) and isinstance(after, list): + if len(before) != len(after): + found.append(Difference(schema, name, pointer, "shape_changed", before, after)) + return + for index, (left, right) in enumerate(zip(before, after)): + _walk(schema, name, f"{pointer}/{index}", left, right, found) + return + if _canonical(before) != _canonical(after): + found.append( + Difference(schema, name, pointer, classify_change(before, after), before, after) + ) + + +#: Wire names the generator cannot use as Python identifiers, with the field +#: name it emits instead when ``model_dump`` runs without ``by_alias``. +_ALIASES: dict[str, str] = { + "$schema": "field_schema", + "pass": "pass_", + "from": "from_", + "media-buy": "media_buy", + "_truncation": "field_truncation", + "2xl": "field_2xl", +} + + +# --------------------------------------------------------------------------- +# Locating the model for a schema. +# --------------------------------------------------------------------------- + + +def _snake(text: str) -> str: + return re.sub(r"[^0-9a-zA-Z]+", "_", text).strip("_").lower() + + +def _camel(text: str) -> str: + return "".join(part[:1].upper() + part[1:] for part in re.split(r"[^0-9a-zA-Z]+", text) if part) + + +@cache +def schema_root() -> Path: + state = _ensure_state(None) + assert state is not None, "the pinned schema bundle is not on disk" + return state.root.root + + +def _request_model_for(tool: str) -> Any: + import adcp.types as types + + camel = "".join(part.capitalize() for part in tool.split("_")) + for name in (f"{camel}Request", camel): + model = getattr(types, name, None) + if model is not None and hasattr(model, "model_validate"): + return model + return None + + +@cache +def model_for(schema: str) -> Any: + """The generated class for a bundle schema path, or ``None``. + + The generator writes one module per schema file at + ``adcp.types.domains..`` and names the root class after + the schema's ``title`` (falling back to the file stem). + """ + rel = Path(schema) + if rel.parts[0] in ("bundled", "mcp") or len(rel.parts) == 1: + return None + module_name = ( + "adcp.types.domains." + ".".join(_snake(p) for p in rel.parts[:-1]) + "." + _snake(rel.stem) + ) + try: + module = importlib.import_module(module_name) + except ImportError: + return None + document = json.loads((schema_root() / rel).read_text()) + candidates = [_camel(document.get("title", "")), _camel(rel.stem)] + by_lower = {name.lower(): obj for name, obj in vars(module).items()} + for name in candidates: + found = getattr(module, name, None) + if found is None: + found = by_lower.get(name.lower()) + if found is None: + continue + if hasattr(found, "model_validate_json"): + return found + if get_origin(found) in (Annotated, Union, UnionType): + # A schema whose root is a oneOf/anyOf is exposed as a type alias + # over its arms (post_generate_fixes.unwrap_rootmodel_unions). + return TypeAdapter(found) + return None + + +def parse_json(grader: Any, text: str) -> Any: + if isinstance(grader, TypeAdapter): + return grader.validate_json(text) + return grader.model_validate_json(text) + + +def dump_json(grader: Any, parsed: Any) -> Any: + """The SDK's default dump: ``model_dump(mode="json")`` as AdCPBaseModel defines it.""" + if hasattr(parsed, "model_dump"): + # A union arm is an AdCP model: dump it the way the SDK does. + return parsed.model_dump(mode="json") + return grader.dump_python(parsed, mode="json", exclude_none=True) + + +def dump_as_received(grader: Any, parsed: Any) -> Any: + """``AdCPBaseModel.model_dump_as_received``: only what the document carried. + + ``exclude_unset`` leaves off every field the document did not set, which + is where a generated default would otherwise appear; ``exclude_none=False`` + keeps an explicit ``null`` the schema admits (a field the schema refuses + ``null`` on is left off by its own ``NoExplicitNull`` exclusion either + way); ``by_alias`` writes the wire name. A union alias root gets the same + settings through its adapter. + """ + if hasattr(parsed, "model_dump_as_received"): + return parsed.model_dump_as_received() + kwargs: dict[str, Any] = dict( + mode="json", exclude_unset=True, exclude_none=False, by_alias=True + ) + if hasattr(parsed, "model_dump"): + # A RootModel over a union: pydantic's own dump with the same settings. + return parsed.model_dump(**kwargs) + return grader.dump_python(parsed, **kwargs) + + +# --------------------------------------------------------------------------- +# The corpus. +# --------------------------------------------------------------------------- + + +def _bundle_schemas() -> Iterator[Path]: + root = schema_root() + for file in sorted(root.rglob("*.json")): + rel = file.relative_to(root) + if rel.parts[0] in ("bundled", "mcp") or len(rel.parts) == 1: + continue + yield rel + + +def is_valid(schema: str, payload: Any) -> bool: + validator = get_named_validator(schema) + return validator is not None and validator.is_valid(payload) + + +@cache +def synthesize_named(schema: str) -> Any: + """A schema-valid document for any bundle schema, or ``None``. + + :func:`_schema_parity.synthesize` instantiates a tool's request schema + with the request validator as the oracle; this does the same for a named + schema with :func:`get_named_validator`, then grows the document with + every optional property that keeps it valid. + """ + validator = get_named_validator(schema) + if validator is None: + return None + state = _ensure_state(None) + assert state is not None + file = state.root.root / schema + node = json.loads(file.read_text()) + resolver = _make_ref_resolver(state, file, node) + try: + candidates = parity.instances(node, resolver, 0, 64) + except parity.UnsynthesizableError: + return None + for candidate in candidates: + if validator.is_valid(candidate): + return _grow_named(validator, candidate, node, resolver) + return None + + +def _grow_named(validator: Any, payload: Any, schema: Any, resolver: Any) -> Any: + if not isinstance(payload, dict): + return payload + + def walk(node: Any, obj: Any, depth: int = 0) -> None: + if depth > 5 or not isinstance(obj, dict) or not isinstance(node, dict): + return + if "$ref" in node: + with resolver.resolving(node["$ref"]) as target: + walk(target, obj, depth) + return + for keyword in ("allOf", "oneOf", "anyOf"): + for member in node.get(keyword) or []: + walk(member, obj, depth) + properties = node.get("properties") or {} + for name, sub in properties.items(): + if name in obj: + continue + try: + value = parity.instances(sub, resolver, depth + 1, 1)[0] + except parity.UnsynthesizableError: + continue + obj[name] = value + if not validator.is_valid(payload): + del obj[name] + for name, sub in properties.items(): + if isinstance(obj.get(name), dict): + walk(sub, obj[name], depth + 1) + + walk(schema, payload) + return payload + + +def _merge(base: Any, over: Any) -> Any: + if isinstance(base, dict) and isinstance(over, dict): + merged = dict(base) + for key, value in over.items(): + merged[key] = _merge(merged[key], value) if key in merged else value + return merged + return over + + +def complete_example(schema: str, example: Any) -> Any: + """``example`` as the bundle wrote it if valid, else completed over a synthesized base. + + Most bundle examples are fragments: they illustrate a few properties and + omit the required ones. Laying the fragment over a synthesized valid + document keeps every byte the example chose and fills in only what the + schema demands; the validator decides whether the result counts. + """ + if is_valid(schema, example): + return example + base = synthesize_named(schema) + if base is None or not isinstance(base, dict) or not isinstance(example, dict): + return None + candidate = _merge(copy.deepcopy(base), example) + return candidate if is_valid(schema, candidate) else None + + +def bundle_examples() -> Iterator[Document]: + """Every top-level ``examples`` entry, as written or completed to validity.""" + root = schema_root() + for rel in _bundle_schemas(): + document = json.loads((root / rel).read_text()) + examples = document.get("examples") if isinstance(document, dict) else None + if not isinstance(examples, list): + continue + for index, example in enumerate(examples): + if not isinstance(example, (dict, list)): + continue + payload = complete_example(rel.as_posix(), example) + if payload is not None: + origin = "example" if payload is example else "completed-example" + yield Document(rel.as_posix(), f"examples[{index}]", payload, origin=origin) + + +def named_documents() -> Iterator[Document]: + """One synthesized document for every bundle schema that has a model.""" + for rel in _bundle_schemas(): + schema = rel.as_posix() + if model_for(schema) is None: + continue + payload = synthesize_named(schema) + if payload is not None: + yield Document(schema, "synthesized", copy.deepcopy(payload), origin="synthesized") + + +def tool_requests() -> Iterator[Document]: + """A synthesized schema-valid request for every tool the SDK models.""" + state = _ensure_state(None) + assert state is not None + for tool in parity.request_tools(): + file = state.file_index[(tool, "request")] + if file.is_relative_to(state.root.bundled): + continue + try: + payload = parity.synthesize(tool) + except parity.UnsynthesizableError: + continue + rel = file.relative_to(state.root.root).as_posix() + yield Document(rel, f"synthesized:{tool}", payload, tool=tool, origin="synthesized") + + +def fixture_requests() -> Iterator[Document]: + """The checked-in differential fixtures the bundled schema accepts.""" + state = _ensure_state(None) + assert state is not None + cases = json.loads( + (REPO_ROOT / "tests" / "fixtures" / "validation-parity-cases.json").read_text() + ) + for case in cases: + if not case.get("canonical_valid") or case.get("direction") != "request": + continue + file = state.file_index.get((case["tool"], "request")) + if file is None or file.is_relative_to(state.root.bundled): + continue + rel = file.relative_to(state.root.root).as_posix() + yield Document( + rel, f"fixture:{case['id']}", case["payload"], tool=case["tool"], origin="fixture" + ) + + +#: Hand-written documents for the bound schemas the instantiator cannot derive +#: from the schema alone (a ``oneOf`` on ``audience.value``, a ``required`` set +#: inside an ``allOf`` branch) and for the opaque echo objects. Each carries +#: a digest computed over its own JCS form where the schema declares one, a +#: bare-origin URL, a ``+00:00`` offset with ``.000`` fractional seconds, a +#: mixed-case host, and -- for the opaque objects -- nested ``null`` values: +#: the forms a normalizing type or an ``exclude_none`` dump rewrites. +FIXTURE_CORPUS = REPO_ROOT / "tests" / "fixtures" / "round_trip" / "corpus.json" + + +def fixture_documents() -> Iterator[Document]: + """The checked-in digest-covered corpus, each confirmed valid by the bundled validator.""" + corpus = json.loads(FIXTURE_CORPUS.read_text()) + for schema, payload in corpus.items(): + assert is_valid(schema, payload), f"{FIXTURE_CORPUS.name}: {schema} is not schema-valid" + yield Document(schema, "fixture:corpus", payload, origin="fixture") + + +def documents() -> list[Document]: + return [ + *bundle_examples(), + *named_documents(), + *tool_requests(), + *fixture_requests(), + *fixture_documents(), + ] + + +# --------------------------------------------------------------------------- +# Variants: the forms a normalizing type rewrites. +# --------------------------------------------------------------------------- + +#: Substitutions tried at every leaf whose value has the matching shape. Each +#: is schema-valid wherever the original was, which the validator confirms +#: before the variant is kept; each is a form a normalizing parser rewrites. +URL_VARIANTS = ( + "https://example.com", # bare origin: AnyUrl appends "/" + "https://Example.COM/a", # host case: AnyUrl lower-cases + "HTTPS://example.com/a", # scheme case: AnyUrl lower-cases + "https://example.com:443/a", # default port: AnyUrl drops it + "https://example.com/a%2Fb", # percent-encoding: kept or re-encoded + "https://example.com/a?b=1&c=2#frag", + "https://example.com/a/", +) +DATE_TIME_VARIANTS = ( + "2026-10-01T00:00:00.000+00:00", # +00:00 and .000: pydantic writes Z, drops .000 + "2026-10-01T00:00:00+00:00", + "2026-10-01T00:00:00-05:00", + "2026-10-01T00:00:00.5Z", + "2026-10-01T00:00:00.123456789Z", # nanoseconds: pydantic keeps microseconds + "2026-10-01t00:00:00z", # RFC 3339 permits lower-case T and Z +) +UUID_VARIANTS = ("00000000-0000-4000-8000-00000000ABCD",) +EMAIL_VARIANTS = ("Ops@Example.COM",) # EmailStr lower-cases the domain + + +def _shape_variants(value: Any) -> tuple[Any, ...]: + if not isinstance(value, str): + return () + if _URL.match(value): + return URL_VARIANTS + if _DATE_TIME.match(value): + return DATE_TIME_VARIANTS + if _UUID.match(value): + return UUID_VARIANTS + if _EMAIL.match(value): + return EMAIL_VARIANTS + return () + + +def variants(document: Document, *, limit_per_pointer: int | None = None) -> Iterator[Document]: + """Schema-valid variants of ``document`` exercising each risky form. + + One leaf is replaced at a time, so a difference names the leaf that caused + it. A leaf is only varied once per form, at the first pointer of each + shape, because every ``format: uri`` field in one document is typed the + same way and varying all of them multiplies runtime without adding + coverage — except that a form may be valid at one field and not another, + so each pointer is still tried until one accepts the form. + """ + accepted: set[tuple[str, Any]] = set() + for pointer, value in parity.leaves(document.payload): + for substitute in _shape_variants(value): + key = (_shape_key(value), substitute) + if key in accepted: + continue + candidate = copy.deepcopy(document.payload) + parity.replace_at(candidate, pointer, substitute) + if not is_valid(document.schema, candidate): + continue + accepted.add(key) + yield Document( + document.schema, + f"{document.name}{pointer}={substitute}", + candidate, + tool=document.tool, + origin="variant", + extra={"pointer": pointer, "form": substitute}, + ) + + +def _shape_key(value: str) -> str: + if _URL.match(value): + return "url" + if _DATE_TIME.match(value): + return "date-time" + if _UUID.match(value): + return "uuid" + return "email" + + +# --------------------------------------------------------------------------- +# The round trip. +# --------------------------------------------------------------------------- + + +def round_trip( + document: Document, *, as_received: bool = False +) -> tuple[list[Difference], Refusal | None]: + """Run ``document`` through its model; return what changed, or why it was refused. + + ``as_received`` dumps with :func:`dump_as_received` instead of the SDK's + default settings. + """ + model = grader_for(document) + if model is None: + return [], None + try: + parsed = parse_json(model, json.dumps(document.payload)) + except ValidationError as exc: + return [], Refusal( + document.schema, + document.name, + frozenset(error["type"] for error in exc.errors()), + "; ".join(f"{'/'.join(map(str, e['loc']))}: {e['msg']}" for e in exc.errors()[:3]), + ) + except (TypeError, ValueError) as exc: + # A model whose validator raises rather than refuses is a defect of + # its own; the sweep records it and moves on. + return [], Refusal( + document.schema, document.name, frozenset({type(exc).__name__}), str(exc)[:200] + ) + dumped = dump_json(model, parsed) if not as_received else dump_as_received(model, parsed) + return differences(document.schema, document.name, document.payload, dumped), None + + +def grader_for(document: Document) -> Any: + if document.tool is not None: + model = _request_model_for(document.tool) + if model is not None: + return model + return model_for(document.schema) + + +def has_model(document: Document) -> bool: + return grader_for(document) is not None + + +def _resolver_for(schema: str) -> Any: + state = _ensure_state(None) + assert state is not None + file = state.root.root / schema + return _make_ref_resolver(state, file, json.loads(file.read_text())) + + +if __name__ == "__main__": # pragma: no cover - exploratory report + import sys + from collections import Counter, defaultdict + + include_variants = "--variants" in sys.argv + per_class: Counter[str] = Counter() + distinct: dict[str, set[tuple[str, str]]] = defaultdict(set) + received_distinct: dict[str, set[tuple[str, str]]] = defaultdict(set) + received_class: Counter[str] = Counter() + per_schema: dict[str, Counter[str]] = defaultdict(Counter) + received_schema: dict[str, Counter[str]] = defaultdict(Counter) + samples: dict[str, list[Difference]] = defaultdict(list) + schema_samples: dict[tuple[str, str], Difference] = {} + refusals: list[Refusal] = [] + unmodelled: set[str] = set() + graded = 0 + origins: Counter[str] = Counter() + for base in documents(): + origins[base.origin] += 1 + docs = [base, *(variants(base) if include_variants else [])] + for doc in docs: + if not has_model(doc): + unmodelled.add(doc.schema) + continue + graded += 1 + diffs, refusal = round_trip(doc) + if refusal is not None: + refusals.append(refusal) + continue + for d in diffs: + per_class[d.kind] += 1 + distinct[d.kind].add((d.schema, re.sub(r"/\d+(?=/|$)", "/*", d.pointer))) + per_schema[d.schema][d.kind] += 1 + schema_samples.setdefault((d.schema, d.kind), d) + if len(samples[d.kind]) < 8: + samples[d.kind].append(d) + for d in round_trip(doc, as_received=True)[0]: + received_class[d.kind] += 1 + received_distinct[d.kind].add((d.schema, re.sub(r"/\d+(?=/|$)", "/*", d.pointer))) + received_schema[d.schema][d.kind] += 1 + schema_samples.setdefault((d.schema, "received:" + d.kind), d) + print( + f"graded documents: {graded}; unmodelled schemas: {len(unmodelled)}; " + f"refusals: {len(refusals)}; origins: {dict(origins)}" + ) + print("\n== per class ==") + for kind, count in per_class.most_common(): + fields = distinct[kind] + schemas = {schema for schema, _ in fields} + print(f" {kind:22} {count:6} hits; {len(fields):5} fields; {len(schemas):4} schemas") + print("\n== per class, dumping as received (exclude_unset, nulls kept, by_alias) ==") + for kind, count in received_class.most_common(): + fields = received_distinct[kind] + schemas = {schema for schema, _ in fields} + print(f" {kind:22} {count:6} hits; {len(fields):5} fields; {len(schemas):4} schemas") + print("\n== digest/echo-covered schemas with differences ==") + for schema in sorted(per_schema): + if schema in DIGEST_COVERED or schema in ECHO_COVERED: + print(f" {schema}: {dict(per_schema[schema])}") + print("\n== all schemas with differences ==") + for schema in sorted(per_schema): + print(f" {schema}: {dict(per_schema[schema])}") + print("\n== digest/echo-covered schemas with differences, as received ==") + for schema in sorted(received_schema): + if schema in DIGEST_COVERED or schema in ECHO_COVERED: + print(f" {schema}: {dict(received_schema[schema])}") + print("\n== one sample per (schema, class) ==") + for (schema, kind), d in sorted(schema_samples.items()): + print(f" {schema} [{kind}] {d.pointer}: {d.before!r} -> {d.after!r}"[:200]) + print("\n== samples ==") + for kind, items in samples.items(): + print(f" [{kind}]") + for d in items: + print(" " + d.describe()[:220]) + print("\n== refusals ==") + for r in refusals[:40]: + print(f" {r.schema} [{r.document}] {sorted(r.error_types)} {r.message[:160]}") + print("\n== unmodelled ==") + for s in sorted(unmodelled): + print(" " + s) diff --git a/tests/conformance/test_wire_round_trip.py b/tests/conformance/test_wire_round_trip.py new file mode 100644 index 000000000..da69824b2 --- /dev/null +++ b/tests/conformance/test_wire_round_trip.py @@ -0,0 +1,252 @@ +"""A schema-valid wire document survives parse → dump unchanged. + +AdCP binds bytes to several objects: ``content_digest`` is SHA-256 over the +RFC 8785 form of an entry's declared fields, a ``context`` is "echoed +unchanged", and a buyer verifies a seller's digest over what it received. A +generated model that rewrites a value on the way through breaks those +contracts without failing anything, which is how #1610 shipped: ``AnyUrl`` +appended a slash to a bare origin and ``AwareDatetime`` rewrote ``+00:00`` as +``Z``, and a parsed-then-dumped ``AudienceEvidence`` no longer matched its own +digest. #1384 (``agent_url``), #1449 (defaults added to a published format), +#1587 (nested nulls) and #1575 (context null keys) were earlier instances of +the same missing invariant, each fixed at one field or one object. + +This suite states the invariant and measures it. For every bundle schema with +a model it takes schema-valid documents — the bundle's ``examples``, a +synthesized document, the differential fixtures, and a checked-in corpus for +the digest-covered schemas — derives variants that exercise each form a +normalizing parser rewrites, runs each through +``Model.model_validate_json(doc).model_dump(mode="json")`` with the SDK's +default settings, and compares the result to the input under JCS. + +Two tiers. A schema whose bytes the protocol binds +(:data:`_round_trip.DIGEST_COVERED`, :data:`_round_trip.ECHO_COVERED`) must +round-trip with no difference of any class. Every other schema may show only +the classes named in :data:`ACCEPTED`, each with the reason it remains and the +issue that tracks it; the set only shrinks, and a class that stops occurring +must be deleted from it. A class outside ``ACCEPTED`` — a rewritten URL, +datetime, UUID or email, a changed value, a changed shape — fails anywhere. +""" + +from __future__ import annotations + +from collections import defaultdict +from functools import cache + +import pytest + +from tests.conformance import _round_trip as rt + +#: The classes of difference this SDK still produces under its default dump +#: settings, each with the reason it is here. A difference of any other class +#: is a regression. A class that stops occurring is deleted — this set only +#: shrinks. +ACCEPTED: dict[str, str] = { + # The generator writes a schema ``default`` into the field and the dump + # emits every field, so a document that omitted a defaulted property gains + # it. "Omitted" and "present with the default" read the same to a schema + # validator; they hash differently. #1613 proposes leaving an unset + # optional default off the dump; until then, dump as received + # (``exclude_unset=True``) to recover the bytes. + "default_emitted": "the generator materializes schema defaults on dump (#1613)", + # ``extra='ignore'`` drops a property the pinned model does not declare, + # and a union arm declares only its own properties while the schema's arm + # admits its siblings'. No digest-covered schema is affected at this pin; + # the hazard is a newer-spec field inside one. #1614. + "field_dropped": "unknown and sibling-arm properties are dropped (#1614)", + # ``model_dump()`` without ``by_alias`` writes ``field_schema`` for + # ``$schema`` and ``pass_`` for ``pass``; the SDK's own wire paths pass + # ``by_alias=True``. #1615. + "alias_emitted": "model_dump() writes the Python name of an aliased key (#1615)", + # ``exclude_none`` drops an explicit ``null`` on a property whose schema + # admits it; on the Python side ``None`` means "not supplied". #1615. + "null_dropped": "exclude_none drops an admitted explicit null (#1615)", +} + +#: Schemas whose bytes the protocol binds. Under ``model_dump_as_received`` +#: no difference of any class; under the default dump no rewrite, no dropped +#: field, no alias and no dropped null -- only, for the schemas listed in +#: :data:`BOUND_DEFAULTS`, a schema default the document omitted. +BOUND = sorted(rt.DIGEST_COVERED | rt.ECHO_COVERED) + +#: Bound schemas whose *default* dump still materializes a schema default the +#: document omitted (#1613): ``commercial_terms`` reaches ``targeting.json`` +#: (``age_restriction.verification_required``), so ``terms_digest`` recomputed +#: over ``model_dump(mode="json")`` mismatches whenever the seller left it +#: out. ``model_dump_as_received`` recovers the bytes, and that is the +#: verification path until #1613 lands. The set only shrinks; a bound schema +#: joining it is a regression. +BOUND_DEFAULTS = frozenset( + { + "core/canonical-proposal.json", + "media-buy/commercial-terms.json", + } +) + +#: Schemas with at least one schema-valid document the generated model refuses +#: (raises, or fails validation). Refusing is the schema/model parity suite's +#: domain, not this one's; the set is recorded so a refusal cannot silently +#: remove a schema from the round-trip grade, and so that it only shrinks. +#: ``mcp-webhook-payload`` and ``async-response-data`` raise ``TypeError`` +#: (#1616); the others refuse documents the bundled schema accepts +#: (``publisher-property-selector`` through the mutual-exclusivity validator +#: ``_ergonomic`` adds, which the schema does not express). +REFUSING = frozenset( + { + "brand/creative-approval-request.json", + "brand/verify-brand-claims-response.json", + "core/async-response-data.json", + "core/mcp-webhook-payload.json", + "core/publisher-property-selector.json", + "core/reporting-delivery-capabilities.json", + "media-buy/change-term.json", + } +) + + +@cache +def _corpus() -> dict[str, list[rt.Document]]: + """Every base document, by schema.""" + by_schema: dict[str, list[rt.Document]] = defaultdict(list) + for document in rt.documents(): + if rt.has_model(document): + by_schema[document.schema].append(document) + return dict(by_schema) + + +@cache +def _graded(schema: str) -> tuple[list[tuple[rt.Document, list[rt.Difference]]], list[rt.Refusal]]: + """Every document and variant for ``schema``, with what the round trip changed. + + A bound schema is graded on every document and every variant of each. For + the rest, variants are derived from the first document only: every + ``format: uri`` field in a schema is typed the same way, so varying one + document's leaves covers the type and varying ten multiplies runtime. + """ + documents = _corpus().get(schema, []) + graded: list[tuple[rt.Document, list[rt.Difference]]] = [] + refusals: list[rt.Refusal] = [] + for index, base in enumerate(documents): + expand = schema in rt.DIGEST_COVERED or schema in rt.ECHO_COVERED or index == 0 + for document in [base, *(rt.variants(base) if expand else [])]: + differences, refusal = rt.round_trip(document) + if refusal is not None: + refusals.append(refusal) + else: + graded.append((document, differences)) + return graded, refusals + + +SCHEMAS = sorted(_corpus()) + + +def test_every_bound_schema_has_a_document() -> None: + """A digest- or echo-covered schema with no document is ungraded, which is a coverage hole.""" + missing = [schema for schema in BOUND if not _corpus().get(schema)] + assert not missing, ( + f"no schema-valid document for {missing}; add one to " + f"{rt.FIXTURE_CORPUS.relative_to(rt.REPO_ROOT)}" + ) + + +@pytest.mark.parametrize("schema", BOUND) +def test_bound_object_round_trips_byte_for_byte_as_received(schema: str) -> None: + """``model_dump_as_received`` of a digest- or echo-covered object is the document.""" + graded, refusals = _graded(schema) + assert not refusals, "\n".join( + [f"{len(refusals)} schema-valid document(s) for {schema} refused by the model:"] + + [f" [{r.document}] {sorted(r.error_types)}: {r.message}" for r in refusals[:10]] + ) + assert graded, f"{schema} graded no documents" + changed = [d for document, _ in graded for d in rt.round_trip(document, as_received=True)[0]] + assert not changed, "\n".join( + [f"{len(changed)} difference(s) in a digest- or echo-covered object ({schema}):"] + + [f" {d.describe()}" for d in changed[:20]] + ) + + +@pytest.mark.parametrize("schema", BOUND) +def test_bound_object_is_never_rewritten_by_the_default_dump(schema: str) -> None: + """The default dump of a bound object rewrites nothing; at most it adds a declared default.""" + graded, _refusals = _graded(schema) + changed = [d for _, differences in graded for d in differences] + rewritten = [d for d in changed if d.kind != "default_emitted"] + assert not rewritten, "\n".join( + [f"{len(rewritten)} rewrite(s) in a digest- or echo-covered object ({schema}):"] + + [f" {d.describe()}" for d in rewritten[:20]] + ) + defaults = [d for d in changed if d.kind == "default_emitted"] + if schema in BOUND_DEFAULTS: + assert ( + defaults + ), f"{schema} no longer emits an omitted default; delete it from BOUND_DEFAULTS" + else: + assert not defaults, "\n".join( + [f"{schema} now emits a default the document omitted (#1613):"] + + [f" {d.describe()}" for d in defaults[:20]] + ) + + +@pytest.mark.parametrize("schema", SCHEMAS) +def test_schema_round_trips_or_declares_its_differences(schema: str) -> None: + """Every difference belongs to a class this SDK declares, with its reason.""" + graded, _refusals = _graded(schema) + undeclared = [d for _, differences in graded for d in differences if d.kind not in ACCEPTED] + assert not undeclared, "\n".join( + [ + f"{len(undeclared)} rewrite(s) of a class this SDK does not declare ({schema}):", + *(f" {d.describe()}" for d in undeclared[:20]), + "", + "A wire value the model did not change must dump as received. Either fix the", + "type (see adcp.types._wire) or, for a new class of difference, add it to", + "tests/conformance/test_wire_round_trip.py ACCEPTED with its reason.", + ] + ) + + +def test_every_accepted_class_still_occurs() -> None: + """An accepted class that no longer happens is deleted, not kept.""" + observed = { + d.kind for schema in SCHEMAS for _, differences in _graded(schema)[0] for d in differences + } + stale = sorted(set(ACCEPTED) - observed) + assert not stale, f"accepted classes that no longer occur: {stale}; delete them from ACCEPTED" + unknown = sorted(set(ACCEPTED) - set(rt.CLASSES)) + assert not unknown, f"ACCEPTED names classes the sweep does not produce: {unknown}" + + +def test_dump_as_received_leaves_only_unknown_fields() -> None: + """The explicit "as received" dump recovers everything but what the model never held. + + ``AdCPBaseModel.model_dump_as_received`` leaves off unset defaults, keeps + an admitted explicit ``null`` and writes wire names, so the only residue is + a property the model dropped on input (#1614). This is the path a verifier + uses to recompute a digest over a parsed object today. + """ + residue: dict[str, set[str]] = defaultdict(set) + for schema in SCHEMAS: + for document, _ in _graded(schema)[0]: + if document.origin == "variant": + continue + for d in rt.round_trip(document, as_received=True)[0]: + residue[d.kind].add(schema) + unexpected = { + kind: sorted(schemas) for kind, schemas in residue.items() if kind != "field_dropped" + } + assert not unexpected, f"dump-as-received residue beyond dropped fields: {unexpected}" + + +def test_refusing_schemas_are_declared() -> None: + """A schema the model refuses a valid document for is named; the set only shrinks.""" + refusing = {schema for schema in SCHEMAS if _graded(schema)[1]} + new = sorted(refusing - REFUSING) + assert not new, "\n".join( + [f"schema-valid documents refused by the model for {len(new)} undeclared schema(s):"] + + [ + f" {schema}: {sorted(_graded(schema)[1][0].error_types)} " + f"{_graded(schema)[1][0].message}" + for schema in new[:10] + ] + ) + fixed = sorted(REFUSING - refusing) + assert not fixed, f"schemas that no longer refuse: {fixed}; delete them from REFUSING" diff --git a/tests/fixtures/public_api_snapshot.json b/tests/fixtures/public_api_snapshot.json index e448656d0..6e9c3d0ff 100644 --- a/tests/fixtures/public_api_snapshot.json +++ b/tests/fixtures/public_api_snapshot.json @@ -1002,6 +1002,9 @@ "AssetVariant": "union[instance:pydantic.fields:FieldInfo,union[core.assets.asset_union:AudioAsset,core.assets.asset_union:BriefAsset,core.assets.asset_union:CardAsset,core.assets.asset_union:CatalogAsset,core.assets.asset_union:CssAsset,core.assets.asset_union:DaastAsset,core.assets.asset_union:DaastTrackerAsset,core.assets.asset_union:DisplayTagAsset,core.assets.asset_union:HtmlAsset,core.assets.asset_union:ImageAsset,core.assets.asset_union:JavascriptAsset,core.assets.asset_union:MarkdownAsset,core.assets.asset_union:PixelTrackerAsset,core.assets.asset_union:PublishedPostAsset,core.assets.asset_union:TextAsset,core.assets.asset_union:UrlAsset,core.assets.asset_union:VastAsset,core.assets.asset_union:VastTrackerAsset,core.assets.asset_union:VideoAsset,core.assets.asset_union:WebhookAsset,core.assets.asset_union:ZipAsset]]", "AssignedPackage": "creative.list_creatives_response", "Assignments": "creative.list_creatives_response", + "AudienceEvidence": "core.audience_evidence", + "AudienceEvidencePin": "core.audience_evidence_pin", + "AudienceEvidenceSelection": "core.audience_evidence_selection", "AudienceSource": "enums.audience_source", "AudienceTooSmallDetails": "error_details.audience_too_small", "AudioContent": "core.assets.audio_asset:AudioAsset", @@ -1045,6 +1048,8 @@ "CalibrateContentResponse1": "content_standards.calibrate_content_response", "CalibrateContentSuccessResponse": "content_standards.calibrate_content_response:CalibrateContentResponse1", "CanonicalAssetSource": "core.canonical_projection_ref:AssetSource", + "CanonicalAudienceEvidence": "core.canonical_audience_evidence", + "CanonicalAudienceEvidenceSelection": "core.canonical_audience_evidence_selection", "CanonicalCompositionModel": "formats.canonical._base:CompositionModel", "CanonicalFormatAgentPlacement": "formats.canonical.agent_placement:CanonicalFormatAgentPlacementAiSurfaceSponsoredPlacement", "CanonicalFormatBase": "formats.canonical._base", @@ -4363,7 +4368,7 @@ "SpotReportingCapability": "core.spot_reporting_capability", "StaleResponseDetails": "error_details.stale_response", "StandardEnum": "sponsored_intelligence.si_capabilities", - "StartTiming": "union[instance:pydantic.fields:FieldInfo,union[pydantic.types:AwareDatetime,union[value:'asap']]]", + "StartTiming": "union[instance:pydantic.fields:FieldInfo,union[adcp.types._wire:WireDatetime,union[value:'asap']]]", "StartingPosition": "account.list_account_changes_request", "State": "formats.canonical.seller_rendered_stateful_display", "Status": "aao.agent_publishers", @@ -6645,7 +6650,7 @@ "Special": "core.special", "Specification": "core.product", "SpotReportingCapability": "core.spot_reporting_capability", - "StartTiming": "union[instance:pydantic.fields:FieldInfo,union[pydantic.types:AwareDatetime,union[value:'asap']]]", + "StartTiming": "union[instance:pydantic.fields:FieldInfo,union[adcp.types._wire:WireDatetime,union[value:'asap']]]", "StoreItem": "core.store_item", "Storyboard": "core.registry_event", "StoryboardStatus": "core.registry_event", diff --git a/tests/fixtures/round_trip/corpus.json b/tests/fixtures/round_trip/corpus.json new file mode 100644 index 000000000..6e9164184 --- /dev/null +++ b/tests/fixtures/round_trip/corpus.json @@ -0,0 +1,265 @@ +{ + "core/audience-evidence.json": { + "evidence_id": "evidence_age", + "snapshot_id": "evidence_age_2026_q3", + "version": "2026-q3", + "audience": { + "dimension": "age", + "range": { + "min": 25, + "max": 54 + }, + "label": "Adults 25-54" + }, + "relationship": "composition", + "value": 0.6, + "unit": "fraction", + "baseline": { + "system": "https://measurement.example", + "population_id": "us_adults", + "version": "2026" + }, + "evidence_type": "measured", + "methodology": "projected", + "subject_type": "individual", + "provider": { + "domain": "measurement.example" + }, + "measurement_window": { + "start": "2026-07-01", + "end": "2026-09-30" + }, + "last_updated": "2026-10-01T00:00:00.000+00:00", + "methodology_url": "https://Measurement.Example/methodology", + "content_digest": "sha256:5c149933a2feffe1849fe7d16691acf7644881340e79dd2261c3e95deb98f1f7", + "attestation_refs": [ + { + "issuer": { + "type": "origin", + "origin": "https://credentials.example" + }, + "claim_type": "https://adcontextprotocol.org/claims/audience-evidence", + "subject": { + "type": "resource", + "resource_type": "https://adcontextprotocol.org/claims/subjects/audience-evidence", + "namespace": "https://measurement.example", + "id": "evidence_age_2026_q3", + "content_digest": "sha256:5c149933a2feffe1849fe7d16691acf7644881340e79dd2261c3e95deb98f1f7" + }, + "locator": { + "type": "credential_uri", + "credential_uri": "https://credentials.example/vc/1" + } + } + ] + }, + "core/canonical-audience-evidence.json": { + "evidence_id": "evidence_age", + "snapshot_id": "evidence_age_2026_q3", + "version": "2026-q3", + "audience": { + "dimension": "age", + "range": { + "min": 25, + "max": 54 + }, + "label": "Adults 25-54" + }, + "relationship": "composition", + "value": 0.6, + "unit": "fraction", + "baseline": { + "system": "https://measurement.example", + "population_id": "us_adults", + "version": "2026" + }, + "evidence_type": "measured", + "methodology": "projected", + "subject_type": "individual", + "provider": { + "domain": "measurement.example" + }, + "measurement_window": { + "start": "2026-07-01", + "end": "2026-09-30" + }, + "last_updated": "2026-10-01T00:00:00.000+00:00", + "methodology_url": "https://Measurement.Example/methodology", + "content_digest": "sha256:5c149933a2feffe1849fe7d16691acf7644881340e79dd2261c3e95deb98f1f7" + }, + "media-buy/commercial-terms.json": { + "brand": { + "domain": "acme.example" + }, + "purchases": [ + { + "product_id": "prod_1", + "pricing_option_id": "cpm_1", + "pricing": { + "pricing_option_id": "cpm_1", + "pricing_model": "cpm", + "fixed_price": 5, + "currency": "USD" + }, + "start_time": "2026-11-01T00:00:00.000+00:00", + "end_time": "2026-11-30T23:59:59+00:00", + "targeting_overlay": { + "age_restriction": { + "min": 21 + } + } + } + ], + "start_time": "2026-11-01T00:00:00.000+00:00", + "end_time": "2026-11-30T23:59:59+00:00" + }, + "core/canonical-proposal.json": { + "proposal_id": "prop_1", + "proposal_kind": "new_media_buy", + "proposal_status": "committed", + "name": "Q4", + "expires_at": "2026-10-15T00:00:00.000+00:00", + "commercial_terms": { + "brand": { + "domain": "acme.example" + }, + "purchases": [ + { + "product_id": "prod_1", + "pricing_option_id": "cpm_1", + "pricing": { + "pricing_option_id": "cpm_1", + "pricing_model": "cpm", + "fixed_price": 5, + "currency": "USD" + }, + "start_time": "2026-11-01T00:00:00.000+00:00", + "end_time": "2026-11-30T23:59:59+00:00", + "targeting_overlay": { + "age_restriction": { + "min": 21 + } + } + } + ], + "start_time": "2026-11-01T00:00:00.000+00:00", + "end_time": "2026-11-30T23:59:59+00:00" + }, + "terms_digest": "sha256:9ZThE6bffvQLxvNEvrwwyF70xPr6eFFQuAuaMP3XEcg" + }, + "core/creative-representation-set.json": { + "creative_id": "cr_1", + "revision_id": "rev_1", + "name": "Banner", + "representations": [ + { + "representation_id": "rep_1", + "source": { + "system": "https://studio.example", + "source_representation": "asset_7" + }, + "format_kind": "image", + "assets": { + "image_main": { + "asset_type": "image", + "url": "https://CDN.Example/a.png", + "width": 300, + "height": 250 + } + } + } + ], + "revision_content_digest": "sha256:efc9ee3b58cf06c6af9187dda76cbed4899e28cbaba39cffba97606b124d3d23" + }, + "core/rights-attestation-evaluation.json": { + "rights_id": "rights_1", + "content_digest": "sha256:5041bf1f713df204784353e82f6a4a535931cb64f1f4b4a5aeaffcb720918b22", + "reference": { + "issuer": { + "type": "brand", + "brand": { + "domain": "acme.example" + } + }, + "claim_type": "https://adcontextprotocol.org/claims/rights/grant", + "subject": { + "type": "resource", + "resource_type": "https://adcontextprotocol.org/claims/subjects/rights-grant", + "namespace": "https://rights.example", + "id": "rights_1", + "content_digest": "sha256:5041bf1f713df204784353e82f6a4a535931cb64f1f4b4a5aeaffcb720918b22" + }, + "locator": { + "type": "credential_uri", + "credential_uri": "https://credentials.example/vc/rights_1" + }, + "validity_hint": { + "not_before": "2026-01-01T00:00:00+00:00", + "expires_at": "2027-01-01T00:00:00.000Z" + } + }, + "evaluation": { + "reference_digest": "sha256:4a5ef326daa88b5757003264296c103f68393b1aa69150255dfe9cbcfee294d4", + "outcome": "verified", + "evaluated_at": "2026-10-01T00:00:00.000+00:00", + "evaluated_by": "https://Governance.Example", + "credential_digest": "sha256:5543b0e2cdc4859cafc5a7ff61612b4c15cb5f7a0236c915b8a8a579349335d3", + "proof_format": "https://www.w3.org/TR/vc-data-model-2.0/", + "revocation_checked_at": "2026-10-01T00:00:00+00:00", + "valid_until": "2026-12-31T00:00:00+00:00", + "action_binding": { + "action_type": "https://adcontextprotocol.org/actions/rights-grant-evaluation", + "action_id": "rights_1", + "action_digest": "sha256:5041bf1f713df204784353e82f6a4a535931cb64f1f4b4a5aeaffcb720918b22" + } + } + }, + "governance/check-governance-response.json": { + "check_id": "chk_1", + "verdict": "approved", + "explanation": "ok", + "plan_id": "plan_1", + "expires_at": "2026-10-02T00:00:00.000+00:00", + "runtime_attestation_evaluations": [ + { + "reference_digest": "sha256:4a5ef326daa88b5757003264296c103f68393b1aa69150255dfe9cbcfee294d4", + "outcome": "verified", + "evaluated_at": "2026-10-01T00:00:00.000+00:00", + "evaluated_by": "https://Governance.Example", + "credential_digest": "sha256:5543b0e2cdc4859cafc5a7ff61612b4c15cb5f7a0236c915b8a8a579349335d3", + "proof_format": "https://www.w3.org/TR/vc-data-model-2.0/", + "revocation_checked_at": "2026-10-01T00:00:00+00:00", + "valid_until": "2026-12-31T00:00:00+00:00", + "action_binding": { + "action_type": "https://adcontextprotocol.org/actions/governance-check", + "action_id": "chk_1" + } + } + ], + "runtime_attestation_binding_digest": "sha256:295927e83297514165a359593222b89693f8b9625a269898662a0a90f2dad84b" + }, + "core/context.json": { + "buyer_ref": "po-2026-10", + "trace": { + "id": "4b1c", + "parent": null, + "hops": [ + 1, + null, + { + "at": "2026-10-01T00:00:00.000+00:00" + } + ] + }, + "note": null, + "url": "https://Buyer.Example" + }, + "core/ext.json": { + "gam": { + "advertiser_id": "123", + "flags": null, + "endpoint": "https://gam.example" + }, + "roku": null + } +} diff --git a/tests/test_code_generation.py b/tests/test_code_generation.py index 206179cc1..ecaf66630 100644 --- a/tests/test_code_generation.py +++ b/tests/test_code_generation.py @@ -2211,7 +2211,8 @@ def test_generated_create_media_buy_success_matches_schema_nullability(): and isinstance(node.target, ast.Name) and node.target.id == "confirmed_at" ) - assert ast.unparse(confirmed_at.annotation) == "AwareDatetime | None" + # ``WireDatetime`` is the generated spelling of ``AwareDatetime`` (#1610). + assert ast.unparse(confirmed_at.annotation) == "WireDatetime | None" assert confirmed_at.value is None diff --git a/tests/test_composability_invariant.py b/tests/test_composability_invariant.py index 5a908935b..344a5333e 100644 --- a/tests/test_composability_invariant.py +++ b/tests/test_composability_invariant.py @@ -97,6 +97,8 @@ def test_capability_fields_have_no_unreviewed_public_model_collisions() -> None: # These are intentionally different concepts that happen to share a schema # name. They must not be replaced by the public class of the same name. semantic_name_collisions = { + # The capabilities flag object, not the core evidence entry (#1610). + ("CapabilitiesMediaBuy", "audience_evidence", "AudienceEvidence"), ("CapabilitiesMediaBuy", "content_standards", "ContentStandards"), ("CapabilitiesMediaBuy", "performance_feedback", "PerformanceFeedback"), ("Execution", "trusted_match", "TrustedMatch"), diff --git a/tests/test_governance_authorization.py b/tests/test_governance_authorization.py index b7b140dc4..4d072866d 100644 --- a/tests/test_governance_authorization.py +++ b/tests/test_governance_authorization.py @@ -354,7 +354,7 @@ def test_intent_and_execution_builders_keep_plan_on_buyer_side() -> None: payload={"media_buy_id": "mb-1", "budget": 12}, proposed_commitment=build_governance_commitment(2, "USD"), ) - assert str(intent.target_agent) == "https://seller.example/" + assert str(intent.target_agent) == "https://seller.example" # as given (#1610) assert intent.payload == {"media_buy_id": "mb-1", "budget": 12} assert intent.governance_context is None diff --git a/tests/test_post_generate_wire_types.py b/tests/test_post_generate_wire_types.py new file mode 100644 index 000000000..7d421676a --- /dev/null +++ b/tests/test_post_generate_wire_types.py @@ -0,0 +1,50 @@ +"""``preserve_wire_text_on_normalizing_fields`` renames references, not prose (#1610). + +The pass points every generated ``AwareDatetime`` / ``AnyUrl`` / ``UUID`` / +``EmailStr`` field at its wire-preserving type. A field description that +says "Use a fresh UUID v4" must keep its wording, and ``uuid.UUID`` as an +attribute is not a reference to the imported name. +""" + +from __future__ import annotations + +import textwrap + +from scripts import post_generate_fixes as fixes + +_SOURCE = textwrap.dedent(''' + from uuid import UUID + + from pydantic import AnyUrl, AwareDatetime, Field + + + class Model(AdCPBaseModel): + """A UUID identifies it; an AnyUrl locates it.""" + + key: Annotated[UUID, Field(description="Use a fresh UUID v4 for each request.")] + url: AnyUrl | None = None + when: AwareDatetime + other = uuid.UUID + ''') + + +def test_identifier_tokens_are_renamed_and_strings_are_not() -> None: + renamed = fixes._rename_code_names( + _SOURCE, {"UUID": "WireUuid", "AnyUrl": "WireAnyUrl", "AwareDatetime": "WireDatetime"} + ) + assert "key: Annotated[WireUuid, Field(" in renamed + assert "url: WireAnyUrl | None = None" in renamed + assert "when: WireDatetime" in renamed + assert "from uuid import WireUuid" in renamed # the import line is a reference too + assert 'description="Use a fresh UUID v4 for each request."' in renamed + assert '"""A UUID identifies it; an AnyUrl locates it."""' in renamed + assert "other = uuid.UUID" in renamed + + +def test_every_format_type_the_generator_renders_has_a_replacement() -> None: + assert dict(fixes._WIRE_PRESERVING_TYPES) == { + ("pydantic", "AwareDatetime"): "WireDatetime", + ("pydantic", "AnyUrl"): "WireAnyUrl", + ("pydantic", "EmailStr"): "WireEmail", + ("uuid", "UUID"): "WireUuid", + } diff --git a/tests/test_reporting_adjustment_evidence.py b/tests/test_reporting_adjustment_evidence.py index 7bcbc360d..0caea2337 100644 --- a/tests/test_reporting_adjustment_evidence.py +++ b/tests/test_reporting_adjustment_evidence.py @@ -98,9 +98,15 @@ def test_raw_values_and_presence_are_hashed_before_model_normalization(detail: s assert evidence.observed_adjustment_sha256 == raw["canonical_adjustment_sha256"] assert evidence.input_kind == "bytes" assert evidence.adjustment == typed - normalized = typed.model_dump(mode="json", exclude_none=True) - normalized.pop("canonical_adjustment_sha256") - assert rfc8785.dumps(normalized) != evidence.canonical_json + # A typed view parsed from a differently spelled document hashes the same + # raw bytes: the evidence is bound to what was received, not to the model. + respelled = dict(raw, created_at="2026-09-02T00:00:00+00:00") + other = ReportingAdjustment.model_validate(respelled) + assert other.model_dump(mode="json")["created_at"] == "2026-09-02T00:00:00+00:00" + respelled_evidence = capture_reporting_adjustment_evidence( + wire, typed_adjustment=other, scope=SCOPE + ) + assert respelled_evidence.canonical_json == evidence.canonical_json assert json.loads(evidence.canonical_json)["control_total_deltas"][0]["value"] == "-0.00" assert json.loads(evidence.canonical_json)["control_total_deltas"][0]["value_type"] == "decimal" diff --git a/tests/test_type_aliases.py b/tests/test_type_aliases.py index 7fe3f5a04..a6fcb6bdb 100644 --- a/tests/test_type_aliases.py +++ b/tests/test_type_aliases.py @@ -512,7 +512,7 @@ def test_deployment_aliases_can_instantiate(): type="agent", agent_url="https://agent.example.com", is_live=False ) assert agent_deployment.type == "agent" - assert str(agent_deployment.agent_url) == "https://agent.example.com/" + assert str(agent_deployment.agent_url) == "https://agent.example.com" # as given (#1610) assert agent_deployment.is_live is False @@ -552,7 +552,7 @@ def test_destination_aliases_can_instantiate(): # Create AgentDestination agent_dest = AgentDestination(type="agent", agent_url="https://agent.example.com") assert agent_dest.type == "agent" - assert str(agent_dest.agent_url) == "https://agent.example.com/" + assert str(agent_dest.agent_url) == "https://agent.example.com" # as given (#1610) def test_deployment_destination_aliases_in_exports(): diff --git a/tests/test_wire_types.py b/tests/test_wire_types.py new file mode 100644 index 000000000..af4b331aa --- /dev/null +++ b/tests/test_wire_types.py @@ -0,0 +1,429 @@ +"""The wire-preserving scalar types keep the bytes the wire delivered (#1610). + +``AudienceEvidence.content_digest`` is SHA-256 over the RFC 8785 form of the +entry's declared fields. The generated model used to parse ``baseline.system`` +into ``AnyUrl`` (which appends a slash to a bare origin) and ``last_updated`` +into ``AwareDatetime`` (which rewrites ``+00:00`` as ``Z`` and drops ``.000``), +so a parsed-then-dumped entry no longer matched its own digest. The types in +:mod:`adcp.types._wire` validate the same way, keep the attribute API, and in +JSON mode serialize the text they were parsed from. +""" + +from __future__ import annotations + +import copy +import hashlib +import json +import pickle +import uuid +from datetime import datetime, timedelta, timezone +from typing import Any + +import pytest +import rfc8785 +from pydantic import AnyUrl, BaseModel, TypeAdapter, ValidationError + +from adcp.types import AudienceEvidence +from adcp.types._wire import WireAnyUrl, WireDatetime, WireEmail, WireUuid, rfc3339 +from adcp.types.base import AdCPBaseModel + + +class Plain(BaseModel): + when: WireDatetime + url: WireAnyUrl + uid: WireUuid + email: WireEmail + optional_url: WireAnyUrl | None = None + + +class Model(AdCPBaseModel): + """An AdCP model: ``model_dump`` passes ``serialize_as_any=True``.""" + + when: WireDatetime + url: WireAnyUrl + uid: WireUuid + email: WireEmail + + +WIRE = { + "when": "2026-10-01T00:00:00.000+00:00", + "url": "https://Measurement.Example", + "uid": "00000000-0000-4000-8000-00000000ABCD", + "email": "Ops@Example.COM", +} + + +@pytest.mark.parametrize("model", [Plain, Model]) +def test_parsed_text_is_dumped_byte_for_byte(model: type[BaseModel]) -> None: + parsed = model.model_validate_json(json.dumps(WIRE)) + assert parsed.model_dump(mode="json") == { + **WIRE, + **({"optional_url": None} if model is Plain else {}), + } + assert json.loads(parsed.model_dump_json())["when"] == WIRE["when"] + + +def test_python_mode_validation_keeps_the_text_too() -> None: + assert Model.model_validate(WIRE).model_dump(mode="json") == WIRE + + +@pytest.mark.parametrize( + "text", + [ + "2026-10-01T00:00:00+00:00", + "2026-10-01T00:00:00-05:00", + "2026-10-01T00:00:00.5Z", + "2026-10-01T00:00:00.123456789Z", + "2026-10-01t00:00:00z", + ], +) +def test_every_rfc3339_spelling_survives(text: str) -> None: + parsed = Model.model_validate({**WIRE, "when": text}) + assert parsed.model_dump(mode="json")["when"] == text + + +@pytest.mark.parametrize( + "text", + [ + "https://example.com", + "https://Example.COM/a", + "HTTPS://example.com/a", + "https://example.com:443/a", + "https://example.com/a%2Fb", + "https://example.com/a?b=1&c=2#frag", + ], +) +def test_every_url_spelling_survives(text: str) -> None: + parsed = Model.model_validate({**WIRE, "url": text}) + assert parsed.model_dump(mode="json")["url"] == text + assert str(parsed.url) == text + + +def test_attribute_api_is_the_parsed_type() -> None: + parsed = Model.model_validate(WIRE) + assert isinstance(parsed.when, datetime) + assert (parsed.when.year, parsed.when.tzinfo.utcoffset(None)) == (2026, timedelta(0)) + assert parsed.when == datetime(2026, 10, 1, tzinfo=timezone.utc) + assert isinstance(parsed.url, AnyUrl) + assert (parsed.url.scheme, parsed.url.host, parsed.url.path) == ( + "https", + "measurement.example", + "/", + ) + assert parsed.url.unicode_string() == "https://measurement.example/" + assert isinstance(parsed.uid, uuid.UUID) + assert parsed.uid.hex == "0000000000004000800000000000abcd" + assert parsed.uid == uuid.UUID("00000000-0000-4000-8000-00000000abcd") + assert isinstance(parsed.email, str) + + +def test_equality_and_hashing_match_the_pydantic_types() -> None: + parsed = Model.model_validate(WIRE) + plain = AnyUrl("https://measurement.example/") + assert parsed.url == plain + assert plain == parsed.url + assert len({parsed.url, plain}) == 1 + assert len({parsed.when, datetime(2026, 10, 1, tzinfo=timezone.utc)}) == 1 + assert len({parsed.uid, uuid.UUID(int=parsed.uid.int)}) == 1 + + +def test_copies_keep_the_wire_text() -> None: + parsed = Model.model_validate(WIRE) + dumped = parsed.model_dump(mode="json") + assert copy.deepcopy(parsed).model_dump(mode="json") == dumped + assert copy.copy(parsed).model_dump(mode="json") == dumped + assert parsed.model_copy(deep=True).model_dump(mode="json") == dumped + assert pickle.loads(pickle.dumps(parsed)).model_dump(mode="json") == dumped + assert Model.model_validate(parsed.model_dump()).model_dump(mode="json") == dumped + + +def test_a_value_built_in_python_serializes_in_the_pydantic_form() -> None: + built = Model( + when=datetime(2026, 10, 1, 12, 30, 0, 500000, tzinfo=timezone(timedelta(hours=-5))), + url=AnyUrl("https://x.example"), + uid=uuid.UUID("00000000-0000-4000-8000-00000000ABCD"), + email="a@b.example", + ) + assert built.model_dump(mode="json") == { + "when": "2026-10-01T12:30:00.500000-05:00", + "url": "https://x.example/", + "uid": "00000000-0000-4000-8000-00000000abcd", + "email": "a@b.example", + } + utc = Model(**{**built.model_dump(), "when": datetime(2026, 10, 1, tzinfo=timezone.utc)}) + assert utc.model_dump(mode="json")["when"] == "2026-10-01T00:00:00Z" + + +def test_a_derived_datetime_drops_the_wire_text() -> None: + parsed = Model.model_validate(WIRE) + assert parsed.when.wire == WIRE["when"] + assert type(parsed.when.replace(year=2027)) is WireDatetime + assert parsed.when.replace(year=2027).wire == "2027-10-01T00:00:00Z" + assert (parsed.when + timedelta(hours=1)).wire == "2026-10-01T01:00:00Z" + assert ( + rfc3339(parsed.when.astimezone(timezone(timedelta(hours=5)))) == "2026-10-01T05:00:00+05:00" + ) + + +@pytest.mark.parametrize( + "field, value", + [ + ("when", "2026-10-01T00:00:00"), + ("when", "not a date"), + ("url", "not a url"), + ("url", ""), + ("uid", "not a uuid"), + ("email", "not an email"), + ], +) +def test_validation_is_unchanged(field: str, value: Any) -> None: + with pytest.raises(ValidationError): + Model.model_validate({**WIRE, field: value}) + + +def test_json_schema_advertises_the_same_formats() -> None: + properties = Plain.model_json_schema()["properties"] + assert properties["when"] == {"format": "date-time", "title": "When", "type": "string"} + assert properties["url"] == {"format": "uri", "minLength": 1, "title": "Url", "type": "string"} + assert properties["uid"] == {"format": "uuid", "title": "Uid", "type": "string"} + assert properties["email"] == {"format": "email", "title": "Email", "type": "string"} + assert properties["optional_url"]["anyOf"] == [ + {"format": "uri", "minLength": 1, "type": "string"}, + {"type": "null"}, + ] + assert TypeAdapter(WireEmail).json_schema() == {"type": "string", "format": "email"} + + +def test_python_mode_dump_returns_the_typed_values() -> None: + dumped = Model.model_validate(WIRE).model_dump() + assert isinstance(dumped["when"], WireDatetime) + assert isinstance(dumped["url"], WireAnyUrl) + assert isinstance(dumped["uid"], WireUuid) + + +# --------------------------------------------------------------------------- +# The issue's reproduction. +# --------------------------------------------------------------------------- + +_DIGEST_EXCLUDED = {"content_digest", "attestation_refs"} + + +def _content_digest(entry: dict[str, Any]) -> str: + core = {k: v for k, v in entry.items() if k not in _DIGEST_EXCLUDED} + return "sha256:" + hashlib.sha256(rfc8785.dumps(core)).hexdigest() + + +def _entry() -> dict[str, Any]: + entry = { + "evidence_id": "evidence_age", + "snapshot_id": "evidence_age_2026_q3", + "version": "2026-q3", + "audience": {"dimension": "age", "range": {"min": 25, "max": 54}, "label": "Adults 25-54"}, + "relationship": "composition", + "value": 0.6, + "unit": "fraction", + "baseline": { + "system": "https://measurement.example", + "population_id": "us_adults", + "version": "2026", + }, + "evidence_type": "measured", + "methodology": "projected", + "subject_type": "individual", + "provider": {"domain": "measurement.example"}, + "measurement_window": {"start": "2026-07-01", "end": "2026-09-30"}, + "last_updated": "2026-10-01T00:00:00.000+00:00", + } + entry["content_digest"] = _content_digest(entry) + return entry + + +def test_audience_evidence_round_trips_its_own_digest() -> None: + entry = _entry() + assert _content_digest(entry) == entry["content_digest"] + dumped = AudienceEvidence.model_validate(entry).model_dump(mode="json") + assert dumped["baseline"]["system"] == "https://measurement.example" + assert dumped["last_updated"] == "2026-10-01T00:00:00.000+00:00" + assert _content_digest(dumped) == dumped["content_digest"] + assert AudienceEvidence.model_validate_json(json.dumps(entry)).model_dump(mode="json") == entry + + +def test_audience_evidence_attributes_are_still_typed() -> None: + parsed = AudienceEvidence.model_validate(_entry()) + assert parsed.last_updated.year == 2026 + assert parsed.last_updated.tzinfo is not None + assert parsed.baseline.system.host == "measurement.example" + + +def test_audience_evidence_is_exported_from_adcp_types() -> None: + import adcp.types + + assert adcp.types.AudienceEvidence is AudienceEvidence + assert AudienceEvidence.__module__ == "adcp.types.domains.core.audience_evidence" + + +def test_model_dump_as_received_leaves_off_what_the_document_omitted() -> None: + from adcp.types.domains.core.canonical_proposal import CanonicalProposal + + terms: dict[str, Any] = { + "brand": {"domain": "acme.example"}, + "purchases": [ + { + "product_id": "prod_1", + "pricing_option_id": "cpm_1", + "pricing": { + "pricing_option_id": "cpm_1", + "pricing_model": "cpm", + "fixed_price": 5, + "currency": "USD", + }, + "start_time": "2026-11-01T00:00:00.000+00:00", + "end_time": "2026-11-30T23:59:59+00:00", + "targeting_overlay": {"age_restriction": {"min": 21}}, + } + ], + "start_time": "2026-11-01T00:00:00.000+00:00", + "end_time": "2026-11-30T23:59:59+00:00", + } + digest = hashlib.sha256(rfc8785.dumps(terms)).digest() + import base64 + + proposal = { + "proposal_id": "prop_1", + "proposal_kind": "new_media_buy", + "proposal_status": "committed", + "name": "Q4", + "expires_at": "2026-10-15T00:00:00.000+00:00", + "commercial_terms": terms, + "terms_digest": "sha256:" + base64.urlsafe_b64encode(digest).rstrip(b"=").decode(), + } + parsed = CanonicalProposal.model_validate_json(json.dumps(proposal)) + # The default dump materializes ``verification_required: false`` (#1613)... + overlay = parsed.model_dump(mode="json")["commercial_terms"]["purchases"][0][ + "targeting_overlay" + ] + assert overlay["age_restriction"] == {"min": 21, "verification_required": False} + # ...and the as-received dump is the document, so terms_digest verifies. + received = parsed.model_dump_as_received() + assert received == proposal + assert rfc8785.dumps(received["commercial_terms"]) == rfc8785.dumps(terms) + + +def test_model_dump_as_received_keeps_an_admitted_null_and_the_wire_alias() -> None: + from adcp.types.domains.core.targeting_input import TargetingOverlayInput as TargetingInput + + document = {"signal_targeting_groups": None} + parsed = TargetingInput.model_validate_json(json.dumps(document)) + assert parsed.model_dump(mode="json") == {} + assert parsed.model_dump_as_received() == document + + +# --------------------------------------------------------------------------- +# Only strictly valid wire text is kept; lenient spellings normalize as before. +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + "text, expected", + [ + (str(datetime(2026, 10, 1, tzinfo=timezone.utc)), "2026-10-01T00:00:00Z"), # space, not T + ("2026-10-01T00:00:00+0000", "2026-10-01T00:00:00Z"), # offset without a colon + ("2026-10-01T00:00Z", "2026-10-01T00:00:00Z"), # no seconds + ("1790000000", "2026-09-21T14:13:20Z"), # epoch string + ], +) +def test_a_lenient_datetime_spelling_is_normalized(text: str, expected: str) -> None: + parsed = Model.model_validate({**WIRE, "when": text}) + assert parsed.model_dump(mode="json")["when"] == expected + assert parsed.when.wire == expected + + +@pytest.mark.parametrize( + "text, expected", + [ + (" https://x.example ", "https://x.example/"), # surrounding whitespace + ("https:x.example", "https://x.example/"), # no authority + ("https://x.example/a b", "https://x.example/a%20b"), # unescaped space + ("https://x.example/%zz", "https://x.example/%zz"), # malformed escape: parser keeps it + ("https://x.example\n", "https://x.example/"), # trailing newline + ("https://x.example/a\n", "https://x.example/a"), # trailing newline after a path + ], +) +def test_a_lenient_url_spelling_is_normalized(text: str, expected: str) -> None: + parsed = Model.model_validate({**WIRE, "url": text}) + assert parsed.model_dump(mode="json")["url"] == expected + assert str(parsed.url) == expected + + +def test_str_and_host_cannot_disagree() -> None: + """A backslash the WHATWG parser reads as a slash must not reach a client as written.""" + parsed = Model.model_validate({**WIRE, "url": "https://good.example\\@evil.example"}) + assert parsed.url.host == "good.example" + assert str(parsed.url) == "https://good.example/@evil.example" + assert parsed.model_dump(mode="json")["url"] == "https://good.example/@evil.example" + direct = WireAnyUrl("https://good.example\\@evil.example") + assert (direct.host, str(direct)) == ("good.example", "https://good.example/@evil.example") + + +@pytest.mark.parametrize( + "text", + [ + "https://user@x.example/", # userinfo: full RFC 3986 path, host agrees + "https://[::1]/a", # IP literal + "https://straße.de/x", # internationalized host, compared to unicode_host() + ], +) +def test_strict_but_unusual_url_text_is_kept_when_the_host_agrees(text: str) -> None: + parsed = Model.model_validate({**WIRE, "url": text}) + assert parsed.model_dump(mode="json")["url"] == text + + +@pytest.mark.parametrize( + "text", + [ + "{00000000-0000-4000-8000-00000000abcd}", + "urn:uuid:00000000-0000-4000-8000-00000000abcd", + "00000000000040008000000000000abc", + ], +) +def test_a_lenient_uuid_spelling_is_normalized(text: str) -> None: + parsed = Model.model_validate({**WIRE, "uid": text}) + assert parsed.model_dump(mode="json")["uid"] == str(uuid.UUID(text)) + + +@pytest.mark.parametrize( + "text, expected", + [ + ("Bob ", "bob@example.com"), + (" bob@example.com", "bob@example.com"), + ("Bob@Example.COM", "Bob@Example.COM"), + ], +) +def test_an_email_is_kept_only_as_the_trimmed_address(text: str, expected: str) -> None: + parsed = Model.model_validate({**WIRE, "email": text}) + assert parsed.model_dump(mode="json")["email"] == expected + + +def test_a_lenient_spelling_passed_to_a_constructor_is_normalized_too() -> None: + built = Model( + when="2026-10-01 00:00:00+00:00", + url=" https://x.example ", + uid=WIRE["uid"], + email="a@b.example", + ) + assert built.model_dump(mode="json")["when"] == "2026-10-01T00:00:00Z" + assert built.model_dump(mode="json")["url"] == "https://x.example/" + + +@pytest.mark.parametrize( + "text", + ["https://2130706433/", "https://0x7f.1/", "https://127.1/x", "https://0177.0.0.1/"], +) +def test_a_shorthand_ipv4_host_is_not_echoed(text: str) -> None: + """``.host`` reads 127.0.0.1, so ``str()`` must name that host too (an SSRF guard reads it).""" + from urllib.parse import urlsplit + + parsed = Model.model_validate({**WIRE, "url": text}) + assert parsed.url.host == "127.0.0.1" + assert urlsplit(str(parsed.url)).hostname == "127.0.0.1" + assert parsed.model_dump(mode="json")["url"] == str(parsed.url)