@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.
- package/AGENTS.md +278 -0
- package/README.md +96 -61
- package/bin/adcp.js +342 -4
- package/dist/lib/agents/index.generated.d.ts +9 -1
- package/dist/lib/agents/index.generated.d.ts.map +1 -1
- package/dist/lib/agents/index.generated.js +12 -0
- package/dist/lib/agents/index.generated.js.map +1 -1
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.ts +2 -1
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +10 -1
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/discovery/property-crawler.d.ts +4 -0
- package/dist/lib/discovery/property-crawler.d.ts.map +1 -1
- package/dist/lib/discovery/property-crawler.js +10 -2
- package/dist/lib/discovery/property-crawler.js.map +1 -1
- package/dist/lib/index.d.ts +4 -4
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +6 -4
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/protocols/index.d.ts.map +1 -1
- package/dist/lib/protocols/index.js +8 -6
- package/dist/lib/protocols/index.js.map +1 -1
- package/dist/lib/protocols/mcp.d.ts.map +1 -1
- package/dist/lib/protocols/mcp.js +24 -11
- package/dist/lib/protocols/mcp.js.map +1 -1
- package/dist/lib/server/index.d.ts +2 -0
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +3 -1
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/serve.d.ts +73 -0
- package/dist/lib/server/serve.d.ts.map +1 -0
- package/dist/lib/server/serve.js +94 -0
- package/dist/lib/server/serve.js.map +1 -0
- package/dist/lib/testing/client.d.ts.map +1 -1
- package/dist/lib/testing/client.js +1 -0
- package/dist/lib/testing/client.js.map +1 -1
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
- package/dist/lib/testing/compliance/comply.js +48 -63
- package/dist/lib/testing/compliance/comply.js.map +1 -1
- package/dist/lib/testing/compliance/storyboard-tracks.d.ts +24 -0
- package/dist/lib/testing/compliance/storyboard-tracks.d.ts.map +1 -0
- package/dist/lib/testing/compliance/storyboard-tracks.js +157 -0
- package/dist/lib/testing/compliance/storyboard-tracks.js.map +1 -0
- package/dist/lib/testing/compliance/types.d.ts +1 -1
- package/dist/lib/testing/compliance/types.d.ts.map +1 -1
- package/dist/lib/testing/index.d.ts +1 -0
- package/dist/lib/testing/index.d.ts.map +1 -1
- package/dist/lib/testing/index.js +23 -1
- package/dist/lib/testing/index.js.map +1 -1
- package/dist/lib/testing/orchestrator.d.ts +8 -0
- package/dist/lib/testing/orchestrator.d.ts.map +1 -1
- package/dist/lib/testing/orchestrator.js +8 -0
- package/dist/lib/testing/orchestrator.js.map +1 -1
- package/dist/lib/testing/storyboard/context.d.ts +34 -0
- package/dist/lib/testing/storyboard/context.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/context.js +257 -0
- package/dist/lib/testing/storyboard/context.js.map +1 -0
- package/dist/lib/testing/storyboard/index.d.ts +15 -0
- package/dist/lib/testing/storyboard/index.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/index.js +48 -0
- package/dist/lib/testing/storyboard/index.js.map +1 -0
- package/dist/lib/testing/storyboard/loader.d.ts +53 -0
- package/dist/lib/testing/storyboard/loader.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/loader.js +114 -0
- package/dist/lib/testing/storyboard/loader.js.map +1 -0
- package/dist/lib/testing/storyboard/path.d.ts +29 -0
- package/dist/lib/testing/storyboard/path.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/path.js +121 -0
- package/dist/lib/testing/storyboard/path.js.map +1 -0
- package/dist/lib/testing/storyboard/request-builder.d.ts +28 -0
- package/dist/lib/testing/storyboard/request-builder.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/request-builder.js +410 -0
- package/dist/lib/testing/storyboard/request-builder.js.map +1 -0
- package/dist/lib/testing/storyboard/runner.d.ts +24 -0
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/runner.js +280 -0
- package/dist/lib/testing/storyboard/runner.js.map +1 -0
- package/dist/lib/testing/storyboard/task-map.d.ts +21 -0
- package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/task-map.js +84 -0
- package/dist/lib/testing/storyboard/task-map.js.map +1 -0
- package/dist/lib/testing/storyboard/types.d.ts +156 -0
- package/dist/lib/testing/storyboard/types.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/types.js +10 -0
- package/dist/lib/testing/storyboard/types.js.map +1 -0
- package/dist/lib/testing/storyboard/validations.d.ts +17 -0
- package/dist/lib/testing/storyboard/validations.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/validations.js +166 -0
- package/dist/lib/testing/storyboard/validations.js.map +1 -0
- package/dist/lib/testing/types.d.ts +2 -0
- package/dist/lib/testing/types.d.ts.map +1 -1
- package/dist/lib/types/core.generated.d.ts +2 -2
- package/dist/lib/types/core.generated.d.ts.map +1 -1
- package/dist/lib/types/core.generated.js +1 -1
- package/dist/lib/types/schemas.generated.d.ts +193 -34
- package/dist/lib/types/schemas.generated.d.ts.map +1 -1
- package/dist/lib/types/schemas.generated.js +87 -5
- package/dist/lib/types/schemas.generated.js.map +1 -1
- package/dist/lib/types/tools.generated.d.ts +280 -3
- package/dist/lib/types/tools.generated.d.ts.map +1 -1
- package/dist/lib/utils/response-schemas.d.ts.map +1 -1
- package/dist/lib/utils/response-schemas.js +34 -3
- package/dist/lib/utils/response-schemas.js.map +1 -1
- package/dist/lib/utils/validate-user-agent.d.ts +8 -0
- package/dist/lib/utils/validate-user-agent.d.ts.map +1 -0
- package/dist/lib/utils/validate-user-agent.js +15 -0
- package/dist/lib/utils/validate-user-agent.js.map +1 -0
- package/dist/lib/version.d.ts +6 -0
- package/dist/lib/version.d.ts.map +1 -1
- package/dist/lib/version.js +7 -1
- package/dist/lib/version.js.map +1 -1
- package/docs/README.md +42 -0
- package/docs/guides/BUILD-AN-AGENT.md +294 -0
- package/docs/llms.txt +634 -0
- package/examples/README.md +106 -0
- package/examples/adcp.config.json +30 -0
- package/examples/basic-a2a.ts +76 -0
- package/examples/basic-mcp.ts +50 -0
- package/examples/batch-preview-test.ts +266 -0
- package/examples/conversation-client.ts +291 -0
- package/examples/debug-preview-response.ts +73 -0
- package/examples/debug-preview-with-logging.ts +50 -0
- package/examples/easy-config-demo.ts +242 -0
- package/examples/env-config.ts +51 -0
- package/examples/error-compliant-server.ts +237 -0
- package/examples/generative-creative-demo.ts +205 -0
- package/examples/inspect-card-formats.ts +161 -0
- package/examples/logger-usage.ts +165 -0
- package/examples/oauth-cli-example.ts +154 -0
- package/examples/pr78-async-patterns-demo.ts +247 -0
- package/examples/signals-agent.ts +163 -0
- package/examples/simple-getting-started.ts +225 -0
- package/examples/simple-protocol-demo.ts +75 -0
- package/examples/test-helpers-demo.ts +239 -0
- package/examples/zod-validation-example.ts +126 -0
- package/package.json +12 -2
- package/skills/adcp/SKILL.md +13 -2
- package/storyboards/audience_sync.yaml +199 -0
- package/storyboards/behavioral_analysis.yaml +244 -0
- package/storyboards/brand_rights.yaml +131 -0
- package/storyboards/creative_ad_server.yaml +171 -0
- package/storyboards/creative_sales_agent.yaml +169 -0
- package/storyboards/creative_template.yaml +306 -0
- package/storyboards/deterministic_testing.yaml +925 -0
- package/storyboards/error_compliance.yaml +231 -0
- package/storyboards/governance_content_standards.yaml +213 -0
- package/storyboards/governance_property_lists.yaml +372 -0
- package/storyboards/media_buy_catalog_creative.yaml +457 -0
- package/storyboards/media_buy_governance_escalation.yaml +467 -0
- package/storyboards/media_buy_guaranteed_approval.yaml +396 -0
- package/storyboards/media_buy_non_guaranteed.yaml +288 -0
- package/storyboards/media_buy_proposal_mode.yaml +369 -0
- package/storyboards/media_buy_seller.yaml +560 -0
- package/storyboards/media_buy_state_machine.yaml +254 -0
- package/storyboards/schema.yaml +65 -0
- package/storyboards/schema_validation.yaml +166 -0
- package/storyboards/si_session.yaml +384 -0
- package/storyboards/signal_marketplace.yaml +283 -0
- 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'"
|