@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,126 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Example: Using Zod Schemas for Runtime Validation
4
+ *
5
+ * This example demonstrates how to use the generated Zod schemas to validate
6
+ * AdCP data structures at runtime. This is particularly useful for:
7
+ *
8
+ * - Validating API responses from agents
9
+ * - Ensuring data integrity before sending requests
10
+ * - Building forms with runtime validation
11
+ * - Integrating with zod-form libraries
12
+ */
13
+
14
+ import {
15
+ MediaBuySchema,
16
+ ProductSchema,
17
+ GetProductsRequestSchema,
18
+ GetProductsResponseSchema,
19
+ CreateMediaBuyRequestSchema,
20
+ CreateMediaBuyResponseSchema,
21
+ } from '../src/lib/types/schemas.generated';
22
+
23
+ // Example 1: Validate a media buy structure
24
+ console.log('📦 Example 1: Validating a MediaBuy\n');
25
+
26
+ const mediaBuy = {
27
+ media_buy_id: 'mb_12345',
28
+ status: 'active',
29
+ promoted_offering: 'Nike Spring Collection 2024',
30
+ total_budget: 50000,
31
+ packages: [],
32
+ };
33
+
34
+ const mediaBuyResult = MediaBuySchema.safeParse(mediaBuy);
35
+
36
+ if (mediaBuyResult.success) {
37
+ console.log('✅ MediaBuy is valid!');
38
+ console.log('Validated data:', mediaBuyResult.data);
39
+ } else {
40
+ console.log('❌ MediaBuy validation failed:');
41
+ console.log(mediaBuyResult.error.format());
42
+ }
43
+
44
+ // Example 2: Validate and catch errors in a product
45
+ console.log('\n📦 Example 2: Validating a Product (with intentional error)\n');
46
+
47
+ const invalidProduct = {
48
+ product_id: 'prod_123',
49
+ // Missing required fields like 'name', 'description', etc.
50
+ };
51
+
52
+ const productResult = ProductSchema.safeParse(invalidProduct);
53
+
54
+ if (productResult.success) {
55
+ console.log('✅ Product is valid!');
56
+ } else {
57
+ console.log('❌ Product validation failed (as expected):');
58
+ console.log(
59
+ 'Issues found:',
60
+ productResult.error.issues.map(issue => ({
61
+ path: issue.path.join('.'),
62
+ message: issue.message,
63
+ }))
64
+ );
65
+ }
66
+
67
+ // Example 3: Validate request before sending to agent
68
+ console.log('\n📦 Example 3: Validating a GetProducts Request\n');
69
+
70
+ const getProductsRequest = {
71
+ brief: 'Looking for premium display inventory targeting tech professionals',
72
+ brand_manifest: {
73
+ brand_name: 'TechCorp',
74
+ product_catalog: [],
75
+ },
76
+ };
77
+
78
+ const requestResult = GetProductsRequestSchema.safeParse(getProductsRequest);
79
+
80
+ if (requestResult.success) {
81
+ console.log('✅ Request is valid and ready to send!');
82
+ console.log('Brief:', requestResult.data.brief);
83
+ } else {
84
+ console.log('❌ Request validation failed:');
85
+ console.log(requestResult.error.format());
86
+ }
87
+
88
+ // Example 4: Validate response from agent
89
+ console.log('\n📦 Example 4: Validating a GetProducts Response\n');
90
+
91
+ const getProductsResponse = {
92
+ products: [],
93
+ };
94
+
95
+ const responseResult = GetProductsResponseSchema.safeParse(getProductsResponse);
96
+
97
+ if (responseResult.success) {
98
+ console.log('✅ Response is valid!');
99
+ console.log('Number of products:', responseResult.data.products.length);
100
+ } else {
101
+ console.log('❌ Response validation failed:');
102
+ console.log(responseResult.error.format());
103
+ }
104
+
105
+ // Example 5: Using parse() instead of safeParse() to throw errors
106
+ console.log('\n📦 Example 5: Using parse() for stricter validation\n');
107
+
108
+ try {
109
+ // This will throw a ZodError if validation fails
110
+ const strictMediaBuy = MediaBuySchema.parse(mediaBuy);
111
+ console.log('✅ Strict validation passed!');
112
+ } catch (error) {
113
+ console.log('❌ Strict validation failed and threw an error');
114
+ console.log(error);
115
+ }
116
+
117
+ console.log('\n✨ Examples complete!\n');
118
+
119
+ // Additional use cases:
120
+ console.log('💡 Additional use cases for Zod schemas:\n');
121
+ console.log('1. Form validation with React Hook Form + Zod');
122
+ console.log('2. API middleware for validating requests/responses');
123
+ console.log('3. Testing: Validate mock data matches schema');
124
+ console.log('4. OpenAPI generation with zod-to-openapi');
125
+ console.log('5. Database schema validation before persistence');
126
+ console.log('6. Type-safe transformations with .transform()');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adcp/client",
3
- "version": "4.21.0",
3
+ "version": "4.22.1",
4
4
  "description": "AdCP client library with protocol support for MCP and A2A",
