Skip to content

Commit 3eee8d5

Browse files
authored
fix(types): support AdCP 3.1 beta 7
Support AdCP 3.1.0-beta.7 schemas and generated types.\n\nIncludes response fixture/example updates for the beta.7 media-buy revision and confirmed_at requirements.\n\nMerged via Codex; squash subject intentionally uses fix(types) so release-please advances the current beta prerelease number instead of opening a new minor line.
1 parent 79a979d commit 3eee8d5

818 files changed

Lines changed: 711776 additions & 20260 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

SCHEMA_DELTAS.md

Lines changed: 127 additions & 1 deletion
Large diffs are not rendered by default.

examples/minimal_sales_agent.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ async def create_media_buy(
280280
return CreateMediaBuySuccessResponse(
281281
media_buy_id=media_buy_id,
282282
buyer_ref=buyer_ref,
283+
confirmed_at="2026-05-01T00:00:00Z",
284+
revision=1,
283285
packages=confirmed_packages,
284286
).model_dump(mode="json", exclude_none=True)
285287

examples/sales_proposal_mode_seller/src/platform.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ class _MediaBuy:
4444
total_budget: float
4545
start_time: datetime
4646
end_time: datetime
47+
confirmed_at: str
48+
revision: int = 1
4749
status: str = "active"
4850
recipes_seen: dict[str, str] | None = None # product_id -> line_item_template_id
4951

@@ -139,6 +141,7 @@ def create_media_buy(
139141
total_budget = float(_dotted(req, "total_budget.amount", 0.0) or 0.0)
140142
start_time = _read_datetime(getattr(req, "start_time", None))
141143
end_time = _read_datetime(getattr(req, "end_time", None))
144+
confirmed_at = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
142145

143146
with self._lock:
144147
self._buys[media_buy_id] = _MediaBuy(
@@ -147,6 +150,7 @@ def create_media_buy(
147150
total_budget=total_budget,
148151
start_time=start_time,
149152
end_time=end_time,
153+
confirmed_at=confirmed_at,
150154
recipes_seen=recipes_seen,
151155
)
152156

@@ -156,7 +160,8 @@ def create_media_buy(
156160
"media_buy_id": media_buy_id,
157161
"buyer_ref": getattr(req, "buyer_ref", None),
158162
"status": "active",
159-
"confirmed_at": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
163+
"confirmed_at": confirmed_at,
164+
"revision": 1,
160165
"proposal_id": str(proposal_id) if proposal_id else None,
161166
"packages": [
162167
{
@@ -203,10 +208,12 @@ def update_media_buy(
203208
),
204209
recovery="terminal",
205210
)
211+
buy.revision += 1
206212
return {
207213
"media_buy_id": media_buy_id,
208214
"buyer_ref": buy.proposal_id,
209215
"status": buy.status,
216+
"revision": buy.revision,
210217
"packages": [],
211218
}
212219

@@ -269,6 +276,8 @@ def get_media_buys(self, req: Any, ctx: RequestContext[Any]) -> dict[str, Any]:
269276
"media_buy_id": b.media_buy_id,
270277
"status": b.status,
271278
"buyer_ref": b.proposal_id,
279+
"confirmed_at": b.confirmed_at,
280+
"revision": b.revision,
272281
"packages": [],
273282
}
274283
for b in buys

examples/seller_agent.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,10 +619,12 @@ async def create_media_buy(self, params: dict[str, Any], context: Any = None) ->
619619
status = "active" if has_creatives else "pending_creatives"
620620

621621
mb_id = f"mb-{uuid.uuid4().hex[:8]}"
622+
confirmed_at = _now_z()
622623
media_buys[mb_id] = {
623624
"status": status,
624625
"currency": "USD",
625626
"packages": packages,
627+
"confirmed_at": confirmed_at,
626628
"revision": 1,
627629
}
628630
# Pull valid_actions from the SDK's authoritative state machine —
@@ -631,6 +633,8 @@ async def create_media_buy(self, params: dict[str, Any], context: Any = None) ->
631633
mb_id,
632634
packages,
633635
status=status,
636+
revision=1,
637+
confirmed_at=confirmed_at,
634638
valid_actions=valid_actions_for_status(status) or None,
635639
)
636640

@@ -645,6 +649,8 @@ async def get_media_buys(self, params: dict[str, Any], context: Any = None) -> d
645649
{
646650
"media_buy_id": mb_id,
647651
"status": mb["status"],
652+
"confirmed_at": mb.get("confirmed_at") or _now_z(),
653+
"revision": mb.get("revision", 1),
648654
"currency": mb.get("currency", "USD"),
649655
"packages": mb.get("packages", []),
650656
"total_budget": total_budget,
@@ -710,7 +716,8 @@ async def update_media_buy(self, params: dict[str, Any], context: Any = None) ->
710716
if status in ("completed", "rejected", "canceled"):
711717
return adcp_error("NOT_CANCELLABLE", f"Cannot cancel a {status} media buy")
712718
mb["status"] = "canceled"
713-
return cancel_media_buy_response(mb_id, "buyer")
719+
mb["revision"] = mb.get("revision", 1) + 1
720+
return cancel_media_buy_response(mb_id, "buyer", revision=mb["revision"])
714721

715722
mb["revision"] = mb.get("revision", 1) + 1
716723
return update_media_buy_response(
@@ -797,6 +804,7 @@ async def sync_creatives(self, params: dict[str, Any], context: Any = None) -> d
797804
if mb.get("status") == "pending_creatives":
798805
mb["status"] = "pending_start"
799806
mb["revision"] = mb.get("revision", 1) + 1
807+
mb.setdefault("confirmed_at", _now_z())
800808
return sync_creatives_response(results)
801809

802810
async def list_creatives(self, params: dict[str, Any], context: Any = None) -> dict[str, Any]:
@@ -1138,6 +1146,8 @@ async def seed_media_buy(
11381146
data.setdefault("status", "active")
11391147
data.setdefault("currency", "USD")
11401148
data.setdefault("packages", [])
1149+
data.setdefault("confirmed_at", _now_z())
1150+
data.setdefault("revision", 1)
11411151
media_buys[mb_id] = data
11421152
return {"media_buy_id": mb_id}
11431153

examples/type_aliases_demo.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ def handle_create_media_buy_response(
4242
success = CreateMediaBuySuccessResponse(
4343
media_buy_id="mb_12345",
4444
buyer_ref="ref_67890",
45+
confirmed_at="2026-05-01T00:00:00Z",
46+
revision=1,
4547
packages=[],
4648
)
4749
handle_create_media_buy_response(success)

examples/v3_reference_seller/src/platform.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,14 @@
135135
logger = logging.getLogger(__name__)
136136

137137

138+
def _order_confirmed_at(order: dict[str, Any]) -> str:
139+
"""Return a wire timestamp for the buy confirmation moment."""
140+
value = order.get("confirmed_at") or order.get("updated_at") or order.get("created_at")
141+
if value is not None:
142+
return str(value)
143+
return datetime.now(timezone.utc).isoformat()
144+
145+
138146
# ---------------------------------------------------------------------------
139147
# AccountStore — explicit (wire ref drives lookup)
140148
# ---------------------------------------------------------------------------
@@ -1193,6 +1201,7 @@ async def _project_create_success(
11931201
wire_status = "pending_creatives"
11941202
order_id = order["order_id"]
11951203
buy_state = self._buy_state.setdefault(order_id, {"packages": {}, "canceled": False})
1204+
revision = self._buy_revisions.setdefault(order_id, 1)
11961205
response_packages: list[dict[str, Any]] = []
11971206
for idx, pkg in enumerate(req_packages):
11981207
line_item = await upstream_helpers.add_line_item(
@@ -1217,6 +1226,8 @@ async def _project_create_success(
12171226
{
12181227
"media_buy_id": order_id,
12191228
"status": wire_status,
1229+
"confirmed_at": _order_confirmed_at(order),
1230+
"revision": revision,
12201231
"packages": response_packages,
12211232
"invoice_recipient": (
12221233
invoice_recipient.model_dump(mode="json", exclude_none=True)
@@ -1676,6 +1687,8 @@ async def get_media_buys(
16761687
{
16771688
"media_buy_id": order_id,
16781689
"status": wire_status,
1690+
"confirmed_at": _order_confirmed_at(order),
1691+
"revision": self._buy_revisions.setdefault(order_id, 1),
16791692
"currency": order.get("currency", "USD"),
16801693
"total_budget": float(order.get("budget", 0.0)),
16811694
"packages": packages,
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "A2UI Bound Value",
4+
"description": "A value that can be a literal or bound to a path in the data model",
5+
"oneOf": [
6+
{
7+
"type": "object",
8+
"description": "Literal string value",
9+
"properties": {
10+
"literalString": {
11+
"type": "string",
12+
"description": "Static string value"
13+
}
14+
},
15+
"required": [
16+
"literalString"
17+
],
18+
"additionalProperties": false
19+
},
20+
{
21+
"type": "object",
22+
"description": "Literal number value",
23+
"properties": {
24+
"literalNumber": {
25+
"type": "number",
26+
"description": "Static number value"
27+
}
28+
},
29+
"required": [
30+
"literalNumber"
31+
],
32+
"additionalProperties": false
33+
},
34+
{
35+
"type": "object",
36+
"description": "Literal boolean value",
37+
"properties": {
38+
"literalBoolean": {
39+
"type": "boolean",
40+
"description": "Static boolean value"
41+
}
42+
},
43+
"required": [
44+
"literalBoolean"
45+
],
46+
"additionalProperties": false
47+
},
48+
{
49+
"type": "object",
50+
"description": "Path to data model value",
51+
"properties": {
52+
"path": {
53+
"type": "string",
54+
"description": "JSON pointer path to value in data model (e.g., '/products/0/title')"
55+
}
56+
},
57+
"required": [
58+
"path"
59+
],
60+
"additionalProperties": false
61+
},
62+
{
63+
"type": "object",
64+
"description": "Literal with path binding (sets default and binds)",
65+
"properties": {
66+
"literalString": {
67+
"type": "string"
68+
},
69+
"path": {
70+
"type": "string"
71+
}
72+
},
73+
"required": [
74+
"literalString",
75+
"path"
76+
],
77+
"additionalProperties": false
78+
}
79+
]
80+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "A2UI Component",
4+
"description": "A component in an A2UI surface",
5+
"type": "object",
6+
"properties": {
7+
"id": {
8+
"type": "string",
9+
"description": "Unique identifier for this component within the surface"
10+
},
11+
"parentId": {
12+
"type": "string",
13+
"description": "ID of the parent component (null for root)"
14+
},
15+
"component": {
16+
"type": "object",
17+
"description": "Component definition (keyed by component type)",
18+
"minProperties": 1,
19+
"maxProperties": 1,
20+
"additionalProperties": {
21+
"type": "object",
22+
"description": "Component properties"
23+
}
24+
}
25+
},
26+
"required": [
27+
"id",
28+
"component"
29+
],
30+
"additionalProperties": true
31+
}

0 commit comments

Comments
 (0)