@adcp/client 4.22.1 → 4.24.0
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/README.md +23 -9
- package/bin/adcp.js +83 -18
- package/dist/lib/index.d.ts +3 -5
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +16 -12
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/server/index.d.ts +5 -1
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +10 -1
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/postgres-task-store.d.ts +105 -0
- package/dist/lib/server/postgres-task-store.d.ts.map +1 -0
- package/dist/lib/server/postgres-task-store.js +267 -0
- package/dist/lib/server/postgres-task-store.js.map +1 -0
- package/dist/lib/server/responses.d.ts +1 -0
- package/dist/lib/server/responses.d.ts.map +1 -1
- package/dist/lib/server/responses.js +1 -0
- package/dist/lib/server/responses.js.map +1 -1
- package/dist/lib/server/test-controller.d.ts +88 -0
- package/dist/lib/server/test-controller.d.ts.map +1 -0
- package/dist/lib/server/test-controller.js +227 -0
- package/dist/lib/server/test-controller.js.map +1 -0
- package/dist/lib/testing/agent-tester.d.ts +1 -1
- package/dist/lib/testing/agent-tester.d.ts.map +1 -1
- package/dist/lib/testing/agent-tester.js +13 -1
- package/dist/lib/testing/agent-tester.js.map +1 -1
- package/dist/lib/testing/compliance/comply.d.ts +24 -5
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
- package/dist/lib/testing/compliance/comply.js +318 -277
- package/dist/lib/testing/compliance/comply.js.map +1 -1
- package/dist/lib/testing/compliance/index.d.ts +2 -1
- package/dist/lib/testing/compliance/index.d.ts.map +1 -1
- package/dist/lib/testing/compliance/index.js +6 -1
- package/dist/lib/testing/compliance/index.js.map +1 -1
- package/dist/lib/testing/compliance/platform-storyboards.d.ts +44 -0
- package/dist/lib/testing/compliance/platform-storyboards.d.ts.map +1 -0
- package/dist/lib/testing/compliance/platform-storyboards.js +232 -0
- package/dist/lib/testing/compliance/platform-storyboards.js.map +1 -0
- package/dist/lib/testing/compliance/storyboard-tracks.d.ts +2 -9
- package/dist/lib/testing/compliance/storyboard-tracks.d.ts.map +1 -1
- package/dist/lib/testing/compliance/storyboard-tracks.js +15 -46
- package/dist/lib/testing/compliance/storyboard-tracks.js.map +1 -1
- package/dist/lib/testing/compliance/types.d.ts +22 -1
- package/dist/lib/testing/compliance/types.d.ts.map +1 -1
- package/dist/lib/testing/index.d.ts +1 -1
- package/dist/lib/testing/index.d.ts.map +1 -1
- package/dist/lib/testing/index.js +6 -1
- package/dist/lib/testing/index.js.map +1 -1
- package/dist/lib/testing/orchestrator.d.ts.map +1 -1
- package/dist/lib/testing/orchestrator.js +5 -1
- package/dist/lib/testing/orchestrator.js.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.d.ts +19 -1
- package/dist/lib/testing/scenarios/brand-rights.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.js +138 -1
- package/dist/lib/testing/scenarios/brand-rights.js.map +1 -1
- package/dist/lib/testing/scenarios/deterministic.js +7 -7
- package/dist/lib/testing/scenarios/deterministic.js.map +1 -1
- package/dist/lib/testing/scenarios/index.d.ts +1 -1
- package/dist/lib/testing/scenarios/index.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/index.js +4 -2
- package/dist/lib/testing/scenarios/index.js.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.js +4 -4
- package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
- package/dist/lib/testing/storyboard/loader.d.ts +1 -0
- package/dist/lib/testing/storyboard/loader.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/loader.js +14 -0
- package/dist/lib/testing/storyboard/loader.js.map +1 -1
- package/dist/lib/testing/storyboard/request-builder.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/request-builder.js +88 -11
- package/dist/lib/testing/storyboard/request-builder.js.map +1 -1
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +83 -5
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/testing/storyboard/task-map.d.ts +2 -0
- package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/task-map.js +23 -9
- package/dist/lib/testing/storyboard/task-map.js.map +1 -1
- package/dist/lib/testing/storyboard/types.d.ts +6 -2
- package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/validations.js +21 -4
- package/dist/lib/testing/storyboard/validations.js.map +1 -1
- package/dist/lib/testing/types.d.ts +1 -1
- package/dist/lib/testing/types.d.ts.map +1 -1
- package/dist/lib/types/core.generated.d.ts +242 -3
- 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 +3697 -3468
- package/dist/lib/types/schemas.generated.d.ts.map +1 -1
- package/dist/lib/types/schemas.generated.js +226 -118
- package/dist/lib/types/schemas.generated.js.map +1 -1
- package/dist/lib/types/tools.generated.d.ts +281 -79
- package/dist/lib/types/tools.generated.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.d.ts +2 -2
- package/dist/lib/utils/capabilities.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.js +9 -3
- package/dist/lib/utils/capabilities.js.map +1 -1
- package/dist/lib/utils/response-schemas.d.ts.map +1 -1
- package/dist/lib/utils/response-schemas.js +9 -0
- package/dist/lib/utils/response-schemas.js.map +1 -1
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/docs/llms.txt +56 -32
- package/package.json +8 -2
- package/skills/adcp/SKILL.md +118 -33
- package/skills/build-creative-agent/SKILL.md +221 -0
- package/skills/build-generative-seller-agent/SKILL.md +288 -0
- package/skills/build-retail-media-agent/SKILL.md +237 -0
- package/skills/build-seller-agent/SKILL.md +313 -0
- package/skills/build-signals-agent/SKILL.md +203 -0
- package/storyboards/audience_sync.yaml +18 -29
- package/storyboards/behavioral_analysis.yaml +40 -72
- package/storyboards/brand_rights.yaml +172 -75
- package/storyboards/campaign_governance_conditions.yaml +187 -0
- package/storyboards/campaign_governance_delivery.yaml +231 -0
- package/storyboards/campaign_governance_denied.yaml +136 -0
- package/storyboards/capability_discovery.yaml +106 -0
- package/storyboards/content_standards.yaml +251 -0
- package/storyboards/creative_ad_server.yaml +108 -16
- package/storyboards/creative_generative.yaml +317 -0
- package/storyboards/creative_lifecycle.yaml +284 -0
- package/storyboards/creative_sales_agent.yaml +2 -6
- package/storyboards/creative_template.yaml +3 -6
- package/storyboards/deterministic_testing.yaml +271 -245
- package/storyboards/error_compliance.yaml +105 -108
- package/storyboards/media_buy_catalog_creative.yaml +8 -5
- package/storyboards/media_buy_generative_seller.yaml +581 -0
- package/storyboards/media_buy_governance_escalation.yaml +10 -6
- package/storyboards/media_buy_guaranteed_approval.yaml +21 -19
- package/storyboards/media_buy_non_guaranteed.yaml +9 -8
- package/storyboards/media_buy_proposal_mode.yaml +12 -11
- package/storyboards/media_buy_seller.yaml +161 -173
- package/storyboards/media_buy_state_machine.yaml +102 -101
- package/storyboards/property_governance.yaml +239 -0
- package/storyboards/schema.yaml +3 -2
- package/storyboards/schema_validation.yaml +58 -51
- package/storyboards/si_session.yaml +99 -317
- package/storyboards/signal_marketplace.yaml +9 -5
- package/storyboards/signal_owned.yaml +6 -5
- package/storyboards/social_platform.yaml +274 -0
- package/storyboards/test-kits/acme-outdoor.yaml +118 -0
- package/storyboards/test-kits/nova-motors.yaml +134 -0
- package/storyboards/governance_content_standards.yaml +0 -213
- package/storyboards/governance_property_lists.yaml +0 -372
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: build-signals-agent
|
|
3
|
+
description: Use when building an AdCP signals agent, creating an audience data server, or standing up a data provider agent that serves targeting segments to buyers.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Build a Signals Agent
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
A signals agent serves audience segments to buyers for campaign targeting. Two tools: `get_signals` (discovery) and `activate_signal` (push to DSPs or sales agents). The business model — marketplace vs owned data — shapes every implementation decision. Determine that first.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- User wants to build an agent that serves audience/targeting data
|
|
15
|
+
- User mentions signals, segments, audiences, data provider, or CDP in the context of AdCP
|
|
16
|
+
- User references `get_signals`, `activate_signal`, or the signals protocol
|
|
17
|
+
|
|
18
|
+
**Not this skill:**
|
|
19
|
+
- Selling ad inventory (products, packages, media buys) → `skills/build-seller-agent/`
|
|
20
|
+
- Rendering creatives from briefs → that's a creative agent
|
|
21
|
+
- Building a client that *calls* a signals agent → see `docs/getting-started.md`
|
|
22
|
+
|
|
23
|
+
## Before Writing Code
|
|
24
|
+
|
|
25
|
+
Determine these four things. Ask the user — don't guess.
|
|
26
|
+
|
|
27
|
+
### 1. Marketplace or Owned?
|
|
28
|
+
|
|
29
|
+
These are fundamentally different businesses.
|
|
30
|
+
|
|
31
|
+
**Marketplace** — aggregates third-party data providers (LiveRamp, Oracle Data Cloud, Lotame). Each signal traces to a `data_provider_domain` that buyers can verify via `adagents.json`. `signal_type: "marketplace"`, `signal_id.source: "catalog"`.
|
|
32
|
+
|
|
33
|
+
**Owned** — first-party data (retailer CDP, publisher contextual, CRM). Buyers trust your agent directly. `signal_type: "owned"` or `"custom"`, `signal_id.source: "agent"`.
|
|
34
|
+
|
|
35
|
+
### 2. What Segments?
|
|
36
|
+
|
|
37
|
+
Get specifics: names, definitions, what each represents. Push for 3-5 segments with variety. Each needs:
|
|
38
|
+
- Clear behavioral/demographic definition
|
|
39
|
+
- Realistic `coverage_percentage` (typically 5-30%)
|
|
40
|
+
- Value type: `binary` (in/out), `categorical` (tier levels — define the categories), or `numeric` (score range — define min/max)
|
|
41
|
+
|
|
42
|
+
### 3. Pricing
|
|
43
|
+
|
|
44
|
+
At least one pricing option per signal:
|
|
45
|
+
- `cpm` — `{ pricing_option_id: "po_cpm", model: "cpm", cpm: 2.50, currency: "USD" }`
|
|
46
|
+
- `percent_of_media` — `{ pricing_option_id: "po_pom", model: "percent_of_media", percent: 15, currency: "USD" }`
|
|
47
|
+
- `flat_fee` — `{ pricing_option_id: "po_flat", model: "flat_fee", amount: 5000, period: "monthly", currency: "USD" }`
|
|
48
|
+
|
|
49
|
+
### 4. Activation Destinations
|
|
50
|
+
|
|
51
|
+
If implementing `activate_signal`:
|
|
52
|
+
- **Platform** (DSP): `type: "platform"`, returns `activation_key: { type: "segment_id", segment_id: "..." }`
|
|
53
|
+
- **Agent** (sales agent): `type: "agent"`, returns `activation_key: { type: "key_value", key: "...", value: "..." }`
|
|
54
|
+
|
|
55
|
+
## Tools and Required Response Shapes
|
|
56
|
+
|
|
57
|
+
**`get_adcp_capabilities`** — register first, empty `{}` schema
|
|
58
|
+
```
|
|
59
|
+
capabilitiesResponse({
|
|
60
|
+
adcp: { major_versions: [3] },
|
|
61
|
+
supported_protocols: ['signals'],
|
|
62
|
+
})
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**`get_signals`** — `GetSignalsRequestSchema.shape`
|
|
66
|
+
|
|
67
|
+
Two discovery modes — support both:
|
|
68
|
+
1. `signal_spec` — natural language. Match against segment names and descriptions.
|
|
69
|
+
2. `signal_ids` — exact lookup by `{ source, data_provider_domain, id }` or `{ source, agent_url, id }`.
|
|
70
|
+
|
|
71
|
+
Plus filtering via `filters.catalog_types`, `filters.max_cpm`, `filters.min_coverage_percentage`, and `max_results`.
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
taskToolResponse({
|
|
75
|
+
signals: [{
|
|
76
|
+
signal_agent_segment_id: string, // required - key for activate_signal
|
|
77
|
+
name: string, // required
|
|
78
|
+
description: string, // required
|
|
79
|
+
signal_type: 'marketplace' | 'owned' | 'custom', // required
|
|
80
|
+
data_provider: string, // required - your company name
|
|
81
|
+
coverage_percentage: number, // required - 0 to 100
|
|
82
|
+
deployments: [], // required - empty array (not live until activated)
|
|
83
|
+
pricing_options: [{ // required - at least one
|
|
84
|
+
pricing_option_id: string, // required
|
|
85
|
+
model: 'cpm', // required - discriminator
|
|
86
|
+
cpm: number, // required for cpm model
|
|
87
|
+
currency: 'USD', // required
|
|
88
|
+
}],
|
|
89
|
+
// signal_id is critical — shape depends on marketplace vs owned:
|
|
90
|
+
signal_id: {
|
|
91
|
+
source: 'catalog', // marketplace
|
|
92
|
+
data_provider_domain: string, // marketplace — domain for provenance verification
|
|
93
|
+
id: string, // unique segment ID
|
|
94
|
+
},
|
|
95
|
+
// OR for owned:
|
|
96
|
+
signal_id: {
|
|
97
|
+
source: 'agent', // owned
|
|
98
|
+
agent_url: string, // your agent URL
|
|
99
|
+
id: string,
|
|
100
|
+
},
|
|
101
|
+
value_type: 'binary' | 'categorical' | 'numeric', // optional but recommended
|
|
102
|
+
}],
|
|
103
|
+
sandbox: true, // for mock data
|
|
104
|
+
})
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**`activate_signal`** — `ActivateSignalRequestSchema.shape`
|
|
108
|
+
|
|
109
|
+
Look up by `signal_agent_segment_id`. Validate `pricing_option_id`. Return deployments matching the requested destinations.
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
taskToolResponse({
|
|
113
|
+
deployments: [{
|
|
114
|
+
// Match the destination type from the request:
|
|
115
|
+
type: 'platform', // for platform destinations
|
|
116
|
+
platform: string, // echo from request destination
|
|
117
|
+
account: string | null, // echo from request
|
|
118
|
+
is_live: true, // signal is now active
|
|
119
|
+
activation_key: {
|
|
120
|
+
type: 'segment_id',
|
|
121
|
+
segment_id: string, // platform-specific segment ID
|
|
122
|
+
},
|
|
123
|
+
}],
|
|
124
|
+
// OR for agent destinations:
|
|
125
|
+
deployments: [{
|
|
126
|
+
type: 'agent',
|
|
127
|
+
agent_url: string,
|
|
128
|
+
is_live: true,
|
|
129
|
+
activation_key: {
|
|
130
|
+
type: 'key_value',
|
|
131
|
+
key: string,
|
|
132
|
+
value: string,
|
|
133
|
+
},
|
|
134
|
+
}],
|
|
135
|
+
sandbox: true,
|
|
136
|
+
})
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## SDK Quick Reference
|
|
140
|
+
|
|
141
|
+
| SDK piece | Usage |
|
|
142
|
+
|-----------|-------|
|
|
143
|
+
| `serve(createAgent)` | Start HTTP server on `:3001/mcp` |
|
|
144
|
+
| `createTaskCapableServer(name, version, { taskStore })` | Create MCP server with task support |
|
|
145
|
+
| `server.tool(name, Schema.shape, handler)` | Register tool — `.shape` unwraps Zod |
|
|
146
|
+
| `capabilitiesResponse(data)` | Build `get_adcp_capabilities` response |
|
|
147
|
+
| `taskToolResponse(data, summary)` | Build tool response |
|
|
148
|
+
| `adcpError(code, { message })` | Structured error (`SIGNAL_NOT_FOUND`, `INVALID_DESTINATION`) |
|
|
149
|
+
| `GetSignalsRequestSchema.shape` | Zod schema for get_signals input |
|
|
150
|
+
| `ActivateSignalRequestSchema.shape` | Zod schema for activate_signal input |
|
|
151
|
+
| `type Signal = GetSignalsResponse['signals'][number]` | Type for a single signal object |
|
|
152
|
+
|
|
153
|
+
Import everything from `@adcp/client`. Types from `@adcp/client` with `import type`.
|
|
154
|
+
|
|
155
|
+
## Implementation
|
|
156
|
+
|
|
157
|
+
1. Single `.ts` file — all tools in one file
|
|
158
|
+
2. Always register `get_adcp_capabilities` as the **first** tool with empty `{}` schema
|
|
159
|
+
3. Use `Schema.shape` (not `Schema`) when registering tools
|
|
160
|
+
4. Set `sandbox: true` for mock/demo data
|
|
161
|
+
5. Use `ServeContext` pattern: `function createAgent({ taskStore }: ServeContext)`
|
|
162
|
+
|
|
163
|
+
The skill contains everything you need. Do not read additional docs before writing code.
|
|
164
|
+
|
|
165
|
+
## Validation
|
|
166
|
+
|
|
167
|
+
**After writing the agent, validate it. Fix failures. Repeat.**
|
|
168
|
+
|
|
169
|
+
**Full validation** (if you can bind ports):
|
|
170
|
+
```bash
|
|
171
|
+
npx tsx agent.ts &
|
|
172
|
+
npx @adcp/client storyboard run http://localhost:3001/mcp signal_owned --json # for owned data
|
|
173
|
+
npx @adcp/client storyboard run http://localhost:3001/mcp signal_marketplace --json # for marketplace
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Sandbox validation** (if ports are blocked):
|
|
177
|
+
```bash
|
|
178
|
+
npx tsc --noEmit agent.ts
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Keep iterating until all steps pass.**
|
|
182
|
+
|
|
183
|
+
## Common Mistakes
|
|
184
|
+
|
|
185
|
+
| Mistake | Fix |
|
|
186
|
+
|---------|-----|
|
|
187
|
+
| Pass `Schema` instead of `Schema.shape` | MCP SDK needs unwrapped Zod fields |
|
|
188
|
+
| Skip `get_adcp_capabilities` | Must be the first tool registered |
|
|
189
|
+
| Missing `signal_agent_segment_id` on signals | Buyers can't activate without it |
|
|
190
|
+
| Wrong `signal_id` shape | Marketplace: `{ source: "catalog", data_provider_domain, id }`. Owned: `{ source: "agent", agent_url, id }` |
|
|
191
|
+
| Missing `data_provider` field | Required on every signal — your company/brand name |
|
|
192
|
+
| Empty `pricing_options` array | Must have at least one pricing option per signal |
|
|
193
|
+
| `is_live: true` in get_signals deployments | Signals aren't live until `activate_signal` — use empty `deployments: []` |
|
|
194
|
+
| Activation doesn't match destination type | If request has `type: "platform"`, deployment must be `type: "platform"` |
|
|
195
|
+
| `sandbox: false` on mock data | Buyers may treat mock data as real |
|
|
196
|
+
|
|
197
|
+
## Reference
|
|
198
|
+
|
|
199
|
+
- `examples/signals-agent.ts` — complete runnable example
|
|
200
|
+
- `storyboards/signal_marketplace.yaml` — buyer call sequences for marketplace agent
|
|
201
|
+
- `storyboards/signal_owned.yaml` — call sequences for owned data agent
|
|
202
|
+
- `docs/guides/BUILD-AN-AGENT.md` — SDK patterns
|
|
203
|
+
- `docs/llms.txt` — full protocol reference
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
id: audience_sync
|
|
2
2
|
version: "1.0.0"
|
|
3
3
|
title: "Audience sync"
|
|
4
|
-
category:
|
|
5
|
-
summary: "
|
|
4
|
+
category: audience_sync
|
|
5
|
+
summary: "Full audience lifecycle: account discovery, audience creation with hashed identifiers, and audience deletion."
|
|
6
6
|
track: audiences
|
|
7
7
|
required_tools:
|
|
8
8
|
- sync_audiences
|
|
@@ -10,6 +10,7 @@ platform_types:
|
|
|
10
10
|
- display_ad_server
|
|
11
11
|
- social_platform
|
|
12
12
|
- retail_media
|
|
13
|
+
- audio_platform
|
|
13
14
|
- dsp
|
|
14
15
|
- pmax_platform
|
|
15
16
|
|
|
@@ -53,20 +54,14 @@ phases:
|
|
|
53
54
|
- id: discover_account
|
|
54
55
|
title: "Discover or create account"
|
|
55
56
|
narrative: |
|
|
56
|
-
List existing accounts to find one suitable for audience sync.
|
|
57
|
-
exist
|
|
57
|
+
List existing accounts to find one suitable for audience sync. The account
|
|
58
|
+
must already exist and be active. The account_id is captured for use in
|
|
58
59
|
subsequent audience operations.
|
|
59
60
|
task: list_accounts
|
|
60
61
|
schema_ref: "account/list-accounts-request.json"
|
|
61
62
|
response_schema_ref: "account/list-accounts-response.json"
|
|
62
63
|
doc_ref: "/accounts/tasks/list_accounts"
|
|
63
|
-
comply_scenario: account_setup
|
|
64
64
|
stateful: false
|
|
65
|
-
context_outputs:
|
|
66
|
-
- name: account_id
|
|
67
|
-
path: "accounts[0].account_id"
|
|
68
|
-
- name: account_ref
|
|
69
|
-
path: "accounts[0]"
|
|
70
65
|
expected: |
|
|
71
66
|
Return at least one account with:
|
|
72
67
|
- account_id: platform-assigned identifier
|
|
@@ -98,14 +93,11 @@ phases:
|
|
|
98
93
|
already exist on the platform for this account. This is a read-only
|
|
99
94
|
discovery call.
|
|
100
95
|
task: sync_audiences
|
|
101
|
-
schema_ref: "
|
|
102
|
-
response_schema_ref: "
|
|
103
|
-
doc_ref: "/
|
|
104
|
-
comply_scenario:
|
|
96
|
+
schema_ref: "media-buy/sync-audiences-request.json"
|
|
97
|
+
response_schema_ref: "media-buy/sync-audiences-response.json"
|
|
98
|
+
doc_ref: "/media-buy/task-reference/sync_audiences"
|
|
99
|
+
comply_scenario: sync_audiences
|
|
105
100
|
stateful: false
|
|
106
|
-
context_outputs:
|
|
107
|
-
- name: existing_audience_count
|
|
108
|
-
path: "audiences.length"
|
|
109
101
|
expected: |
|
|
110
102
|
Return existing audiences for the account (may be empty).
|
|
111
103
|
Each audience should have an audience_id and status.
|
|
@@ -127,14 +119,11 @@ phases:
|
|
|
127
119
|
matches identifiers against their user graph and returns the audience with
|
|
128
120
|
action: created, match counts, and match rate.
|
|
129
121
|
task: sync_audiences
|
|
130
|
-
schema_ref: "
|
|
131
|
-
response_schema_ref: "
|
|
132
|
-
doc_ref: "/
|
|
133
|
-
comply_scenario:
|
|
122
|
+
schema_ref: "media-buy/sync-audiences-request.json"
|
|
123
|
+
response_schema_ref: "media-buy/sync-audiences-response.json"
|
|
124
|
+
doc_ref: "/media-buy/task-reference/sync_audiences"
|
|
125
|
+
comply_scenario: sync_audiences
|
|
134
126
|
stateful: true
|
|
135
|
-
context_outputs:
|
|
136
|
-
- name: test_audience_id
|
|
137
|
-
path: "audiences[0].audience_id"
|
|
138
127
|
expected: |
|
|
139
128
|
Accept the audience and return:
|
|
140
129
|
- audience_id: matches the submitted ID
|
|
@@ -151,7 +140,7 @@ phases:
|
|
|
151
140
|
operator: "pinnacle-agency.com"
|
|
152
141
|
audiences:
|
|
153
142
|
- audience_id: "adcp-test-audience-001"
|
|
154
|
-
name: "AdCP
|
|
143
|
+
name: "AdCP test audience"
|
|
155
144
|
add:
|
|
156
145
|
- hashed_email: "a000000000000000000000000000000000000000000000000000000000000000"
|
|
157
146
|
- hashed_phone: "b000000000000000000000000000000000000000000000000000000000000000"
|
|
@@ -172,10 +161,10 @@ phases:
|
|
|
172
161
|
Clean up by deleting the test audience. The seller should acknowledge the
|
|
173
162
|
deletion with action: deleted.
|
|
174
163
|
task: sync_audiences
|
|
175
|
-
schema_ref: "
|
|
176
|
-
response_schema_ref: "
|
|
177
|
-
doc_ref: "/
|
|
178
|
-
comply_scenario:
|
|
164
|
+
schema_ref: "media-buy/sync-audiences-request.json"
|
|
165
|
+
response_schema_ref: "media-buy/sync-audiences-response.json"
|
|
166
|
+
doc_ref: "/media-buy/task-reference/sync_audiences"
|
|
167
|
+
comply_scenario: sync_audiences
|
|
179
168
|
stateful: true
|
|
180
169
|
expected: |
|
|
181
170
|
Delete the test audience and return:
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
id: behavioral_analysis
|
|
2
2
|
version: "1.0.0"
|
|
3
3
|
title: "Behavioral analysis"
|
|
4
|
-
category:
|
|
5
|
-
summary: "Validates product discovery behavior, response consistency, and pricing edge cases."
|
|
4
|
+
category: behavioral_analysis
|
|
5
|
+
summary: "Validates product discovery behavior: brief filtering, response consistency, and pricing edge cases."
|
|
6
6
|
track: products
|
|
7
7
|
required_tools:
|
|
8
8
|
- get_products
|
|
@@ -13,7 +13,6 @@ platform_types:
|
|
|
13
13
|
- retail_media
|
|
14
14
|
- search_platform
|
|
15
15
|
- audio_platform
|
|
16
|
-
- linear_tv_platform
|
|
17
16
|
- dsp
|
|
18
17
|
- pmax_platform
|
|
19
18
|
- ai_ad_network
|
|
@@ -54,7 +53,7 @@ prerequisites:
|
|
|
54
53
|
|
|
55
54
|
phases:
|
|
56
55
|
- id: behavior_analysis
|
|
57
|
-
title: "
|
|
56
|
+
title: "Brief filtering behavior"
|
|
58
57
|
narrative: |
|
|
59
58
|
Tests whether the agent filters products based on the brief content. Two different
|
|
60
59
|
briefs are sent — one narrow (podcast audio only) and one broad (all products across
|
|
@@ -73,18 +72,13 @@ phases:
|
|
|
73
72
|
doc_ref: "/media-buy/task-reference/get_products"
|
|
74
73
|
comply_scenario: behavior_analysis
|
|
75
74
|
stateful: false
|
|
76
|
-
context_outputs:
|
|
77
|
-
- name: narrow_product_ids
|
|
78
|
-
path: "products[*].product_id"
|
|
79
|
-
- name: narrow_product_count
|
|
80
|
-
path: "products.length"
|
|
81
75
|
expected: |
|
|
82
|
-
Return products matching the narrow brief.
|
|
83
|
-
|
|
76
|
+
Return products matching the narrow brief. If the agent interprets briefs,
|
|
77
|
+
this should return fewer products than a broad request.
|
|
84
78
|
|
|
85
79
|
sample_request:
|
|
86
80
|
buying_mode: "brief"
|
|
87
|
-
brief: "
|
|
81
|
+
brief: "Podcast audio advertising only — 30-second pre-roll spots"
|
|
88
82
|
brand:
|
|
89
83
|
domain: "acmeoutdoor.com"
|
|
90
84
|
|
|
@@ -98,9 +92,9 @@ phases:
|
|
|
98
92
|
- id: get_products_broad
|
|
99
93
|
title: "Get products with broad brief"
|
|
100
94
|
narrative: |
|
|
101
|
-
Send a broad brief requesting all products
|
|
102
|
-
|
|
103
|
-
|
|
95
|
+
Send a broad brief requesting all available products. Compare the result count
|
|
96
|
+
with the narrow brief — if the agent filters by brief, the broad request should
|
|
97
|
+
return more products.
|
|
104
98
|
task: get_products
|
|
105
99
|
schema_ref: "media-buy/get-products-request.json"
|
|
106
100
|
response_schema_ref: "media-buy/get-products-response.json"
|
|
@@ -108,69 +102,56 @@ phases:
|
|
|
108
102
|
comply_scenario: behavior_analysis
|
|
109
103
|
stateful: false
|
|
110
104
|
expected: |
|
|
111
|
-
Return all available products.
|
|
112
|
-
|
|
105
|
+
Return all available products. The product count should be greater than or
|
|
106
|
+
equal to the narrow brief response.
|
|
113
107
|
|
|
114
108
|
sample_request:
|
|
115
109
|
buying_mode: "brief"
|
|
116
|
-
brief: "Show
|
|
110
|
+
brief: "Show me everything — all channels, all formats, all inventory"
|
|
117
111
|
brand:
|
|
118
112
|
domain: "acmeoutdoor.com"
|
|
119
113
|
|
|
120
114
|
validations:
|
|
121
115
|
- check: response_schema
|
|
122
116
|
description: "Response matches get-products-response.json schema"
|
|
123
|
-
- check: field_present
|
|
124
|
-
path: "products"
|
|
125
|
-
description: "Response contains a products array"
|
|
126
|
-
- check: compare_context
|
|
127
|
-
description: "Broad brief returns >= narrow brief product count"
|
|
128
|
-
context_ref: narrow_product_count
|
|
129
|
-
comparison: ">="
|
|
130
117
|
|
|
131
118
|
- id: response_consistency
|
|
132
119
|
title: "Response consistency"
|
|
133
120
|
narrative: |
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
121
|
+
Tests whether the agent returns deterministic results for identical inputs.
|
|
122
|
+
The same brief is sent twice — the response should contain the same product IDs
|
|
123
|
+
in both cases.
|
|
137
124
|
|
|
138
125
|
steps:
|
|
139
126
|
- id: get_products_first
|
|
140
|
-
title: "First
|
|
127
|
+
title: "First request with identical brief"
|
|
141
128
|
narrative: |
|
|
142
|
-
Send a
|
|
129
|
+
Send a brief and capture the product IDs returned.
|
|
143
130
|
task: get_products
|
|
144
131
|
schema_ref: "media-buy/get-products-request.json"
|
|
145
132
|
response_schema_ref: "media-buy/get-products-response.json"
|
|
146
133
|
doc_ref: "/media-buy/task-reference/get_products"
|
|
147
134
|
comply_scenario: response_consistency
|
|
148
135
|
stateful: false
|
|
149
|
-
context_outputs:
|
|
150
|
-
- name: first_call_product_ids
|
|
151
|
-
path: "products[*].product_id"
|
|
152
136
|
expected: |
|
|
153
|
-
Return products matching the brief. Product IDs
|
|
154
|
-
|
|
137
|
+
Return products matching the brief. Product IDs will be compared with a
|
|
138
|
+
subsequent identical request.
|
|
155
139
|
|
|
156
140
|
sample_request:
|
|
157
141
|
buying_mode: "brief"
|
|
158
|
-
brief: "Premium video inventory on sports
|
|
142
|
+
brief: "Premium video inventory on sports publishers. Adults 25-54."
|
|
159
143
|
brand:
|
|
160
144
|
domain: "acmeoutdoor.com"
|
|
161
145
|
|
|
162
146
|
validations:
|
|
163
147
|
- check: response_schema
|
|
164
148
|
description: "Response matches get-products-response.json schema"
|
|
165
|
-
- check: field_present
|
|
166
|
-
path: "products[0].product_id"
|
|
167
|
-
description: "At least one product returned with a product_id"
|
|
168
149
|
|
|
169
150
|
- id: get_products_second
|
|
170
|
-
title: "Second
|
|
151
|
+
title: "Second request with identical brief"
|
|
171
152
|
narrative: |
|
|
172
|
-
|
|
173
|
-
|
|
153
|
+
Send the exact same brief again. The returned product IDs should match the
|
|
154
|
+
first response.
|
|
174
155
|
task: get_products
|
|
175
156
|
schema_ref: "media-buy/get-products-request.json"
|
|
176
157
|
response_schema_ref: "media-buy/get-products-response.json"
|
|
@@ -178,37 +159,31 @@ phases:
|
|
|
178
159
|
comply_scenario: response_consistency
|
|
179
160
|
stateful: false
|
|
180
161
|
expected: |
|
|
181
|
-
Return the same product IDs as the first
|
|
182
|
-
for identical inputs
|
|
162
|
+
Return the same product IDs as the first request. Deterministic responses
|
|
163
|
+
are expected for identical inputs.
|
|
183
164
|
|
|
184
165
|
sample_request:
|
|
185
166
|
buying_mode: "brief"
|
|
186
|
-
brief: "Premium video inventory on sports
|
|
167
|
+
brief: "Premium video inventory on sports publishers. Adults 25-54."
|
|
187
168
|
brand:
|
|
188
169
|
domain: "acmeoutdoor.com"
|
|
189
170
|
|
|
190
171
|
validations:
|
|
191
172
|
- check: response_schema
|
|
192
173
|
description: "Response matches get-products-response.json schema"
|
|
193
|
-
- check: match_context
|
|
194
|
-
description: "Product IDs match the first call"
|
|
195
|
-
context_ref: first_call_product_ids
|
|
196
|
-
path: "products[*].product_id"
|
|
197
174
|
|
|
198
175
|
- id: pricing_edge_cases
|
|
199
|
-
title: "Pricing
|
|
176
|
+
title: "Pricing structure validation"
|
|
200
177
|
narrative: |
|
|
201
|
-
Validates
|
|
202
|
-
|
|
203
|
-
pricing options with recognized fields.
|
|
178
|
+
Validates that products declare well-formed pricing_options with recognized
|
|
179
|
+
delivery_type values and complete pricing structures.
|
|
204
180
|
|
|
205
181
|
steps:
|
|
206
182
|
- id: get_products_pricing
|
|
207
|
-
title: "
|
|
183
|
+
title: "Validate pricing structures"
|
|
208
184
|
narrative: |
|
|
209
|
-
|
|
210
|
-
a
|
|
211
|
-
(fixed_price, floor_price, price_guidance, min_spend_per_package).
|
|
185
|
+
Send a brief and inspect the pricing structures on returned products.
|
|
186
|
+
Every product must have a recognized delivery_type and valid pricing_options.
|
|
212
187
|
task: get_products
|
|
213
188
|
schema_ref: "media-buy/get-products-request.json"
|
|
214
189
|
response_schema_ref: "media-buy/get-products-response.json"
|
|
@@ -216,29 +191,22 @@ phases:
|
|
|
216
191
|
comply_scenario: pricing_edge_cases
|
|
217
192
|
stateful: false
|
|
218
193
|
expected: |
|
|
219
|
-
|
|
220
|
-
-
|
|
221
|
-
-
|
|
222
|
-
- Pricing fields are valid (no negative values, recognized pricing models)
|
|
194
|
+
Products with valid pricing structures:
|
|
195
|
+
- delivery_type: "guaranteed" or "non_guaranteed"
|
|
196
|
+
- pricing_options with pricing_option_id and pricing_model
|
|
223
197
|
|
|
224
198
|
sample_request:
|
|
225
199
|
buying_mode: "brief"
|
|
226
|
-
brief: "
|
|
200
|
+
brief: "All available products with detailed pricing"
|
|
227
201
|
brand:
|
|
228
202
|
domain: "acmeoutdoor.com"
|
|
229
203
|
|
|
230
204
|
validations:
|
|
231
205
|
- check: response_schema
|
|
232
206
|
description: "Response matches get-products-response.json schema"
|
|
233
|
-
- check: field_present
|
|
234
|
-
path: "products"
|
|
235
|
-
description: "Response contains products array"
|
|
236
207
|
- check: field_present
|
|
237
208
|
path: "products[0].delivery_type"
|
|
238
|
-
description: "
|
|
239
|
-
- check:
|
|
240
|
-
path: "products[
|
|
241
|
-
|
|
242
|
-
- "guaranteed"
|
|
243
|
-
- "non_guaranteed"
|
|
244
|
-
description: "delivery_type is a recognized value"
|
|
209
|
+
description: "Products declare delivery_type"
|
|
210
|
+
- check: field_present
|
|
211
|
+
path: "products[0].pricing_options"
|
|
212
|
+
description: "Products include pricing_options"
|