5
5
  "main": "dist/lib/index.js",
6
6
  "types": "dist/lib/index.d.ts",
@@ -56,6 +56,11 @@
56
56
  "bin/**/*.js",
57
57
  "skills/**/*",
58
58
  ".claude-plugin/**/*",
59
+ "storyboards/**/*",
60
+ "docs/llms.txt",
61
+ "docs/guides/BUILD-AN-AGENT.md",
62
+ "examples/**/*",
63
+ "AGENTS.md",
59
64
  "README.md",
60
65
  "LICENSE"
61
66
  ],
@@ -81,6 +86,7 @@
81
86
  "generate-types": "tsx scripts/generate-types.ts",
82
87
  "generate-registry-types": "tsx scripts/generate-registry-types.ts",
83
88
  "generate-zod-schemas": "tsx scripts/generate-zod-from-ts.ts",
89
+ "generate-agent-docs": "tsx scripts/generate-agent-docs.ts",
84
90
  "sync-version": "tsx scripts/sync-version.ts",
85
91
  "validate-schemas": "tsx scripts/validate-schemas.ts",
86
92
  "lint": "eslint 'src/lib/testing/**/*.ts'",
@@ -94,6 +100,7 @@
94
100
  "ci:validate": "node scripts/ci-validate.js",
95
101
  "ci:quick": "npm run format:check && npm run typecheck && npm run build:lib && npm test",
96
102
  "ci:schema-check": "npm run sync-schemas && npm run generate-types && npm run generate-registry-types && git diff --exit-code src/lib/types/ src/lib/agents/ src/lib/registry/types.generated.ts schemas/registry/registry.yaml || (echo '⚠️ Generated files are out of sync. Run: npm run sync-schemas && npm run generate-types && npm run generate-registry-types' && exit 1)",
103
+ "ci:docs-check": "npm run generate-agent-docs && git diff --exit-code -I '> Generated at:' docs/llms.txt docs/TYPE-SUMMARY.md || (echo '⚠️ Agent docs are out of sync. Run: npm run generate-agent-docs' && exit 1)",
97
104
  "ci:pre-push": "npm run ci:schema-check && npm run ci:quick",
98
105
  "hooks:install": "node scripts/install-hooks.js",
99
106
  "hooks:uninstall": "rm -f .git/hooks/pre-push",
@@ -129,6 +136,9 @@
129
136
  "url": "https://github.com/adcontextprotocol/adcp-client/issues",
130
137
  "email": "bugs@adcontextprotocol.org"
131
138
  },
