@adcp/client 4.21.0 → 4.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/AGENTS.md +278 -0
  2. package/README.md +96 -61
  3. package/bin/adcp.js +342 -4
  4. package/dist/lib/agents/index.generated.d.ts +9 -1
  5. package/dist/lib/agents/index.generated.d.ts.map +1 -1
  6. package/dist/lib/agents/index.generated.js +12 -0
  7. package/dist/lib/agents/index.generated.js.map +1 -1
  8. package/dist/lib/core/AgentClient.d.ts.map +1 -1
  9. package/dist/lib/core/SingleAgentClient.d.ts +2 -1
  10. package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
  11. package/dist/lib/core/SingleAgentClient.js +10 -1
  12. package/dist/lib/core/SingleAgentClient.js.map +1 -1
  13. package/dist/lib/discovery/property-crawler.d.ts +4 -0
  14. package/dist/lib/discovery/property-crawler.d.ts.map +1 -1
  15. package/dist/lib/discovery/property-crawler.js +10 -2
  16. package/dist/lib/discovery/property-crawler.js.map +1 -1
  17. package/dist/lib/index.d.ts +4 -4
  18. package/dist/lib/index.d.ts.map +1 -1
  19. package/dist/lib/index.js +6 -4
  20. package/dist/lib/index.js.map +1 -1
  21. package/dist/lib/protocols/index.d.ts.map +1 -1
  22. package/dist/lib/protocols/index.js +8 -6
  23. package/dist/lib/protocols/index.js.map +1 -1
  24. package/dist/lib/protocols/mcp.d.ts.map +1 -1
  25. package/dist/lib/protocols/mcp.js +24 -11
  26. package/dist/lib/protocols/mcp.js.map +1 -1
  27. package/dist/lib/server/index.d.ts +2 -0
  28. package/dist/lib/server/index.d.ts.map +1 -1
  29. package/dist/lib/server/index.js +3 -1
  30. package/dist/lib/server/index.js.map +1 -1
  31. package/dist/lib/server/serve.d.ts +73 -0
  32. package/dist/lib/server/serve.d.ts.map +1 -0
  33. package/dist/lib/server/serve.js +94 -0
  34. package/dist/lib/server/serve.js.map +1 -0
  35. package/dist/lib/testing/client.d.ts.map +1 -1
  36. package/dist/lib/testing/client.js +1 -0
  37. package/dist/lib/testing/client.js.map +1 -1
  38. package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
  39. package/dist/lib/testing/compliance/comply.js +48 -63
  40. package/dist/lib/testing/compliance/comply.js.map +1 -1
  41. package/dist/lib/testing/compliance/storyboard-tracks.d.ts +24 -0
  42. package/dist/lib/testing/compliance/storyboard-tracks.d.ts.map +1 -0
  43. package/dist/lib/testing/compliance/storyboard-tracks.js +157 -0
  44. package/dist/lib/testing/compliance/storyboard-tracks.js.map +1 -0
  45. package/dist/lib/testing/compliance/types.d.ts +1 -1
  46. package/dist/lib/testing/compliance/types.d.ts.map +1 -1
  47. package/dist/lib/testing/index.d.ts +1 -0
  48. package/dist/lib/testing/index.d.ts.map +1 -1
  49. package/dist/lib/testing/index.js +23 -1
  50. package/dist/lib/testing/index.js.map +1 -1
  51. package/dist/lib/testing/orchestrator.d.ts +8 -0
  52. package/dist/lib/testing/orchestrator.d.ts.map +1 -1
  53. package/dist/lib/testing/orchestrator.js +8 -0
  54. package/dist/lib/testing/orchestrator.js.map +1 -1
  55. package/dist/lib/testing/storyboard/context.d.ts +34 -0
  56. package/dist/lib/testing/storyboard/context.d.ts.map +1 -0
  57. package/dist/lib/testing/storyboard/context.js +257 -0
  58. package/dist/lib/testing/storyboard/context.js.map +1 -0
  59. package/dist/lib/testing/storyboard/index.d.ts +15 -0
  60. package/dist/lib/testing/storyboard/index.d.ts.map +1 -0
  61. package/dist/lib/testing/storyboard/index.js +48 -0
  62. package/dist/lib/testing/storyboard/index.js.map +1 -0
  63. package/dist/lib/testing/storyboard/loader.d.ts +53 -0
  64. package/dist/lib/testing/storyboard/loader.d.ts.map +1 -0
  65. package/dist/lib/testing/storyboard/loader.js +114 -0
  66. package/dist/lib/testing/storyboard/loader.js.map +1 -0
  67. package/dist/lib/testing/storyboard/path.d.ts +29 -0
  68. package/dist/lib/testing/storyboard/path.d.ts.map +1 -0
  69. package/dist/lib/testing/storyboard/path.js +121 -0
  70. package/dist/lib/testing/storyboard/path.js.map +1 -0
  71. package/dist/lib/testing/storyboard/request-builder.d.ts +28 -0
  72. package/dist/lib/testing/storyboard/request-builder.d.ts.map +1 -0
  73. package/dist/lib/testing/storyboard/request-builder.js +410 -0
  74. package/dist/lib/testing/storyboard/request-builder.js.map +1 -0
  75. package/dist/lib/testing/storyboard/runner.d.ts +24 -0
  76. package/dist/lib/testing/storyboard/runner.d.ts.map +1 -0
  77. package/dist/lib/testing/storyboard/runner.js +280 -0
  78. package/dist/lib/testing/storyboard/runner.js.map +1 -0
  79. package/dist/lib/testing/storyboard/task-map.d.ts +21 -0
  80. package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -0
  81. package/dist/lib/testing/storyboard/task-map.js +84 -0
  82. package/dist/lib/testing/storyboard/task-map.js.map +1 -0
  83. package/dist/lib/testing/storyboard/types.d.ts +156 -0
  84. package/dist/lib/testing/storyboard/types.d.ts.map +1 -0
  85. package/dist/lib/testing/storyboard/types.js +10 -0
  86. package/dist/lib/testing/storyboard/types.js.map +1 -0
  87. package/dist/lib/testing/storyboard/validations.d.ts +17 -0
  88. package/dist/lib/testing/storyboard/validations.d.ts.map +1 -0
  89. package/dist/lib/testing/storyboard/validations.js +166 -0
  90. package/dist/lib/testing/storyboard/validations.js.map +1 -0
  91. package/dist/lib/testing/types.d.ts +2 -0
  92. package/dist/lib/testing/types.d.ts.map +1 -1
  93. package/dist/lib/types/core.generated.d.ts +2 -2
  94. package/dist/lib/types/core.generated.d.ts.map +1 -1
  95. package/dist/lib/types/core.generated.js +1 -1
  96. package/dist/lib/types/schemas.generated.d.ts +193 -34
  97. package/dist/lib/types/schemas.generated.d.ts.map +1 -1
  98. package/dist/lib/types/schemas.generated.js +87 -5
  99. package/dist/lib/types/schemas.generated.js.map +1 -1
  100. package/dist/lib/types/tools.generated.d.ts +280 -3
  101. package/dist/lib/types/tools.generated.d.ts.map +1 -1
  102. package/dist/lib/utils/response-schemas.d.ts.map +1 -1
  103. package/dist/lib/utils/response-schemas.js +34 -3
  104. package/dist/lib/utils/response-schemas.js.map +1 -1
  105. package/dist/lib/utils/validate-user-agent.d.ts +8 -0
  106. package/dist/lib/utils/validate-user-agent.d.ts.map +1 -0
  107. package/dist/lib/utils/validate-user-agent.js +15 -0
  108. package/dist/lib/utils/validate-user-agent.js.map +1 -0
  109. package/dist/lib/version.d.ts +6 -0
  110. package/dist/lib/version.d.ts.map +1 -1
  111. package/dist/lib/version.js +7 -1
  112. package/dist/lib/version.js.map +1 -1
  113. package/docs/README.md +42 -0
  114. package/docs/guides/BUILD-AN-AGENT.md +294 -0
  115. package/docs/llms.txt +634 -0
  116. package/examples/README.md +106 -0
  117. package/examples/adcp.config.json +30 -0
  118. package/examples/basic-a2a.ts +76 -0
  119. package/examples/basic-mcp.ts +50 -0
  120. package/examples/batch-preview-test.ts +266 -0
  121. package/examples/conversation-client.ts +291 -0
  122. package/examples/debug-preview-response.ts +73 -0
  123. package/examples/debug-preview-with-logging.ts +50 -0
  124. package/examples/easy-config-demo.ts +242 -0
  125. package/examples/env-config.ts +51 -0
  126. package/examples/error-compliant-server.ts +237 -0
  127. package/examples/generative-creative-demo.ts +205 -0
  128. package/examples/inspect-card-formats.ts +161 -0
  129. package/examples/logger-usage.ts +165 -0
  130. package/examples/oauth-cli-example.ts +154 -0
  131. package/examples/pr78-async-patterns-demo.ts +247 -0
  132. package/examples/signals-agent.ts +163 -0
  133. package/examples/simple-getting-started.ts +225 -0
  134. package/examples/simple-protocol-demo.ts +75 -0
  135. package/examples/test-helpers-demo.ts +239 -0
  136. package/examples/zod-validation-example.ts +126 -0
  137. package/package.json +12 -2
  138. package/skills/adcp/SKILL.md +13 -2
  139. package/storyboards/audience_sync.yaml +199 -0
  140. package/storyboards/behavioral_analysis.yaml +244 -0
  141. package/storyboards/brand_rights.yaml +131 -0
  142. package/storyboards/creative_ad_server.yaml +171 -0
  143. package/storyboards/creative_sales_agent.yaml +169 -0
  144. package/storyboards/creative_template.yaml +306 -0
  145. package/storyboards/deterministic_testing.yaml +925 -0
  146. package/storyboards/error_compliance.yaml +231 -0
  147. package/storyboards/governance_content_standards.yaml +213 -0
  148. package/storyboards/governance_property_lists.yaml +372 -0
  149. package/storyboards/media_buy_catalog_creative.yaml +457 -0
  150. package/storyboards/media_buy_governance_escalation.yaml +467 -0
  151. package/storyboards/media_buy_guaranteed_approval.yaml +396 -0
  152. package/storyboards/media_buy_non_guaranteed.yaml +288 -0
  153. package/storyboards/media_buy_proposal_mode.yaml +369 -0
  154. package/storyboards/media_buy_seller.yaml +560 -0
  155. package/storyboards/media_buy_state_machine.yaml +254 -0
  156. package/storyboards/schema.yaml +65 -0
  157. package/storyboards/schema_validation.yaml +166 -0
  158. package/storyboards/si_session.yaml +384 -0
  159. package/storyboards/signal_marketplace.yaml +283 -0
  160. package/storyboards/signal_owned.yaml +211 -0
