@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,384 @@
1
+ id: si_session
2
+ version: "1.0.0"
3
+ title: "Sponsored intelligence session"
4
+ category: si_session
5
+ summary: "SI agent that serves offerings, manages conversational sessions, and handles purchase handoffs."
6
+ track: si
7
+ required_tools:
8
+ - si_initiate_session
9
+
10
+ narrative: |
11
+ You run a sponsored intelligence platform — an AI-powered conversational commerce agent
12
+ that monetizes through product recommendations and purchase handoffs. A caller connects
13
+ to check offering availability, initiate a conversation session, exchange messages, and
14
+ optionally hand off to a transaction flow.
15
+
16
+ The caller starts by querying whether an offering is available. If it is, they initiate
17
+ a session, send messages to explore products, and eventually terminate the session. When
18
+ the user expresses purchase intent, the termination includes a handoff_transaction reason
19
+ and the response may contain an acp_handoff structure with checkout details.
20
+
21
+ This storyboard walks through the full SI lifecycle: availability, session management,
22
+ and transaction handoff.
23
+
24
+ agent:
25
+ interaction_model: si_platform
26
+ capabilities:
27
+ - sponsored_intelligence
28
+ - conversational
29
+ examples:
30
+ - "AI shopping assistants"
31
+ - "Conversational commerce platforms"
32
+ - "Product recommendation engines"
33
+
34
+ caller:
35
+ role: buyer_agent
36
+ example: "AI assistant caller"
37
+
38
+ prerequisites:
39
+ description: |
40
+ The caller needs an offering ID and identity credentials. The test uses
41
+ default values (e2e-test-offering, e2e-test-principal) suitable for
42
+ validating the SI protocol flow.
43
+
44
+ phases:
45
+ - id: availability
46
+ title: "Offering availability"
47
+ narrative: |
48
+ The caller checks whether a specific SI offering is available before initiating
49
+ a session. The agent returns availability status, an optional offering token for
50
+ session initiation, and metadata about the offering's capabilities.
51
+
52
+ This phase also validates that the agent handles invalid offering IDs gracefully,
53
+ returning unavailable rather than erroring.
54
+
55
+ steps:
56
+ - id: get_offering
57
+ title: "Check SI offering availability"
58
+ requires_tool: si_get_offering
59
+ narrative: |
60
+ The caller queries a specific offering by ID. The agent checks inventory and
61
+ returns whether the offering is available, along with an offering_token the
62
+ caller uses in subsequent session initiation.
63
+ task: si_get_offering
64
+ stateful: false
65
+ expected: |
66
+ Return the offering status:
67
+ - available: true or false
68
+ - offering_token: opaque token for session initiation (when available)
69
+ - title and description of the offering
70
+ - capabilities: what modalities this offering supports
71
+ - unavailable_reason: explanation when not available
72
+
73
+ sample_request:
74
+ offering_id: "e2e-test-offering"
75
+ context: "Checking SI offering availability"
76
+ identity:
77
+ principal: "e2e-test-principal"
78
+ device_id: "e2e-test-device"
79
+
80
+ validations:
81
+ - check: field_present
82
+ path: "available"
83
+ description: "Response includes availability status"
84
+
85
+ context_outputs:
86
+ - path: "offering_token"
87
+ key: "offering_token"
88
+
89
+ - id: get_offering_invalid
90
+ title: "Check invalid offering availability"
91
+ requires_tool: si_get_offering
92
+ narrative: |
93
+ The caller queries a nonexistent offering ID. The agent should return
94
+ available: false with an unavailable_reason, or reject the request with
95
+ an error. Either response is acceptable.
96
+ task: si_get_offering
97
+ stateful: false
98
+ expected: |
99
+ Return available: false for the invalid offering, or return an error.
100
+ The agent must not return available: true for an unknown offering ID.
101
+
102
+ sample_request:
103
+ offering_id: "INVALID_OFFERING_ID_DOES_NOT_EXIST_12345"
104
+ context: "Checking unavailable offering"
105
+ identity:
106
+ principal: "e2e-test-principal"
107
+
108
+ validations:
109
+ - check: field_value
110
+ path: "available"
111
+ value: false
112
+ description: "Invalid offering is reported as unavailable"
113
+
114
+ - id: session_lifecycle
115
+ title: "Session lifecycle"
116
+ narrative: |
117
+ The caller initiates a conversational session, exchanges messages, and
118
+ terminates the session. This tests the core SI protocol flow: create a
119
+ session, send messages within it, and cleanly shut it down.
120
+
121
+ After termination, the caller verifies the session is no longer active
122
+ by attempting to send a message to the terminated session.
123
+
124
+ steps:
125
+ - id: initiate_session
126
+ title: "Initiate SI session"
127
+ narrative: |
128
+ The caller starts a new conversational session with an offering. The agent
129
+ provisions the session and returns a session_id, an initial response message,
130
+ and negotiated capabilities describing what the session supports.
131
+ task: si_initiate_session
132
+ stateful: true
133
+ expected: |
134
+ Return the session with:
135
+ - session_id: unique identifier for this session
136
+ - response: initial message and optional ui_elements
137
+ - negotiated_capabilities: what modalities were agreed upon
138
+
139
+ sample_request:
140
+ offering_id: "e2e-test-offering"
141
+ offering_token: "$context.offering_token"
142
+ identity:
143
+ principal: "e2e-test-principal"
144
+ device_id: "e2e-test-device"
145
+ context: "Initiating conversation about products"
146
+ placement: "e2e-test-placement"
147
+ supported_capabilities:
148
+ modalities:
149
+ conversational: true
150
+ rich_media: true
151
+
152
+ context_inputs:
153
+ - key: "offering_token"
154
+ inject_at: "offering_token"
155
+
156
+ validations:
157
+ - check: field_present
158
+ path: "session_id"
159
+ description: "Response includes a session_id"
160
+
161
+ context_outputs:
162
+ - path: "session_id"
163
+ key: "session_id"
164
+
165
+ - id: send_message_1
166
+ title: "Send message: product inquiry"
167
+ requires_tool: si_send_message
168
+ narrative: |
169
+ The caller sends a product inquiry message within the active session.
170
+ The agent responds with product information and optional UI elements
171
+ such as product cards or carousels.
172
+ task: si_send_message
173
+ stateful: true
174
+ expected: |
175
+ Return the message response:
176
+ - session_id: matches the active session
177
+ - session_status: active
178
+ - response: message text and optional ui_elements
179
+
180
+ sample_request:
181
+ session_id: "$context.session_id"
182
+ message: "What products do you have available?"
183
+ metadata:
184
+ test_iteration: 1
185
+
186
+ context_inputs:
187
+ - key: "session_id"
188
+ inject_at: "session_id"
189
+
190
+ validations:
191
+ - check: field_present
192
+ path: "session_id"
193
+ description: "Response includes the session_id"
194
+
195
+ - id: send_message_2
196
+ title: "Send message: product details"
197
+ requires_tool: si_send_message
198
+ narrative: |
199
+ The caller asks a follow-up question about a specific product.
200
+ The agent should maintain conversational context from the previous
201
+ message and respond with more detailed information.
202
+ task: si_send_message
203
+ stateful: true
204
+ expected: |
205
+ Return a contextual response continuing the conversation:
206
+ - session_status: active
207
+ - response: detailed product information
208
+
209
+ sample_request:
210
+ session_id: "$context.session_id"
211
+ message: "Can you tell me more about your best seller?"
212
+ metadata:
213
+ test_iteration: 2
214
+
215
+ context_inputs:
216
+ - key: "session_id"
217
+ inject_at: "session_id"
218
+
219
+ validations:
220
+ - check: field_present
221
+ path: "session_id"
222
+ description: "Response includes the session_id"
223
+
224
+ - id: terminate_session
225
+ title: "Terminate SI session"
226
+ requires_tool: si_terminate_session
227
+ narrative: |
228
+ The caller ends the session with a user_exit reason. The agent
229
+ confirms termination and cleans up session state.
230
+ task: si_terminate_session
231
+ stateful: true
232
+ expected: |
233
+ Confirm session termination:
234
+ - session_id: matches the terminated session
235
+ - terminated: true
236
+
237
+ sample_request:
238
+ session_id: "$context.session_id"
239
+ reason: "user_exit"
240
+ termination_context:
241
+ summary: "Session completed successfully"
242
+
243
+ context_inputs:
244
+ - key: "session_id"
245
+ inject_at: "session_id"
246
+
247
+ validations:
248
+ - check: field_present
249
+ path: "terminated"
250
+ description: "Response confirms termination"
251
+
252
+ - id: send_to_terminated
253
+ title: "Send message to terminated session (error expected)"
254
+ requires_tool: si_send_message
255
+ expect_error: true
256
+ narrative: |
257
+ The caller attempts to send a message to the terminated session.
258
+ The agent should reject the message or return a terminated session
259
+ status. This validates that session cleanup is enforced.
260
+ task: si_send_message
261
+ stateful: true
262
+ expected: |
263
+ Reject the message or return session_status of "terminated" or "complete".
264
+ The agent must not accept new messages on a terminated session.
265
+
266
+ sample_request:
267
+ session_id: "$context.session_id"
268
+ message: "This should fail"
269
+
270
+ context_inputs:
271
+ - key: "session_id"
272
+ inject_at: "session_id"
273
+
274
+ validations: []
275
+
276
+ - id: handoff
277
+ title: "Transaction handoff"
278
+ narrative: |
279
+ The caller initiates a session, sends a purchase-intent message, and then
280
+ terminates with reason handoff_transaction. The agent may include an
281
+ acp_handoff structure in the termination response containing checkout URLs,
282
+ tokens, and transaction metadata.
283
+
284
+ This tests the commerce integration path where conversational AI transitions
285
+ to a purchase flow.
286
+
287
+ steps:
288
+ - id: initiate_handoff_session
289
+ title: "Initiate SI session (handoff flow)"
290
+ narrative: |
291
+ The caller starts a new session specifically for testing the handoff flow.
292
+ This is a separate session from the lifecycle phase.
293
+ task: si_initiate_session
294
+ stateful: true
295
+ expected: |
296
+ Return a new session with a session_id for the handoff test.
297
+
298
+ sample_request:
299
+ offering_id: "e2e-test-offering"
300
+ offering_token: "$context.offering_token"
301
+ identity:
302
+ principal: "e2e-test-principal"
303
+ device_id: "e2e-test-device"
304
+ context: "Initiating session for handoff test"
305
+ placement: "e2e-test-placement"
306
+ supported_capabilities:
307
+ modalities:
308
+ conversational: true
309
+
310
+ context_inputs:
311
+ - key: "offering_token"
312
+ inject_at: "offering_token"
313
+
314
+ validations:
315
+ - check: field_present
316
+ path: "session_id"
317
+ description: "Response includes a session_id"
318
+
319
+ context_outputs:
320
+ - path: "session_id"
321
+ key: "handoff_session_id"
322
+
323
+ - id: send_purchase_intent
324
+ title: "Send purchase intent message"
325
+ requires_tool: si_send_message
326
+ narrative: |
327
+ The caller sends a message expressing purchase intent. This primes the
328
+ agent for a transaction handoff in the next step.
329
+ task: si_send_message
330
+ stateful: true
331
+ expected: |
332
+ Accept the purchase intent message. The agent may begin preparing
333
+ transaction details in anticipation of a handoff.
334
+
335
+ sample_request:
336
+ session_id: "$context.handoff_session_id"
337
+ message: "I'd like to purchase this product. Can you set up a transaction?"
338
+
339
+ context_inputs:
340
+ - key: "handoff_session_id"
341
+ inject_at: "session_id"
342
+
343
+ validations:
344
+ - check: field_present
345
+ path: "session_id"
346
+ description: "Response includes the session_id"
347
+
348
+ - id: terminate_with_handoff
349
+ title: "Terminate with handoff_transaction"
350
+ requires_tool: si_terminate_session
351
+ narrative: |
352
+ The caller terminates the session with reason handoff_transaction and
353
+ includes transaction intent metadata. The agent confirms termination and
354
+ may return an acp_handoff structure with checkout details.
355
+ task: si_terminate_session
356
+ stateful: true
357
+ expected: |
358
+ Confirm termination and optionally return an acp_handoff object:
359
+ - terminated: true
360
+ - acp_handoff (optional):
361
+ - checkout_url: URL for the purchase flow
362
+ - checkout_token: opaque token for the transaction
363
+ - payload: additional transaction data
364
+ - expires_at: when the handoff expires
365
+
366
+ If the agent does not support transaction handoff, terminated: true
367
+ without acp_handoff is acceptable.
368
+
369
+ sample_request:
370
+ session_id: "$context.handoff_session_id"
371
+ reason: "handoff_transaction"
372
+ termination_context:
373
+ summary: "Terminating for handoff validation"
374
+ transaction_intent:
375
+ intent: "purchase"
376
+
377
+ context_inputs:
378
+ - key: "handoff_session_id"
379
+ inject_at: "session_id"
380
+
381
+ validations:
382
+ - check: field_present
383
+ path: "terminated"
384
+ description: "Response confirms termination"
@@ -0,0 +1,283 @@
1
+ id: signal_marketplace
2
+ version: "1.0.0"
3
+ title: "Marketplace signal agent"
4
+ category: signal_marketplace
5
+ summary: "Signal agent that resells third-party data provider signals with verifiable catalog provenance."
6
+ platform_types:
7
+ - pmax_platform
8
+ - ai_ad_network
9
+ - ai_platform
10
+
11
+ track: signals
12
+ required_tools:
13
+ - get_signals
14
+ narrative: |
15
+ You operate a signal marketplace — an intermediary that aggregates audience data from
16
+ multiple third-party providers and makes it available to buyers through a single interface.
17
+ Think LiveRamp Data Marketplace, Oracle Data Cloud, or Lotame.
18
+
19
+ Your agent searches across catalogs published by data providers in their adagents.json
20
+ files. Buyers discover signals through natural language queries, verify provenance by
21
+ checking the data provider's catalog directly, and activate signals on DSPs or sales
22
+ agents for campaign targeting.
23
+
24
+ The key property of marketplace signals: provenance is independently verifiable. Each
25
+ signal traces back to a data_provider_domain whose adagents.json lists your agent as
26
+ authorized. Buyers can (and should) verify this before spending.
27
+
28
+ This storyboard walks through discovery, verification, and both activation patterns —
29
+ activating directly on a DSP (buyer manages targeting) and activating on a sales agent
30
+ (SA handles downstream coordination).
31
+
32
+ agent:
33
+ interaction_model: marketplace_catalog
34
+ capabilities:
35
+ - catalog_signals
36
+ examples:
37
+ - "LiveRamp Data Marketplace"
38
+ - "Oracle Data Cloud"
39
+ - "Lotame"
40
+
41
+ caller:
42
+ role: buyer_agent
43
+ example: "Pinnacle Agency (buyer)"
44
+
45
+ prerequisites:
46
+ description: |
47
+ The buyer has a campaign brief with targeting objectives. The test kit provides
48
+ sample signal definitions, pricing options, and destination configurations that
49
+ match the training agent's signal providers.
50
+ test_kit: "test-kits/nova-motors.yaml"
51
+
52
+ phases:
53
+ - id: discovery
54
+ title: "Signal discovery"
55
+ narrative: |
56
+ The buyer describes what they need in natural language. Your agent searches
57
+ across all authorized data provider catalogs and returns matching signals with
58
+ pricing, coverage estimates, and value types.
59
+
60
+ This is where marketplace agents earn their keep — the buyer doesn't need to
61
+ know which providers exist or what taxonomies they use. One query, many sources.
62
+
63
+ steps:
64
+ - id: search_by_spec
65
+ title: "Discover signals from a campaign brief"
66
+ narrative: |
67
+ The buyer's platform translates a campaign brief into a get_signals call.
68
+ Your agent searches catalogs from every authorized data provider and returns
69
+ what matches — automotive intent from one provider, geo data from another,
70
+ retail purchase history from a third.
71
+ task: get_signals
72
+ schema_ref: "signals/get-signals-request.json"
73
+ response_schema_ref: "signals/get-signals-response.json"
74
+ doc_ref: "/signals/tasks/get_signals"
75
+ stateful: false
76
+ expected: |
77
+ Return matching signals from multiple data providers. Each signal must include:
78
+ - signal_agent_segment_id for activation
79
+ - signal_id with source, data_provider_domain, and id
80
+ - name, description, and value_type (binary, categorical, or numeric)
81
+ - coverage_percentage (audience reach estimate)
82
+ - pricing_options with at least one pricing model
83
+ - signal_type: "marketplace"
84
+
85
+ sample_request:
86
+ signal_spec: "In-market EV buyers with high purchase propensity, near auto dealerships"
87
+
88
+ validations:
89
+ - check: response_schema
90
+ description: "Response matches get-signals-response.json schema"
91
+ - check: field_present
92
+ path: "signals[0].signal_agent_segment_id"
93
+ description: "Each signal has a signal_agent_segment_id"
94
+ - check: field_present
95
+ path: "signals[0].signal_id.data_provider_domain"
96
+ description: "Each signal traces to a data provider domain"
97
+ - check: field_present
98
+ path: "signals[0].pricing_options"
99
+ description: "Each signal has pricing options"
100
+ - check: field_present
101
+ path: "signals[0].coverage_percentage"
102
+ description: "Each signal has a coverage estimate"
103
+
104
+ - id: search_by_ids
105
+ title: "Look up specific signals by ID"
106
+ narrative: |
107
+ The buyer already knows which signals they want — maybe from a previous
108
+ campaign or a provider's documentation. They pass signal_ids directly
109
+ instead of a natural language query.
110
+ task: get_signals
111
+ schema_ref: "signals/get-signals-request.json"
112
+ response_schema_ref: "signals/get-signals-response.json"
113
+ doc_ref: "/signals/tasks/get_signals"
114
+ stateful: false
115
+ expected: |
116
+ Return the exact signals requested, with full metadata and pricing.
117
+ If a signal_id doesn't exist, omit it from results — don't error.
118
+
119
+ sample_request:
120
+ signal_ids:
121
+ - source: "catalog"
122
+ data_provider_domain: "tridentauto.example"
123
+ id: "likely_ev_buyers"
124
+ - source: "catalog"
125
+ data_provider_domain: "meridiangeo.example"
126
+ id: "competitor_visitors"
127
+
128
+ validations:
129
+ - check: response_schema
130
+ description: "Response matches schema"
131
+ - check: field_present
132
+ path: "signals"
133
+ description: "Response contains a signals array"
134
+
135
+ - id: verification
136
+ title: "Catalog verification"
137
+ narrative: |
138
+ Before activating third-party data, the buyer verifies provenance. They fetch
139
+ the data provider's adagents.json directly and confirm the signal exists and
140
+ your agent is authorized. This independent check is outside the AdCP protocol —
141
+ but your agent must return the metadata that makes it possible.
142
+
143
+ This phase tests that your get_signals responses include verifiable provenance
144
+ data: signal_id.source is "catalog" and signal_id.data_provider_domain points
145
+ to a real domain whose adagents.json the buyer can fetch independently.
146
+
147
+ steps:
148
+ - id: verify_provenance_metadata
149
+ title: "Confirm signals carry verifiable provenance"
150
+ narrative: |
151
+ The buyer looks up a specific signal by ID and checks that the response
152
+ includes the metadata needed for independent verification — source is
153
+ "catalog" and data_provider_domain points to a fetchable adagents.json.
154
+ The actual HTTP fetch of adagents.json is the buyer's responsibility, but
155
+ your agent must provide the domain to fetch from.
156
+ task: get_signals
157
+ schema_ref: "signals/get-signals-request.json"
158
+ response_schema_ref: "signals/get-signals-response.json"
159
+ doc_ref: "/signals/data-providers"
160
+ stateful: false
161
+ expected: |
162
+ Return the requested signal with verifiable provenance metadata:
163
+ - signal_id.source is "catalog"
164
+ - signal_id.data_provider_domain matches a real domain
165
+ The buyer will independently fetch that domain's adagents.json to confirm
166
+ your agent is listed in authorized_agents.
167
+
168
+ sample_request:
169
+ signal_ids:
170
+ - source: "catalog"
171
+ data_provider_domain: "shopgrid.example"
172
+ id: "new_to_brand"
173
+
174
+ validations:
175
+ - check: field_value
176
+ path: "signals[0].signal_id.source"
177
+ description: "Signal source is 'catalog' (verifiable via adagents.json)"
178
+ - check: field_present
179
+ path: "signals[0].signal_id.data_provider_domain"
180
+ description: "Data provider domain is present for independent verification"
181
+
182
+ - id: platform_activation
183
+ title: "Activate on a DSP"
184
+ narrative: |
185
+ The buyer activates a signal directly on a DSP platform. The signal agent pushes
186
+ segment data to the platform, and the buyer gets back a segment_id they can
187
+ reference when configuring campaign targeting.
188
+
189
+ Use platform destinations when the buyer is managing DSP campaigns directly —
190
+ not through a sales agent.
191
+
192
+ steps:
193
+ - id: activate_on_platform
194
+ title: "Activate signal on a DSP"
195
+ narrative: |
196
+ The buyer selects a signal and a DSP destination. The signal agent pushes
197
+ the segment to the platform. This is typically asynchronous — the initial
198
+ response shows is_live: false with an estimated duration, and the buyer
199
+ polls for completion.
200
+ task: activate_signal
201
+ schema_ref: "signals/activate-signal-request.json"
202
+ response_schema_ref: "signals/activate-signal-response.json"
203
+ doc_ref: "/signals/tasks/activate_signal"
204
+ stateful: true
205
+ expected: |
206
+ Return a deployment with:
207
+ - type: "platform" matching the requested destination
208
+ - is_live: false initially (async activation)
209
+ - estimated_activation_duration_minutes
210
+ After polling, the deployment should show:
211
+ - is_live: true
212
+ - activation_key with type: "segment_id" and a platform-native segment ID
213
+ - deployed_at timestamp
214
+
215
+ sample_request:
216
+ signal_agent_segment_id: "trident_likely_ev_buyers"
217
+ pricing_option_id: "po_trident_ev_cpm"
218
+ destinations:
219
+ - type: "platform"
220
+ platform: "the-trade-desk"
221
+ account: "agency-123-ttd"
222
+
223
+ validations:
224
+ - check: response_schema
225
+ description: "Response matches activate-signal-response.json schema"
226
+ - check: field_present
227
+ path: "deployments[0].type"
228
+ description: "Deployment includes type"
229
+ - check: field_value
230
+ path: "deployments[0].type"
231
+ description: "Deployment type is 'platform'"
232
+
233
+ - id: agent_activation
234
+ title: "Activate on a sales agent"
235
+ narrative: |
236
+ The buyer activates a signal on a sales agent instead of a DSP. This is the
237
+ right pattern when the buyer is purchasing media through the SA — the SA handles
238
+ its own DSP coordination.
239
+
240
+ The buyer doesn't need to know which DSP the SA uses. The activation key confirms
241
+ the signal is live on the SA, and the SA applies targeting when fulfilling media
242
+ buys through create_media_buy.
243
+
244
+ steps:
245
+ - id: activate_on_agent
246
+ title: "Activate signal on a sales agent"
247
+ narrative: |
248
+ The buyer activates a signal with the sales agent's URL as the destination.
249
+ Agent activations are typically synchronous — the SA records the activation
250
+ immediately and returns a key_value activation key.
251
+ task: activate_signal
252
+ schema_ref: "signals/activate-signal-request.json"
253
+ response_schema_ref: "signals/activate-signal-response.json"
254
+ doc_ref: "/signals/tasks/activate_signal"
255
+ stateful: true
256
+ expected: |
257
+ Return a deployment with:
258
+ - type: "agent" matching the requested destination
259
+ - agent_url matching the SA's URL
260
+ - is_live: true (sync activation)
261
+ - activation_key with type: "key_value"
262
+ - deployed_at timestamp
263
+
264
+ The SA records the activation internally. When the buyer later calls
265
+ create_media_buy through this SA, signal-based targeting is already
266
+ in place.
267
+
268
+ sample_request:
269
+ signal_agent_segment_id: "shopgrid_new_to_brand"
270
+ pricing_option_id: "po_shopgrid_retail_cpm"
271
+ destinations:
272
+ - type: "agent"
273
+ agent_url: "https://wonderstruck.salesagents.example"
274
+
275
+ validations:
276
+ - check: response_schema
277
+ description: "Response matches activate-signal-response.json schema"
278
+ - check: field_present
279
+ path: "deployments[0].activation_key"
280
+ description: "Deployment includes activation key"
281
+ - check: field_value
282
+ path: "deployments[0].type"
283
+ description: "Deployment type is 'agent'"