139
+ "dependencies": {
140
+ "yaml": "^2.7.1"
141
+ },
132
142
  "peerDependencies": {
133
143
  "@a2a-js/sdk": "^0.3.4",
134
144
  "@modelcontextprotocol/sdk": "^1.17.5",
@@ -144,7 +154,7 @@
144
154
  "@changesets/cli": "^2.29.7",
145
155
  "@commitlint/cli": "^19.6.0",
146
156
  "@commitlint/config-conventional": "^19.6.0",
147
- "@modelcontextprotocol/sdk": "^1.24.1",
157
+ "@modelcontextprotocol/sdk": "^1.29.0",
148
158
  "@opentelemetry/api": "^1.9.0",
149
159
  "@types/node": "^20.19.13",
150
160
  "eslint": "^10.0.3",
@@ -73,30 +73,40 @@ adcp https://agent.example.com get_products '{}' --protocol a2a
73
73
 
74
74
  ## Test runner
75
75
 
76
- Run protocol compliance tests against any AdCP agent. 20 built-in scenarios.
76
+ Run protocol compliance tests against any AdCP agent. 24 built-in scenarios.
77
77
 
78
78
  ```bash
79
79
  adcp test <agent> [scenario] [options]
80
80
  adcp test --list-scenarios
81
81
  ```
82
82
 
83
- ### Scenarios (run `adcp test --list-scenarios` for all 20 with descriptions)
83
+ ### Scenarios (run `adcp test --list-scenarios` for all 24 with descriptions)
84
84
  | Scenario | What it tests |
85
85
  |----------|---------------|
86
86
  | `health_check` | Basic connectivity |
87
87
  | `discovery` | get_products, list_creative_formats, list_authorized_properties |
88
88
  | `create_media_buy` | Discovery + create a media buy (dry-run by default) |
89
89
  | `full_sales_flow` | Full lifecycle: discovery, create, update, delivery |
90
+ | `creative_sync` | Test sync_creatives flow |
91
+ | `creative_inline` | Test inline creatives in create_media_buy |
90
92
  | `creative_flow` | Creative agent: list_formats, build, preview |
91
93
  | `signals_flow` | Signals: get_signals, activate |
92
94
  | `validation` | Schema validation (invalid inputs should be rejected) |
93
95
  | `error_handling` | Verify proper error responses |
94
96
  | `pricing_edge_cases` | Auction vs fixed pricing, min spend, bid_price |
97
+ | `temporal_validation` | Date/time ordering and format validation |
95
98
  | `behavior_analysis` | Auth, brief relevance, filtering behavior |
99
+ | `response_consistency` | Schema errors, pagination bugs, data mismatches |
96
100
  | `capability_discovery` | v3: get_adcp_capabilities |
97
101
  | `governance_property_lists` | v3: property list CRUD |
98
102
  | `governance_content_standards` | v3: content standards listing and calibration |
99
103
  | `si_session_lifecycle` | v3: structured interaction sessions |
104
+ | `si_availability` | v3: SI offering availability check |
105
+ | `campaign_governance` | v3: full governance lifecycle: sync_plans → check → execute → report |
106
+ | `campaign_governance_denied` | v3: denied flow: over-budget, unauthorized market |
107
+ | `campaign_governance_conditions` | v3: apply conditions → re-check |
108
+ | `campaign_governance_delivery` | v3: delivery monitoring with drift detection |
109
+ | `seller_governance_context` | v3: verify seller persists governance_context |
100
110
 
101
111
  ### Examples
102
112
  ```bash
@@ -204,6 +214,7 @@ If not installed, prefix all commands with `npx @adcp/client`. Requires Node.js
204
214
  - **"brand" / "property" / "registry" / "look up" / "validate domain"** — `adcp registry <command>`
205
215
  - **Specific tool name mentioned** — `adcp <agent> <tool> '<payload>'`
206
216
  - **"compare protocols"** — Run same call with `--protocol mcp` then `--protocol a2a`, diff results
217
+ - **"build an agent" / "implement AdCP" / "server side"** — Read `docs/guides/BUILD-AN-AGENT.md` for server setup, and `storyboards/` for expected tool call sequences. Use `docs/llms.txt` for the protocol overview.
207
218
 
208
219
  ### Step 3: Handle authentication
209
220
 
@@ -0,0 +1,199 @@
1
+ id: audience_sync
2
+ version: "1.0.0"
3
+ title: "Audience sync"
4
+ category: audiences
5
+ summary: "Tests sync_audiences: account discovery, audience creation with hashed identifiers, and audience deletion."
6
+ track: audiences
7
+ required_tools:
8
+ - sync_audiences
9
+ platform_types:
10
+ - display_ad_server
11
+ - social_platform
12
+ - retail_media
13
+ - dsp
14
+ - pmax_platform
15
+
16
+ narrative: |
17
+ You support audience syncing via the sync_audiences task. Buyers push first-party
18
+ audience segments to your platform using hashed identifiers (emails, phones). Your
19
+ platform matches those identifiers against your user graph and returns match rates.
20
+
21
+ This storyboard verifies the full audience lifecycle: discover an account to sync
22
+ against, create a test audience with hashed identifiers, and clean up by deleting it.
23
+
24
+ agent:
25
+ interaction_model: media_buy_seller
26
+ capabilities:
27
+ - sells_media
28
+ - accepts_audiences
29
+ examples:
30
+ - "Retail media networks"
31
+ - "Social platforms"
32
+ - "DSPs with audience onboarding"
33
+
34
+ caller:
35
+ role: buyer_agent
36
+ example: "Scope3 (DSP)"
37
+
38
+ prerequisites:
39
+ description: |
40
+ The caller needs an active account on the seller platform. The account_setup phase
41
+ discovers or creates the account relationship before syncing audiences.
42
+ test_kit: "test-kits/acme-outdoor.yaml"
43
+
44
+ phases:
45
+ - id: account_setup
46
+ title: "Account setup"
47
+ narrative: |
48
+ Before syncing audiences, the buyer needs an account on the seller platform.
49
+ This phase discovers an existing account via list_accounts or establishes one
50
+ via sync_accounts.
51
+
52
+ steps:
53
+ - id: discover_account
54
+ title: "Discover or create account"
55
+ narrative: |
56
+ List existing accounts to find one suitable for audience sync. If no accounts
57
+ exist, sync_accounts creates one. The account_id is captured for use in
58
+ subsequent audience operations.
59
+ task: list_accounts
60
+ schema_ref: "account/list-accounts-request.json"
61
+ response_schema_ref: "account/list-accounts-response.json"
62
+ doc_ref: "/accounts/tasks/list_accounts"
63
+ comply_scenario: account_setup
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
+ expected: |
71
+ Return at least one account with:
72
+ - account_id: platform-assigned identifier
73
+ - status: active (required for audience sync)
74
+
75
+ sample_request: {}
76
+
77
+ validations:
78
+ - check: response_schema
79
+ description: "Response matches list-accounts-response.json schema"
80
+ - check: field_present
81
+ path: "accounts[0].account_id"
82
+ description: "At least one account exists with an account_id"
83
+
84
+ - id: audience_sync
85
+ title: "Audience sync"
86
+ narrative: |
87
+ The buyer syncs a test audience containing hashed email and phone identifiers.
88
+ The seller platform matches those identifiers and returns per-audience results
89
+ including action (created/updated), status, and match rates.
90
+
91
+ After verifying creation, the test audience is deleted to clean up.
92
+
93
+ steps:
94
+ - id: discover_audiences
95
+ title: "Discover existing audiences"
96
+ narrative: |
97
+ Call sync_audiences with no audiences array to discover what audiences
98
+ already exist on the platform for this account. This is a read-only
99
+ discovery call.
100
+ task: sync_audiences
101
+ schema_ref: "audience/sync-audiences-request.json"
102
+ response_schema_ref: "audience/sync-audiences-response.json"
103
+ doc_ref: "/audiences/tasks/sync_audiences"
104
+ comply_scenario: audience_sync
105
+ stateful: false
106
+ context_outputs:
107
+ - name: existing_audience_count
108
+ path: "audiences.length"
109
+ expected: |
110
+ Return existing audiences for the account (may be empty).
111
+ Each audience should have an audience_id and status.
112
+
113
+ sample_request:
114
+ account:
115
+ brand:
116
+ domain: "acmeoutdoor.com"
117
+ operator: "pinnacle-agency.com"
118
+
119
+ validations:
120
+ - check: response_schema
121
+ description: "Response matches sync-audiences-response.json schema"
122
+
123
+ - id: create_audience
124
+ title: "Create test audience"
125
+ narrative: |
126
+ Push a test audience with hashed email and phone identifiers. The seller
127
+ matches identifiers against their user graph and returns the audience with
128
+ action: created, match counts, and match rate.
129
+ task: sync_audiences
130
+ schema_ref: "audience/sync-audiences-request.json"
131
+ response_schema_ref: "audience/sync-audiences-response.json"
132
+ doc_ref: "/audiences/tasks/sync_audiences"
133
+ comply_scenario: audience_sync
134
+ stateful: true
135
+ context_outputs:
136
+ - name: test_audience_id
137
+ path: "audiences[0].audience_id"
138
+ expected: |
139
+ Accept the audience and return:
140
+ - audience_id: matches the submitted ID
141
+ - action: created
142
+ - status: active or processing
143
+ - uploaded_count: number of identifiers received
144
+ - matched_count: number of identifiers matched
145
+ - effective_match_rate: ratio of matched to uploaded
146
+
147
+ sample_request:
148
+ account:
149
+ brand:
150
+ domain: "acmeoutdoor.com"
151
+ operator: "pinnacle-agency.com"
152
+ audiences:
153
+ - audience_id: "adcp-test-audience-001"
154
+ name: "AdCP E2E Test Audience"
155
+ add:
156
+ - hashed_email: "a000000000000000000000000000000000000000000000000000000000000000"
157
+ - hashed_phone: "b000000000000000000000000000000000000000000000000000000000000000"
158
+
159
+ validations:
160
+ - check: response_schema
161
+ description: "Response matches sync-audiences-response.json schema"
162
+ - check: field_present
163
+ path: "audiences[0].audience_id"
164
+ description: "Audience has an audience_id"
165
+ - check: field_present
166
+ path: "audiences[0].action"
167
+ description: "Audience has an action (created or updated)"
168
+
169
+ - id: delete_audience
170
+ title: "Delete test audience"
171
+ narrative: |
172
+ Clean up by deleting the test audience. The seller should acknowledge the
173
+ deletion with action: deleted.
174
+ task: sync_audiences
175
+ schema_ref: "audience/sync-audiences-request.json"
176
+ response_schema_ref: "audience/sync-audiences-response.json"
177
+ doc_ref: "/audiences/tasks/sync_audiences"
178
+ comply_scenario: audience_sync
179
+ stateful: true
180
+ expected: |
181
+ Delete the test audience and return:
182
+ - audience_id: matches the test audience
183
+ - action: deleted
184
+
185
+ sample_request:
186
+ account:
187
+ brand:
188
+ domain: "acmeoutdoor.com"
189
+ operator: "pinnacle-agency.com"
190
+ audiences:
191
+ - audience_id: "adcp-test-audience-001"
192
+ delete: true
193
+
194
+ validations:
195
+ - check: response_schema
196
+ description: "Response matches sync-audiences-response.json schema"
197
+ - check: field_present
198
+ path: "audiences[0].action"
199
+ description: "Audience deletion acknowledged with action field"
@@ -0,0 +1,244 @@
1
+ id: behavioral_analysis
2
+ version: "1.0.0"
3
+ title: "Behavioral analysis"
4
+ category: products
5
+ summary: "Validates product discovery behavior, response consistency, and pricing edge cases."
6
+ track: products
7
+ required_tools:
8
+ - get_products
9
+ platform_types:
10
+ - display_ad_server
11
+ - video_ad_server
12
+ - social_platform
13
+ - retail_media
14
+ - search_platform
15
+ - audio_platform
16
+ - linear_tv_platform
17
+ - dsp
18
+ - pmax_platform
19
+ - ai_ad_network
20
+ - ai_platform
21
+ - generative_dsp
22
+
23
+ narrative: |
24
+ You expose a get_products task that accepts natural-language briefs and returns structured
25
+ product listings with pricing, delivery forecasts, and targeting. This storyboard verifies
26
+ three properties of that task:
27
+
28
+ 1. Behavior analysis — do results change when the brief changes? Does the agent filter
29
+ products by brief content or return a static catalog?
30
+ 2. Response consistency — given the same brief twice, does the agent return the same
31
+ product IDs? Deterministic responses are expected for identical inputs.
32
+ 3. Pricing edge cases — do products declare valid pricing_options structures with
33
+ recognized delivery_type values (guaranteed or non_guaranteed)?
34
+
35
+ agent:
36
+ interaction_model: media_buy_seller
37
+ capabilities:
38
+ - sells_media
39
+ - accepts_briefs
40
+ examples:
41
+ - "Yahoo"
42
+ - "Retail media networks"
43
+ - "Publisher platforms"
44
+
45
+ caller:
46
+ role: buyer_agent
47
+ example: "Scope3 (DSP)"
48
+
49
+ prerequisites:
50
+ description: |
51
+ The caller needs a brand identity for product discovery requests.
52
+ The test kit provides a sample brand (Acme Outdoor) with campaign parameters.
53
+ test_kit: "test-kits/acme-outdoor.yaml"
54
+
55
+ phases:
56
+ - id: behavior_analysis
57
+ title: "Behavior analysis"
58
+ narrative: |
59
+ Tests whether the agent filters products based on the brief content. Two different
60
+ briefs are sent — one narrow (podcast audio only) and one broad (all products across
61
+ all channels). If the agent interprets briefs, the narrow brief should return fewer
62
+ products than the broad brief.
63
+
64
+ steps:
65
+ - id: get_products_narrow
66
+ title: "Get products with narrow brief"
67
+ narrative: |
68
+ Send a narrow brief requesting only podcast audio advertising. Capture the
69
+ returned product IDs so they can be compared with a broader request.
70
+ task: get_products
71
+ schema_ref: "media-buy/get-products-request.json"
72
+ response_schema_ref: "media-buy/get-products-response.json"
73
+ doc_ref: "/media-buy/task-reference/get_products"
74
+ comply_scenario: behavior_analysis
75
+ 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
+ expected: |
82
+ Return products matching the narrow brief. The result set should be scoped
83
+ to audio/podcast inventory if the agent performs brief filtering.
84
+
85
+ sample_request:
86
+ buying_mode: "brief"
87
+ brief: "Looking specifically for podcast audio advertising only"
88
+ brand:
89
+ domain: "acmeoutdoor.com"
90
+
91
+ validations:
92
+ - check: response_schema
93
+ description: "Response matches get-products-response.json schema"
94
+ - check: field_present
95
+ path: "products"
96
+ description: "Response contains a products array"
97
+
98
+ - id: get_products_broad
99
+ title: "Get products with broad brief"
100
+ narrative: |
101
+ Send a broad brief requesting all products across all channels. Compare the
102
+ result count against the narrow brief to determine whether the agent filters
103
+ by brief content.
104
+ task: get_products
105
+ schema_ref: "media-buy/get-products-request.json"
106
+ response_schema_ref: "media-buy/get-products-response.json"
107
+ doc_ref: "/media-buy/task-reference/get_products"
108
+ comply_scenario: behavior_analysis
109
+ stateful: false
110
+ expected: |
111
+ Return all available products. If the agent performs brief filtering, this
112
+ response should contain more products than the narrow brief response.
113
+
114
+ sample_request:
115
+ buying_mode: "brief"
116
+ brief: "Show all products across all channels and formats"
117
+ brand:
118
+ domain: "acmeoutdoor.com"
119
+
120
+ validations:
121
+ - check: response_schema
122
+ 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
+
131
+ - id: response_consistency
132
+ title: "Response consistency"
133
+ narrative: |
134
+ Calls get_products twice with the identical brief and brand. The agent should
135
+ return the same product IDs both times. Non-deterministic responses indicate
136
+ randomness in product selection or caching issues.
137
+
138
+ steps:
139
+ - id: get_products_first
140
+ title: "First call with fixed brief"
141
+ narrative: |
142
+ Send a specific brief and capture the returned product IDs for comparison.
143
+ task: get_products
144
+ schema_ref: "media-buy/get-products-request.json"
145
+ response_schema_ref: "media-buy/get-products-response.json"
146
+ doc_ref: "/media-buy/task-reference/get_products"
147
+ comply_scenario: response_consistency
148
+ stateful: false
149
+ context_outputs:
150
+ - name: first_call_product_ids
151
+ path: "products[*].product_id"
152
+ expected: |
153
+ Return products matching the brief. Product IDs are captured for comparison
154
+ with a second identical call.
155
+
156
+ sample_request:
157
+ buying_mode: "brief"
158
+ brief: "Premium video inventory on sports and outdoor lifestyle publishers. Q2 flight, $50K budget. Adults 25-54, US and Canada."
159
+ brand:
160
+ domain: "acmeoutdoor.com"
161
+
162
+ validations:
163
+ - check: response_schema
164
+ 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
+
169
+ - id: get_products_second
170
+ title: "Second call with identical brief"
171
+ narrative: |
172
+ Repeat the exact same request. The returned product IDs should match
173
+ those from the first call.
174
+ task: get_products
175
+ schema_ref: "media-buy/get-products-request.json"
176
+ response_schema_ref: "media-buy/get-products-response.json"
177
+ doc_ref: "/media-buy/task-reference/get_products"
178
+ comply_scenario: response_consistency
179
+ stateful: false
180
+ expected: |
181
+ Return the same product IDs as the first call. Consistent responses
182
+ for identical inputs are expected.
183
+
184
+ sample_request:
185
+ buying_mode: "brief"
186
+ brief: "Premium video inventory on sports and outdoor lifestyle publishers. Q2 flight, $50K budget. Adults 25-54, US and Canada."
187
+ brand:
188
+ domain: "acmeoutdoor.com"
189
+
190
+ validations:
191
+ - check: response_schema
192
+ 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
+
198
+ - id: pricing_edge_cases
199
+ title: "Pricing edge cases"
200
+ narrative: |
201
+ Validates the structure of pricing_options on returned products. Each product
202
+ should declare a delivery_type (guaranteed or non_guaranteed) and well-formed
203
+ pricing options with recognized fields.
204
+
205
+ steps:
206
+ - id: get_products_pricing
207
+ title: "Fetch products for pricing analysis"
208
+ narrative: |
209
+ Request all products with pricing details. Validate that each product has
210
+ a valid delivery_type and that pricing_options contain recognized fields
211
+ (fixed_price, floor_price, price_guidance, min_spend_per_package).
212
+ task: get_products
213
+ schema_ref: "media-buy/get-products-request.json"
214
+ response_schema_ref: "media-buy/get-products-response.json"
215
+ doc_ref: "/media-buy/task-reference/get_products"
216
+ comply_scenario: pricing_edge_cases
217
+ stateful: false
218
+ expected: |
219
+ Return products with well-formed pricing structures:
220
+ - Each product has a delivery_type: guaranteed or non_guaranteed
221
+ - Each pricing_option has a pricing_option_id
222
+ - Pricing fields are valid (no negative values, recognized pricing models)
223
+
224
+ sample_request:
225
+ buying_mode: "brief"
226
+ brief: "Show all products with pricing details"
227
+ brand:
228
+ domain: "acmeoutdoor.com"
229
+
230
+ validations:
231
+ - check: response_schema
232
+ description: "Response matches get-products-response.json schema"
233
+ - check: field_present
234
+ path: "products"
235
+ description: "Response contains products array"
236
+ - check: field_present
237
+ path: "products[0].delivery_type"
238
+ description: "Each product declares guaranteed or non_guaranteed delivery"
239
+ - check: field_value
240
+ path: "products[*].delivery_type"
241
+ allowed_values:
242
+ - "guaranteed"
243
+ - "non_guaranteed"
244
+ description: "delivery_type is a recognized value"