@@ -0,0 +1,254 @@
1
+ id: media_buy_state_machine
2
+ version: "1.0.0"
3
+ title: "Media buy state machine lifecycle"
4
+ category: media_buy_seller
5
+ track: media_buy
6
+ summary: "Validates media buy state transitions: create, pause, resume, cancel, and terminal state enforcement."
7
+
8
+ required_tools:
9
+ - create_media_buy
10
+ - update_media_buy
11
+
12
+ narrative: |
13
+ A media buy has a well-defined state machine: draft, pending_approval, confirmed, active,
14
+ paused, completed, canceled. Transitions between states must follow the spec — you cannot
15
+ resume a canceled buy or pause a completed one.
16
+
17
+ This storyboard creates a media buy, walks it through pause/resume/cancel transitions, then
18
+ verifies that the agent rejects updates to a buy in a terminal state (canceled or completed).
19
+ It also tests package-level pause/resume independent of the media buy status.
20
+
21
+ The state machine is the backbone of media buy reliability. If an agent allows invalid
22
+ transitions, buyers cannot trust the status field and automation breaks down.
23
+
24
+ agent:
25
+ interaction_model: media_buy_seller
26
+ capabilities:
27
+ - sells_media
28
+ - accepts_briefs
29
+ examples:
30
+ - "Any AdCP seller with media buy support"
31
+
32
+ caller:
33
+ role: buyer_agent
34
+ example: "Scope3 (DSP)"
35
+
36
+ prerequisites:
37
+ description: |
38
+ The caller needs a brand identity for account setup. The test creates a media buy
39
+ using discovered products, then exercises state transitions.
40
+ test_kit: "test-kits/acme-outdoor.yaml"
41
+
42
+ phases:
43
+ - id: setup
44
+ title: "Create a media buy"
45
+ narrative: |
46
+ Discover products and create a media buy to use for state transition testing.
47
+ The media buy ID is captured and passed to subsequent phases via context.
48
+
49
+ steps:
50
+ - id: discover_products
51
+ title: "Discover products for media buy"
52
+ narrative: |
53
+ Send a brief to get available products with pricing options. The first product
54
+ with a pricing option will be used to create the test media buy.
55
+ task: get_products
56
+ requires_tool: get_products
57
+ stateful: false
58
+ expected: |
59
+ Return products with:
60
+ - product_id
61
+ - pricing_options with pricing_option_id
62
+ sample_request:
63
+ buying_mode: "brief"
64
+ brief: "Display advertising products for state machine testing"
65
+ brand:
66
+ domain: "acmeoutdoor.com"
67
+ context_outputs:
68
+ - path: "products[0].product_id"
69
+ key: "product_id"
70
+ - path: "products[0].pricing_options[0].pricing_option_id"
71
+ key: "pricing_option_id"
72
+ validations:
73
+ - check: response_schema
74
+ description: "Response matches get-products-response.json schema"
75
+ - check: field_present
76
+ path: "products[0].product_id"
77
+ description: "At least one product with a product_id"
78
+
79
+ - id: create_buy
80
+ title: "Create the test media buy"
81
+ narrative: |
82
+ Create a media buy using the discovered product. The media_buy_id is captured
83
+ for use in all subsequent state transition steps.
84
+ task: create_media_buy
85
+ requires_tool: create_media_buy
86
+ stateful: true
87
+ expected: |
88
+ Return a media buy with:
89
+ - media_buy_id: platform-assigned identifier
90
+ - status: confirmed, active, or pending_approval
91
+ sample_request:
92
+ account:
93
+ brand:
94
+ domain: "acmeoutdoor.com"
95
+ operator: "pinnacle-agency.com"
96
+ brand:
97
+ domain: "acmeoutdoor.com"
98
+ start_time: "2026-05-01T00:00:00Z"
99
+ end_time: "2026-05-31T23:59:59Z"
100
+ packages:
101
+ - product_id: "$context.product_id"
102
+ budget: 5000
103
+ pricing_option_id: "$context.pricing_option_id"
104
+ context_outputs:
105
+ - path: "media_buy_id"
106
+ key: "media_buy_id"
107
+ - path: "media_buy.media_buy_id"
108
+ key: "media_buy_id"
109
+ validations:
110
+ - check: response_schema
111
+ description: "Response matches create-media-buy-response.json schema"
112
+ - check: field_present
113
+ path: "media_buy_id"
114
+ description: "Response contains a media_buy_id"
115
+
116
+ - id: state_transitions
117
+ title: "State transitions: pause, resume, cancel"
118
+ narrative: |
119
+ Exercise the core state transitions on the media buy. Pause puts the buy on hold
120
+ (status becomes paused), resume reactivates it (status becomes active), and cancel
121
+ terminates it permanently (status becomes canceled).
122
+
123
+ Each transition is validated by checking the returned status field.
124
+
125
+ steps:
126
+ - id: pause_buy
127
+ title: "Pause the media buy"
128
+ narrative: |
129
+ Pause the active media buy. The status should transition to paused.
130
+ This is a reversible operation — the buy can be resumed later.
131
+ task: update_media_buy
132
+ requires_tool: update_media_buy
133
+ stateful: true
134
+ expected: |
135
+ Return the media buy with status: paused.
136
+ sample_request:
137
+ media_buy_id: "$context.media_buy_id"
138
+ paused: true
139
+ validations:
140
+ - check: field_value
141
+ path: "status"
142
+ value: "paused"
143
+ description: "Media buy status is paused"
144
+
145
+ - id: resume_buy
146
+ title: "Resume the media buy"
147
+ narrative: |
148
+ Resume the paused media buy. The status should transition back to active
149
+ (or pending_activation on some platforms).
150
+ task: update_media_buy
151
+ requires_tool: update_media_buy
152
+ stateful: true
153
+ expected: |
154
+ Return the media buy with status: active or pending_activation.
155
+ sample_request:
156
+ media_buy_id: "$context.media_buy_id"
157
+ paused: false
158
+ validations:
159
+ - check: field_present
160
+ path: "status"
161
+ description: "Response contains a status field after resume"
162
+
163
+ - id: cancel_buy
164
+ title: "Cancel the media buy"
165
+ narrative: |
166
+ Cancel the media buy permanently. The status should transition to canceled.
167
+ This is a terminal state — no further transitions are allowed.
168
+ task: update_media_buy
169
+ requires_tool: update_media_buy
170
+ stateful: true
171
+ expected: |
172
+ Return the media buy with status: canceled.
173
+ sample_request:
174
+ media_buy_id: "$context.media_buy_id"
175
+ canceled: true
176
+ cancellation_reason: "AdCP compliance test — state machine scenario"
177
+ validations:
178
+ - check: field_value
179
+ path: "status"
180
+ value: "canceled"
181
+ description: "Media buy status is canceled"
182
+
183
+ - id: terminal_enforcement
184
+ title: "Terminal state enforcement"
185
+ narrative: |
186
+ Once a media buy is canceled or completed, the agent must reject any further
187
+ state changes. Attempting to pause, resume, or re-cancel a terminated buy should
188
+ return an error — typically INVALID_STATE_TRANSITION or INVALID_STATE.
189
+
190
+ This validates that the state machine is properly enforced and buyers can trust
191
+ that terminal states are final.
192
+
193
+ steps:
194
+ - id: pause_canceled_buy
195
+ title: "Pause a canceled media buy (expect rejection)"
196
+ narrative: |
197
+ Attempt to pause the canceled media buy. The agent should reject this with
198
+ an error indicating the state transition is invalid.
199
+ task: update_media_buy
200
+ requires_tool: update_media_buy
201
+ expect_error: true
202
+ stateful: true
203
+ expected: |
204
+ Reject with an error containing:
205
+ - code: INVALID_STATE_TRANSITION, INVALID_STATE, or similar
206
+ - The buy must remain in canceled status
207
+ sample_request:
208
+ media_buy_id: "$context.media_buy_id"
209
+ paused: true
210
+ validations:
211
+ - check: error_code
212
+ value: "INVALID_STATE_TRANSITION"
213
+ description: "Error code indicates invalid state transition"
214
+
215
+ - id: resume_canceled_buy
216
+ title: "Resume a canceled media buy (expect rejection)"
217
+ narrative: |
218
+ Attempt to resume the canceled media buy. The agent should reject this —
219
+ canceled is a terminal state and cannot be reversed.
220
+ task: update_media_buy
221
+ requires_tool: update_media_buy
222
+ expect_error: true
223
+ stateful: true
224
+ expected: |
225
+ Reject with an error. Canceled media buys cannot be resumed.
226
+ sample_request:
227
+ media_buy_id: "$context.media_buy_id"
228
+ paused: false
229
+ validations:
230
+ - check: error_code
231
+ value: "INVALID_STATE_TRANSITION"
232
+ description: "Error code indicates invalid state transition"
233
+
234
+ - id: recancel_buy
235
+ title: "Re-cancel an already canceled media buy"
236
+ narrative: |
237
+ Attempt to cancel a buy that is already canceled. The agent may either
238
+ reject with INVALID_STATE_TRANSITION or accept idempotently. Both are
239
+ acceptable — what matters is no crash or unstructured error.
240
+ task: update_media_buy
241
+ requires_tool: update_media_buy
242
+ stateful: true
243
+ expected: |
244
+ Either:
245
+ - Reject with INVALID_STATE_TRANSITION (strict)
246
+ - Accept idempotently and return status: canceled (permissive)
247
+ Both are acceptable behaviors.
248
+ sample_request:
249
+ media_buy_id: "$context.media_buy_id"
250
+ canceled: true
251
+ validations:
252
+ - check: field_present
253
+ path: "status"
254
+ description: "Response contains a status field (canceled) or a structured error"
@@ -0,0 +1,65 @@
1
+ # Storyboard Definition Schema
2
+ #
3
+ # Storyboards are narrative test workflows that walk agent builders through
4
+ # the sequence of calls their agent will receive, with context at each step.
5
+ #
6
+ # Each storyboard targets a specific agent interaction model and
7
+ # describes the flow from a caller's perspective.
8
+
9
+ # --- Schema definition ---
10
+
11
+ # A storyboard file must conform to this structure:
12
+ #
13
+ # id: string (unique identifier, e.g., "creative_template")
14
+ # version: string (semver, e.g., "1.0.0")
15
+ # title: string (human-readable title)
16
+ # category: enum (creative_template | creative_ad_server | creative_sales_agent | creative_generative | media_buy_seller | media_buy_guaranteed_approval | media_buy_non_guaranteed | media_buy_proposal_mode | media_buy_governance_escalation | media_buy_catalog_creative | signal_marketplace | signal_owned)
17
+ # summary: string (one-line description for listings)
18
+ # narrative: string (paragraph explaining the overall flow)
19
+ #
20
+ # agent:
21
+ # interaction_model: enum (stateless_transform | stateful_preloaded | stateful_push | stateless_generate | media_buy_seller | marketplace_catalog | owned_signals)
22
+ # capabilities: string[] (AdCP capability flags: supports_transformation, has_creative_library, supports_generation, sells_media, accepts_briefs, supports_guaranteed, supports_non_guaranteed, catalog_signals)
23
+ # examples: string[] (real-world examples: "Celtra", "Innovid")
24
+ #
25
+ # caller:
26
+ # role: string (who initiates the calls: "buyer_agent", "orchestrator", "dsp")
27
+ # example: string (e.g., "Scope3", "Pinnacle Agency")
28
+ #
29
+ # prerequisites:
30
+ # description: string (what must be true before running this storyboard)
31
+ # test_kit: string (reference to a test kit file, e.g., "test-kits/acme-outdoor.yaml")
32
+ #
33
+ # phases: array of Phase objects
34
+ #
35
+ # --- Phase ---
36
+ #
37
+ # id: string (unique within storyboard)
38
+ # title: string (human-readable phase title)
39
+ # narrative: string (paragraph explaining this phase from the caller's perspective)
40
+ #
41
+ # steps: array of Step objects
42
+ #
43
+ # --- Step ---
44
+ #
45
+ # id: string (unique within phase)
46
+ # title: string (human-readable step title)
47
+ # narrative: string (what's happening and why)
48
+ # task: string (AdCP task name: list_creative_formats, preview_creative, build_creative, get_products, create_media_buy, sync_accounts, etc.)
49
+ # schema_ref: string (path to request schema, e.g., "creative/list-creative-formats-request.json" or "media-buy/get-products-request.json")
50
+ # response_schema_ref: string (path to response schema)
51
+ # doc_ref: string (path to documentation page)
52
+ # comply_scenario: string (maps to @adcp/client testing scenario, e.g., "creative_sync")
53
+ # expected: string (human-readable description of expected behavior)
54
+ # stateful: boolean (does this step depend on state from a previous step?)
55
+ #
56
+ # sample_request: object (optional — example request payload for this step)
57
+ # sample_response: object (optional — example expected response)
58
+ #
59
+ # validations: array of Validation objects (optional)
60
+ #
61
+ # --- Validation ---
62
+ #
63
+ # check: string (what to validate: "response_schema", "field_present", "field_value", "status_code")
64
+ # path: string (JSON path to the field, e.g., "formats[0].format_id")
65
+ # description: string (human-readable description of validation)
@@ -0,0 +1,166 @@
1
+ id: schema_validation
2
+ version: "1.0.0"
3
+ title: "Schema compliance and temporal validation"
4
+ category: media_buy_seller
5
+ track: core
6
+ summary: "Validates that agent responses conform to AdCP schemas and that temporal constraints are enforced."
7
+
8
+ required_tools:
9
+ - get_products
10
+
11
+ narrative: |
12
+ Every AdCP agent must return responses that match the published JSON schemas. Fields defined
13
+ in the spec — product_id, delivery_type, pricing_options — must be present and correctly typed.
14
+ Agents must also enforce temporal constraints: flight dates must be logically consistent
15
+ (end after start) and start dates should not be in the past.
16
+
17
+ This storyboard checks schema compliance on get_products responses and validates that
18
+ create_media_buy rejects temporally invalid requests. These are foundational requirements
19
+ that every agent must satisfy regardless of platform type.
20
+
21
+ agent:
22
+ interaction_model: media_buy_seller
23
+ capabilities:
24
+ - sells_media
25
+ examples:
26
+ - "Any AdCP seller agent"
27
+
28
+ caller:
29
+ role: buyer_agent
30
+ example: "Compliance test harness"
31
+
32
+ phases:
33
+ - id: schema_compliance
34
+ title: "Response schema compliance"
35
+ narrative: |
36
+ The buyer sends a standard get_products request and validates that the response
37
+ matches the published schema. Each product must have the required fields defined
38
+ in the AdCP spec: product_id, delivery_type, and pricing information.
39
+
40
+ steps:
41
+ - id: get_products_schema
42
+ title: "Validate get_products response schema"
43
+ narrative: |
44
+ Send a brief and validate the response structure. Every product in the response
45
+ must conform to the get-products-response.json schema, with required v3 fields
46
+ present.
47
+ task: get_products
48
+ requires_tool: get_products
49
+ stateful: false
50
+ expected: |
51
+ Return products matching the brief. Each product must have:
52
+ - product_id: unique identifier
53
+ - name: human-readable name
54
+ - delivery_type: guaranteed or non_guaranteed
55
+ - pricing_options: at least one pricing option
56
+ sample_request:
57
+ buying_mode: "brief"
58
+ brief: "Show all available advertising products"
59
+ brand:
60
+ domain: "acmeoutdoor.com"
61
+ validations:
62
+ - check: response_schema
63
+ description: "Response matches get-products-response.json schema"
64
+ - check: field_present
65
+ path: "products"
66
+ description: "Response contains a products array"
67
+ - check: field_present
68
+ path: "products[0].product_id"
69
+ description: "Each product has a product_id"
70
+ - check: field_present
71
+ path: "products[0].delivery_type"
72
+ description: "Each product declares guaranteed or non_guaranteed delivery"
73
+ - check: field_present
74
+ path: "products[0].name"
75
+ description: "Each product has a name"
76
+
77
+ - id: pricing_options_present
78
+ title: "Validate pricing options structure"
79
+ narrative: |
80
+ Check that products include pricing_options with the required fields:
81
+ pricing_option_id and pricing_model. These are essential for buyers to
82
+ construct valid create_media_buy requests.
83
+ task: get_products
84
+ requires_tool: get_products
85
+ stateful: false
86
+ expected: |
87
+ Products include pricing_options with:
88
+ - pricing_option_id: unique identifier for the pricing option
89
+ - pricing_model: CPM, CPC, flat_fee, etc.
90
+ sample_request:
91
+ buying_mode: "brief"
92
+ brief: "Products with detailed pricing information"
93
+ brand:
94
+ domain: "acmeoutdoor.com"
95
+ validations:
96
+ - check: field_present
97
+ path: "products[0].pricing_options"
98
+ description: "Products include pricing_options array"
99
+ - check: field_present
100
+ path: "products[0].pricing_options[0].pricing_option_id"
101
+ description: "Pricing options have a pricing_option_id"
102
+ - check: field_present
103
+ path: "products[0].pricing_options[0].pricing_model"
104
+ description: "Pricing options declare a pricing_model"
105
+
106
+ - id: temporal_validation
107
+ title: "Temporal constraint enforcement"
108
+ narrative: |
109
+ Flight dates are a fundamental constraint in media buying. The start date must come
110
+ before the end date, and agents should validate this before accepting a media buy.
111
+ Some agents also reject start dates in the past, though this is platform-dependent.
112
+
113
+ steps:
114
+ - id: reversed_dates
115
+ title: "Reject reversed date range"
116
+ narrative: |
117
+ Send a create_media_buy with end_time before start_time. The agent must reject
118
+ this — a campaign cannot end before it starts.
119
+ task: create_media_buy
120
+ requires_tool: create_media_buy
121
+ expect_error: true
122
+ stateful: false
123
+ expected: |
124
+ Reject with an error containing:
125
+ - code: INVALID_REQUEST
126
+ - recovery: correctable
127
+ The agent must not accept a media buy where end_time precedes start_time.
128
+ sample_request:
129
+ idempotency_key: "temporal-test-reversed"
130
+ start_time: "2026-05-31T23:59:59Z"
131
+ end_time: "2026-05-01T00:00:00Z"
132
+ packages:
133
+ - product_id: "test-product"
134
+ budget: 1000
135
+ pricing_option_id: "test-pricing"
136
+ validations:
137
+ - check: error_code
138
+ value: "INVALID_REQUEST"
139
+ description: "Error code is INVALID_REQUEST for reversed date range"
140
+
141
+ - id: past_start_date
142
+ title: "Handle start date in the past"
143
+ narrative: |
144
+ Send a create_media_buy with a start_time in the past. Some agents reject this
145
+ outright, others accept it and auto-adjust the start date. Both behaviors are
146
+ acceptable — what matters is that the response is structured and intentional.
147
+ task: create_media_buy
148
+ requires_tool: create_media_buy
149
+ stateful: false
150
+ expected: |
151
+ Either:
152
+ - Reject with INVALID_REQUEST (strict validation)
153
+ - Accept and auto-adjust the start date (permissive)
154
+ Both are acceptable. The agent should not silently accept a past date without
155
+ adjusting it or warning the buyer.
156
+ sample_request:
157
+ idempotency_key: "temporal-test-past-start"
158
+ start_time: "2025-01-01T00:00:00Z"
159
+ end_time: "2026-12-31T23:59:59Z"
160
+ packages:
161
+ - product_id: "test-product"
162
+ budget: 1000
163
+ pricing_option_id: "test-pricing"
164
+ validations:
165
+ - check: response_schema
166
+ description: "Response is a valid create-media-buy-response or structured error"