Skip to content

feat(adagents): inline-resolution path for publisher_properties selectors + directory inverse-lookup wrapper #749

Description

@bokelley

Two related pieces of SDK work coming out of adcp#4825 and adcp#4823.

Part 1 — Inline-resolution path for publisher_properties selectors

adcp#4825 (spec PR adcp#4827) endorses parent-file inline resolution: when a parent file's top-level properties[] entries carry a publisher_domain matching one in the selector's publisher_domains[], the consumer MAY satisfy the selector from those inline properties without per-child adagents.json fetches.

This SDK has two related bugs at adcp/adagents.py:953-957:

  1. Returns raw selector dicts instead of resolved properties. get_properties_by_agent(cafemedia_data, "https://interchange.io") returns 1 selector dict instead of the 6,843 inline properties.

  2. No publisher_domains[] fan-out. _resolve_agent_properties doesn't expand the compact-form publisher_domains[] array into per-domain resolution. This is the bug adcp#4825's triage identified as orthogonal to the spec change.

Both need fixing. The fix:

if authorization_type == "publisher_properties":
    selectors = agent.get("publisher_properties", [])
    if not isinstance(selectors, list):
        return []
    resolved: list[Property] = []
    parent_properties = adagents_data.get("properties", []) or []
    for selector in selectors:
        if not isinstance(selector, dict):
            continue
        domains = _selector_domains(selector)  # publisher_domain | publisher_domains
        for domain in domains:
            inline = _resolve_inline(selector, parent_properties, domain)
            if inline is not None:
                resolved.extend(inline)
                continue  # inline succeeded; skip federated fetch
            resolved.extend(_resolve_federated(selector, domain))
    return resolved

_resolve_inline returns None (not []) when no matched property carries the expected publisher_domain, so federated fallback fires. Empty inline match with at least one inline candidate counts as a real empty resolution.

Divergence rule (from spec): if both paths resolve to the same (publisher_domain, property_id) and disagree, federated wins; SDK SHOULD log the divergence.

Revocation: inline path MUST honor revoked_publisher_domains[] on the parent file.

Part 2 — Directory inverse-lookup wrapper

adcp#4823 (spec PR adcp#4828) defines GET /v1/agents/{agent_url}/publishers. SDK needs a wrapper:

def fetch_agent_authorizations_from_directory(
    agent_url: str,
    directory_url: str = "https://agenticadvertising.org",
    *,
    since: datetime | None = None,
    status: list[str] = ["authorized"],
    cursor: str | None = None,
    limit: int = 200,
    timeout: float = 30.0,
) -> AgentDirectoryLookup:
    """Inverse of fetch_agent_authorizations — returns the publishers
    that authorize this agent, sourced from the AAO directory's index.

    Mirrors the existing AdAgentsValidationResult provenance fields
    (discovery_method, manager_domain) so consumers can route directory-
    sourced and per-domain results through the same code path.
    """

Return type mirrors the response envelope schema. Iterator support for paginated responses is a plus.

Acceptance

Part 1:

  • get_properties_by_agent(cafemedia_data, "https://interchange.io") returns the 6,843 inline properties matching tags ∋ "raptive_managed" and publisher_domain ∈ publisher_domains[...], not the selector dicts.
  • publisher_domains[] compact form is fanned into per-domain resolution.
  • When inline and federated diverge, SDK logs the divergence and uses the federated result.
  • Parent-file revoked_publisher_domains[] revokes inline matches for that domain.

Part 2:

  • fetch_agent_authorizations_from_directory returns spec-conformant AgentDirectoryLookup with discovery_method, manager_domain, properties_authorized, properties_total, signing_keys_pinned, status, last_verified_at per publisher.
  • Cursor pagination handled transparently or exposed as iterator.
  • Reuses AdAgentsValidationResult provenance fields where the vocabulary overlaps.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions