@adcp/client 4.8.0 → 4.10.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.
Files changed (149) hide show
  1. package/bin/adcp.js +346 -0
  2. package/dist/lib/adapters/governance-adapter.d.ts +88 -0
  3. package/dist/lib/adapters/governance-adapter.d.ts.map +1 -0
  4. package/dist/lib/adapters/governance-adapter.js +96 -0
  5. package/dist/lib/adapters/governance-adapter.js.map +1 -0
  6. package/dist/lib/adapters/index.d.ts +1 -0
  7. package/dist/lib/adapters/index.d.ts.map +1 -1
  8. package/dist/lib/adapters/index.js +7 -1
  9. package/dist/lib/adapters/index.js.map +1 -1
  10. package/dist/lib/agents/index.generated.d.ts +33 -1
  11. package/dist/lib/agents/index.generated.d.ts.map +1 -1
  12. package/dist/lib/agents/index.generated.js +48 -0
  13. package/dist/lib/agents/index.generated.js.map +1 -1
  14. package/dist/lib/core/AgentClient.d.ts +92 -1
  15. package/dist/lib/core/AgentClient.d.ts.map +1 -1
  16. package/dist/lib/core/AgentClient.js +237 -0
  17. package/dist/lib/core/AgentClient.js.map +1 -1
  18. package/dist/lib/core/AsyncHandler.d.ts +19 -2
  19. package/dist/lib/core/AsyncHandler.d.ts.map +1 -1
  20. package/dist/lib/core/AsyncHandler.js.map +1 -1
  21. package/dist/lib/core/ConversationTypes.d.ts +8 -2
  22. package/dist/lib/core/ConversationTypes.d.ts.map +1 -1
  23. package/dist/lib/core/GovernanceMiddleware.d.ts +86 -0
  24. package/dist/lib/core/GovernanceMiddleware.d.ts.map +1 -0
  25. package/dist/lib/core/GovernanceMiddleware.js +289 -0
  26. package/dist/lib/core/GovernanceMiddleware.js.map +1 -0
  27. package/dist/lib/core/GovernanceTypes.d.ts +118 -0
  28. package/dist/lib/core/GovernanceTypes.d.ts.map +1 -0
  29. package/dist/lib/core/GovernanceTypes.js +69 -0
  30. package/dist/lib/core/GovernanceTypes.js.map +1 -0
  31. package/dist/lib/core/SingleAgentClient.d.ts +103 -1
  32. package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
  33. package/dist/lib/core/SingleAgentClient.js +151 -0
  34. package/dist/lib/core/SingleAgentClient.js.map +1 -1
  35. package/dist/lib/core/TaskExecutor.d.ts +10 -0
  36. package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
  37. package/dist/lib/core/TaskExecutor.js +95 -10
  38. package/dist/lib/core/TaskExecutor.js.map +1 -1
  39. package/dist/lib/index.d.ts +6 -2
  40. package/dist/lib/index.d.ts.map +1 -1
  41. package/dist/lib/index.js +22 -4
  42. package/dist/lib/index.js.map +1 -1
  43. package/dist/lib/observability/index.d.ts +8 -0
  44. package/dist/lib/observability/index.d.ts.map +1 -0
  45. package/dist/lib/observability/index.js +17 -0
  46. package/dist/lib/observability/index.js.map +1 -0
  47. package/dist/lib/observability/tracing.d.ts +42 -0
  48. package/dist/lib/observability/tracing.d.ts.map +1 -0
  49. package/dist/lib/observability/tracing.js +126 -0
  50. package/dist/lib/observability/tracing.js.map +1 -0
  51. package/dist/lib/protocols/a2a.d.ts.map +1 -1
  52. package/dist/lib/protocols/a2a.js +16 -1
  53. package/dist/lib/protocols/a2a.js.map +1 -1
  54. package/dist/lib/protocols/index.d.ts.map +1 -1
  55. package/dist/lib/protocols/index.js +37 -29
  56. package/dist/lib/protocols/index.js.map +1 -1
  57. package/dist/lib/protocols/mcp.d.ts.map +1 -1
  58. package/dist/lib/protocols/mcp.js +21 -1
  59. package/dist/lib/protocols/mcp.js.map +1 -1
  60. package/dist/lib/registry/types.generated.d.ts +754 -5
  61. package/dist/lib/registry/types.generated.d.ts.map +1 -1
  62. package/dist/lib/registry/types.generated.js +1 -1
  63. package/dist/lib/testing/agent-tester.d.ts +1 -1
  64. package/dist/lib/testing/agent-tester.d.ts.map +1 -1
  65. package/dist/lib/testing/agent-tester.js +36 -11
  66. package/dist/lib/testing/agent-tester.js.map +1 -1
  67. package/dist/lib/testing/client.d.ts +6 -1
  68. package/dist/lib/testing/client.d.ts.map +1 -1
  69. package/dist/lib/testing/client.js +36 -19
  70. package/dist/lib/testing/client.js.map +1 -1
  71. package/dist/lib/testing/compliance/briefs.d.ts +12 -0
  72. package/dist/lib/testing/compliance/briefs.d.ts.map +1 -0
  73. package/dist/lib/testing/compliance/briefs.js +157 -0
  74. package/dist/lib/testing/compliance/briefs.js.map +1 -0
  75. package/dist/lib/testing/compliance/comply.d.ts +26 -0
  76. package/dist/lib/testing/compliance/comply.d.ts.map +1 -0
  77. package/dist/lib/testing/compliance/comply.js +540 -0
  78. package/dist/lib/testing/compliance/comply.js.map +1 -0
  79. package/dist/lib/testing/compliance/convince.d.ts +27 -0
  80. package/dist/lib/testing/compliance/convince.d.ts.map +1 -0
  81. package/dist/lib/testing/compliance/convince.js +418 -0
  82. package/dist/lib/testing/compliance/convince.js.map +1 -0
  83. package/dist/lib/testing/compliance/index.d.ts +13 -0
  84. package/dist/lib/testing/compliance/index.d.ts.map +1 -0
  85. package/dist/lib/testing/compliance/index.js +22 -0
  86. package/dist/lib/testing/compliance/index.js.map +1 -0
  87. package/dist/lib/testing/compliance/types.d.ts +123 -0
  88. package/dist/lib/testing/compliance/types.d.ts.map +1 -0
  89. package/dist/lib/testing/compliance/types.js +9 -0
  90. package/dist/lib/testing/compliance/types.js.map +1 -0
  91. package/dist/lib/testing/index.d.ts +1 -0
  92. package/dist/lib/testing/index.d.ts.map +1 -1
  93. package/dist/lib/testing/index.js +15 -1
  94. package/dist/lib/testing/index.js.map +1 -1
  95. package/dist/lib/testing/orchestrator.d.ts +0 -2
  96. package/dist/lib/testing/orchestrator.d.ts.map +1 -1
  97. package/dist/lib/testing/orchestrator.js +14 -3
  98. package/dist/lib/testing/orchestrator.js.map +1 -1
  99. package/dist/lib/testing/scenarios/capabilities.d.ts.map +1 -1
  100. package/dist/lib/testing/scenarios/capabilities.js +70 -9
  101. package/dist/lib/testing/scenarios/capabilities.js.map +1 -1
  102. package/dist/lib/testing/scenarios/creative.d.ts +14 -0
  103. package/dist/lib/testing/scenarios/creative.d.ts.map +1 -1
  104. package/dist/lib/testing/scenarios/creative.js +261 -55
  105. package/dist/lib/testing/scenarios/creative.js.map +1 -1
  106. package/dist/lib/testing/scenarios/discovery.d.ts.map +1 -1
  107. package/dist/lib/testing/scenarios/discovery.js +7 -5
  108. package/dist/lib/testing/scenarios/discovery.js.map +1 -1
  109. package/dist/lib/testing/scenarios/edge-cases.d.ts.map +1 -1
  110. package/dist/lib/testing/scenarios/edge-cases.js +90 -112
  111. package/dist/lib/testing/scenarios/edge-cases.js.map +1 -1
  112. package/dist/lib/testing/scenarios/governance.d.ts +48 -0
  113. package/dist/lib/testing/scenarios/governance.d.ts.map +1 -1
  114. package/dist/lib/testing/scenarios/governance.js +725 -39
  115. package/dist/lib/testing/scenarios/governance.js.map +1 -1
  116. package/dist/lib/testing/scenarios/index.d.ts +3 -3
  117. package/dist/lib/testing/scenarios/index.d.ts.map +1 -1
  118. package/dist/lib/testing/scenarios/index.js +8 -1
  119. package/dist/lib/testing/scenarios/index.js.map +1 -1
  120. package/dist/lib/testing/scenarios/media-buy.d.ts +14 -5
  121. package/dist/lib/testing/scenarios/media-buy.d.ts.map +1 -1
  122. package/dist/lib/testing/scenarios/media-buy.js +358 -63
  123. package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
  124. package/dist/lib/testing/scenarios/schema-compliance.d.ts.map +1 -1
  125. package/dist/lib/testing/scenarios/schema-compliance.js +26 -22
  126. package/dist/lib/testing/scenarios/schema-compliance.js.map +1 -1
  127. package/dist/lib/testing/scenarios/signals.d.ts +4 -8
  128. package/dist/lib/testing/scenarios/signals.d.ts.map +1 -1
  129. package/dist/lib/testing/scenarios/signals.js +17 -59
  130. package/dist/lib/testing/scenarios/signals.js.map +1 -1
  131. package/dist/lib/testing/scenarios/sponsored-intelligence.d.ts.map +1 -1
  132. package/dist/lib/testing/scenarios/sponsored-intelligence.js +23 -19
  133. package/dist/lib/testing/scenarios/sponsored-intelligence.js.map +1 -1
  134. package/dist/lib/testing/types.d.ts +3 -2
  135. package/dist/lib/testing/types.d.ts.map +1 -1
  136. package/dist/lib/types/core.generated.d.ts +629 -5
  137. package/dist/lib/types/core.generated.d.ts.map +1 -1
  138. package/dist/lib/types/core.generated.js +1 -1
  139. package/dist/lib/types/schemas.generated.d.ts +13059 -11101
  140. package/dist/lib/types/schemas.generated.d.ts.map +1 -1
  141. package/dist/lib/types/schemas.generated.js +573 -107
  142. package/dist/lib/types/schemas.generated.js.map +1 -1
  143. package/dist/lib/types/tools.generated.d.ts +1827 -360
  144. package/dist/lib/types/tools.generated.d.ts.map +1 -1
  145. package/dist/lib/utils/capabilities.d.ts +17 -2
  146. package/dist/lib/utils/capabilities.d.ts.map +1 -1
  147. package/dist/lib/utils/capabilities.js +26 -2
  148. package/dist/lib/utils/capabilities.js.map +1 -1
  149. package/package.json +13 -3
@@ -16,6 +16,7 @@ exports.testCreateMediaBuy = testCreateMediaBuy;
16
16
  exports.testFullSalesFlow = testFullSalesFlow;
17
17
  exports.testCreativeSync = testCreativeSync;
18
18
  exports.testCreativeInline = testCreativeInline;
19
+ exports.testCreativeReference = testCreativeReference;
19
20
  exports.resolveAccountForAudiences = resolveAccountForAudiences;
20
21
  exports.testSyncAudiences = testSyncAudiences;
21
22
  const client_1 = require("../client");
@@ -27,11 +28,11 @@ function selectProduct(products, options) {
27
28
  // If channels specified, filter to matching products
28
29
  let candidates = products;
29
30
  if (options.channels?.length) {
30
- candidates = products.filter(p => p.channels?.some((ch) => options.channels.includes(ch)));
31
+ candidates = products.filter(p => p.channels?.some(ch => options.channels.includes(ch)));
31
32
  }
32
33
  // If pricing models specified, filter further
33
34
  if (options.pricing_models?.length) {
34
- candidates = candidates.filter(p => p.pricing_options?.some((po) => options.pricing_models.includes(po.model)));
35
+ candidates = candidates.filter(p => p.pricing_options?.some((po) => options.pricing_models.includes(po.pricing_model)));
35
36
  }
36
37
  // Return first matching or first product
37
38
  return candidates[0] || products[0] || null;
@@ -42,7 +43,7 @@ function selectProduct(products, options) {
42
43
  function selectPricingOption(product, preferredModels) {
43
44
  const options = product.pricing_options || [];
44
45
  if (preferredModels?.length) {
45
- const preferred = options.find((po) => preferredModels.includes(po.model));
46
+ const preferred = options.find((po) => preferredModels.includes(po.pricing_model));
46
47
  if (preferred)
47
48
  return preferred;
48
49
  }
@@ -52,11 +53,13 @@ function selectPricingOption(product, preferredModels) {
52
53
  * Build a create_media_buy request
53
54
  */
54
55
  function buildCreateMediaBuyRequest(product, pricingOption, options, extras = {}) {
55
- const budget = options.budget || 1000;
56
+ const minSpend = pricingOption.min_spend_per_package || 0;
57
+ const budget = options.budget || Math.max(1000, minSpend);
56
58
  const now = new Date();
57
59
  const startTime = new Date(now.getTime() + 24 * 60 * 60 * 1000); // Tomorrow
58
60
  const endTime = new Date(startTime.getTime() + 7 * 24 * 60 * 60 * 1000); // 7 days later
59
- const isAuction = pricingOption.model === 'auction' || pricingOption.is_fixed === false || pricingOption.floor_price !== undefined;
61
+ const isAuction = !('fixed_price' in pricingOption) &&
62
+ (pricingOption.floor_price !== undefined || pricingOption.price_guidance !== undefined);
60
63
  const packageRequest = {
61
64
  buyer_ref: `pkg-test-${Date.now()}`,
62
65
  product_id: product.product_id,
@@ -83,6 +86,56 @@ function buildCreateMediaBuyRequest(product, pricingOption, options, extras = {}
83
86
  packages: [packageRequest],
84
87
  };
85
88
  }
89
+ function getDefaultFormatId() {
90
+ return { agent_url: 'https://creative.adcontextprotocol.org', id: 'display_300x250' };
91
+ }
92
+ function formatIdToString(formatId) {
93
+ return formatId.id;
94
+ }
95
+ function selectFormatId(product, fallback = getDefaultFormatId()) {
96
+ if (!product?.format_ids?.length) {
97
+ return fallback;
98
+ }
99
+ const format = product.format_ids[0];
100
+ if (typeof format === 'string') {
101
+ return { ...fallback, id: format };
102
+ }
103
+ const nested = format;
104
+ if (nested.format_id) {
105
+ return nested.format_id;
106
+ }
107
+ const direct = format;
108
+ if (typeof direct.agent_url === 'string' && typeof direct.id === 'string') {
109
+ return direct;
110
+ }
111
+ return fallback;
112
+ }
113
+ function buildStaticInlineCreative(formatId) {
114
+ return {
115
+ name: `Inline Test Creative ${Date.now()}`,
116
+ format_id: formatId,
117
+ assets: {
118
+ primary: {
119
+ url: 'https://via.placeholder.com/300x250?text=Inline+Creative',
120
+ width: 300,
121
+ height: 250,
122
+ format: 'png',
123
+ },
124
+ },
125
+ };
126
+ }
127
+ function extractCreativeManifest(data) {
128
+ return data?.creative_manifest || data?.creative_manifests?.[0];
129
+ }
130
+ function buildSyncCreativeFromManifest(manifest, fallbackFormatId) {
131
+ const creativeId = manifest?.creative_id || `test-creative-${Date.now()}`;
132
+ return {
133
+ creative_id: creativeId,
134
+ name: manifest?.name || `Generated Creative ${creativeId}`,
135
+ format_id: manifest?.format_id || fallbackFormatId,
136
+ assets: manifest?.assets || buildStaticInlineCreative(fallbackFormatId).assets,
137
+ };
138
+ }
86
139
  /**
87
140
  * Test: Create Media Buy
88
141
  * Discovers products, then creates a test media buy
@@ -104,12 +157,14 @@ async function testCreateMediaBuy(agentUrl, options) {
104
157
  return { steps, profile };
105
158
  }
106
159
  // Get products
107
- const { result: productsResult } = await (0, client_1.runStep)('Fetch products for media buy', 'get_products', async () => client.executeTask('get_products', {
160
+ const { result: productsResult } = await (0, client_1.runStep)('Fetch products for media buy', 'get_products', async () => client.getProducts({
108
161
  buying_mode: 'brief',
109
162
  brief: options.brief || 'Looking for display advertising products',
110
163
  brand: (0, client_1.resolveBrand)(options),
164
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- intentional: test request bypasses strict typing
111
165
  }));
112
- const products = productsResult?.data?.products;
166
+ const productsData = productsResult?.data;
167
+ const products = productsData?.products;
113
168
  if (!productsResult?.success || !products?.length) {
114
169
  steps.push({
115
170
  step: 'Create media buy',
@@ -121,6 +176,16 @@ async function testCreateMediaBuy(agentUrl, options) {
121
176
  return { steps, profile };
122
177
  }
123
178
  const product = selectProduct(products, options);
179
+ if (!product) {
180
+ steps.push({
181
+ step: 'Create media buy',
182
+ task: 'create_media_buy',
183
+ passed: false,
184
+ duration_ms: 0,
185
+ error: 'No suitable product found',
186
+ });
187
+ return { steps, profile };
188
+ }
124
189
  const pricingOption = selectPricingOption(product, options.pricing_models);
125
190
  if (!pricingOption) {
126
191
  steps.push({
@@ -134,20 +199,23 @@ async function testCreateMediaBuy(agentUrl, options) {
134
199
  }
135
200
  const createRequest = buildCreateMediaBuyRequest(product, pricingOption, options);
136
201
  // Create the media buy
137
- const { result: createResult, step: createStep } = await (0, client_1.runStep)('Create media buy', 'create_media_buy', async () => client.executeTask('create_media_buy', createRequest));
202
+ const { result: createResult, step: createStep } = await (0, client_1.runStep)('Create media buy', 'create_media_buy',
203
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- intentional: test request bypasses strict typing
204
+ async () => client.createMediaBuy(createRequest));
138
205
  let mediaBuyId;
139
206
  if (createResult?.success && createResult?.data) {
140
207
  const mediaBuy = createResult.data;
141
- mediaBuyId = mediaBuy.media_buy_id || mediaBuy.media_buy?.media_buy_id;
142
- const status = mediaBuy.status || mediaBuy.media_buy?.status;
143
- const packages = mediaBuy.packages || mediaBuy.media_buy?.packages;
208
+ const nested = mediaBuy.media_buy;
209
+ mediaBuyId = (mediaBuy.media_buy_id || nested?.media_buy_id);
210
+ const status = (mediaBuy.status || nested?.status);
211
+ const packages = (mediaBuy.packages || nested?.packages);
144
212
  createStep.details = `Created media buy: ${mediaBuyId}, status: ${status}`;
145
213
  createStep.created_id = mediaBuyId;
146
214
  createStep.response_preview = JSON.stringify({
147
215
  media_buy_id: mediaBuyId,
148
216
  status,
149
217
  packages_count: packages?.length,
150
- pricing_model: pricingOption.model,
218
+ pricing_model: pricingOption.pricing_model,
151
219
  product_name: product.name,
152
220
  }, null, 2);
153
221
  }
@@ -173,7 +241,7 @@ async function testFullSalesFlow(agentUrl, options) {
173
241
  }
174
242
  // Test update_media_buy if available
175
243
  if (profile?.tools.includes('update_media_buy')) {
176
- const { result: updateResult, step: updateStep } = await (0, client_1.runStep)('Update media buy (increase budget)', 'update_media_buy', async () => client.executeTask('update_media_buy', {
244
+ const { result: updateResult, step: updateStep } = await (0, client_1.runStep)('Update media buy (increase budget)', 'update_media_buy', async () => client.updateMediaBuy({
177
245
  media_buy_id: mediaBuyId,
178
246
  packages: [
179
247
  {
@@ -181,13 +249,15 @@ async function testFullSalesFlow(agentUrl, options) {
181
249
  budget: (options.budget || 1000) * 1.5,
182
250
  },
183
251
  ],
252
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- intentional: test request bypasses strict typing
184
253
  }));
185
254
  if (updateResult?.success && updateResult?.data) {
186
255
  const data = updateResult.data;
187
- const status = data.status || data.media_buy?.status;
256
+ const nested = data.media_buy;
257
+ const status = (data.status || nested?.status);
188
258
  updateStep.details = `Updated media buy, status: ${status}`;
189
259
  updateStep.response_preview = JSON.stringify({
190
- media_buy_id: data.media_buy_id || data.media_buy?.media_buy_id,
260
+ media_buy_id: (data.media_buy_id || nested?.media_buy_id),
191
261
  status,
192
262
  }, null, 2);
193
263
  }
@@ -197,16 +267,61 @@ async function testFullSalesFlow(agentUrl, options) {
197
267
  }
198
268
  steps.push(updateStep);
199
269
  }
270
+ if (profile?.tools.includes('get_media_buys')) {
271
+ const { result: snapshotResult, step: snapshotStep } = await (0, client_1.runStep)('Get media buy status with delivery snapshots', 'get_media_buys', async () => client.executeTask('get_media_buys', {
272
+ media_buy_ids: [mediaBuyId],
273
+ include_snapshot: true,
274
+ }));
275
+ if (snapshotResult?.success && snapshotResult?.data) {
276
+ const mediaBuys = snapshotResult.data.media_buys || [];
277
+ const mediaBuy = mediaBuys.find((item) => item.media_buy_id === mediaBuyId) || mediaBuys[0];
278
+ const packages = mediaBuy?.packages || [];
279
+ const invalidPackages = packages.filter((pkg) => {
280
+ if (pkg.snapshot) {
281
+ return !pkg.snapshot.as_of || pkg.snapshot.staleness_seconds === undefined;
282
+ }
283
+ return !pkg.snapshot_unavailable_reason;
284
+ });
285
+ if (!mediaBuy) {
286
+ snapshotStep.passed = false;
287
+ snapshotStep.error = 'get_media_buys did not return the created media buy';
288
+ }
289
+ else if (invalidPackages.length > 0) {
290
+ snapshotStep.passed = false;
291
+ snapshotStep.error =
292
+ 'include_snapshot=true must return either snapshot data or snapshot_unavailable_reason for each package';
293
+ }
294
+ else {
295
+ snapshotStep.details = `Retrieved ${packages.length} package snapshot(s)`;
296
+ snapshotStep.response_preview = JSON.stringify({
297
+ media_buy_id: mediaBuy.media_buy_id,
298
+ package_count: packages.length,
299
+ snapshots_returned: packages.filter((pkg) => !!pkg.snapshot).length,
300
+ snapshot_unavailable: packages
301
+ .filter((pkg) => !!pkg.snapshot_unavailable_reason)
302
+ .map((pkg) => ({ package_id: pkg.package_id, reason: pkg.snapshot_unavailable_reason })),
303
+ }, null, 2);
304
+ }
305
+ }
306
+ else if (snapshotResult && !snapshotResult.success) {
307
+ snapshotStep.passed = false;
308
+ snapshotStep.error = snapshotResult.error || 'get_media_buys returned unsuccessful result';
309
+ }
310
+ steps.push(snapshotStep);
311
+ }
200
312
  // Test get_media_buy_delivery if available
201
313
  if (profile?.tools.includes('get_media_buy_delivery')) {
202
- const { result: deliveryResult, step: deliveryStep } = await (0, client_1.runStep)('Get delivery metrics', 'get_media_buy_delivery', async () => client.executeTask('get_media_buy_delivery', {
314
+ const { result: deliveryResult, step: deliveryStep } = await (0, client_1.runStep)('Get delivery metrics', 'get_media_buy_delivery', async () => client.getMediaBuyDelivery({
203
315
  media_buy_ids: [mediaBuyId],
316
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- intentional: test request bypasses strict typing
204
317
  }));
205
318
  if (deliveryResult?.success && deliveryResult?.data) {
206
319
  const delivery = deliveryResult.data;
320
+ const deliveries = delivery.deliveries;
321
+ const mediaBuys = delivery.media_buys;
207
322
  deliveryStep.details = `Retrieved delivery metrics`;
208
323
  deliveryStep.response_preview = JSON.stringify({
209
- has_deliveries: !!(delivery.deliveries?.length || delivery.media_buys?.length),
324
+ has_deliveries: !!(deliveries?.length || mediaBuys?.length),
210
325
  }, null, 2);
211
326
  }
212
327
  else if (deliveryResult && !deliveryResult.success) {
@@ -238,14 +353,25 @@ async function testCreativeSync(agentUrl, options) {
238
353
  return { steps, profile };
239
354
  }
240
355
  // Get format info first
241
- let formatId = 'display_300x250'; // Default
356
+ let formatId = {
357
+ agent_url: 'https://creative.adcontextprotocol.org',
358
+ id: 'display_300x250',
359
+ };
242
360
  if (profile.tools.includes('list_creative_formats')) {
243
- const { result: formatsResult } = await (0, client_1.runStep)('Get formats for creative', 'list_creative_formats', async () => client.executeTask('list_creative_formats', {}));
361
+ const { result: formatsResult } = await (0, client_1.runStep)('Get formats for creative', 'list_creative_formats', async () => client.listCreativeFormats({}));
244
362
  if (formatsResult?.success && formatsResult?.data) {
245
363
  const data = formatsResult.data;
246
- const firstFormat = data.format_ids?.[0] || data.formats?.[0];
364
+ const formatIds = data.format_ids;
365
+ const formats = data.formats;
366
+ const firstFormat = formatIds?.[0] || formats?.[0];
247
367
  if (firstFormat) {
248
- formatId = typeof firstFormat === 'string' ? firstFormat : firstFormat.id || firstFormat.format_id;
368
+ if (typeof firstFormat === 'string') {
369
+ formatId = { id: firstFormat };
370
+ }
371
+ else {
372
+ const formatObj = firstFormat;
373
+ formatId = formatObj.format_id || formatObj;
374
+ }
249
375
  }
250
376
  }
251
377
  }
@@ -264,18 +390,20 @@ async function testCreativeSync(agentUrl, options) {
264
390
  },
265
391
  },
266
392
  };
267
- const { result: syncResult, step: syncStep } = await (0, client_1.runStep)('Sync creative to library', 'sync_creatives', async () => client.executeTask('sync_creatives', {
393
+ const { result: syncResult, step: syncStep } = await (0, client_1.runStep)('Sync creative to library', 'sync_creatives', async () => client.syncCreatives({
394
+ account: (0, client_1.resolveAccount)(options),
268
395
  creatives: [testCreative],
396
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- intentional: test request bypasses strict typing
269
397
  }));
270
398
  if (syncResult?.success && syncResult?.data) {
271
399
  const data = syncResult.data;
272
400
  const creatives = data.creatives || [];
273
- const actions = creatives.map((c) => c.action);
401
+ const actions = creatives.map(c => c.action);
274
402
  syncStep.details = `Synced ${creatives.length} creative(s), actions: ${actions.join(', ')}`;
275
403
  syncStep.response_preview = JSON.stringify({
276
404
  creatives_count: creatives.length,
277
405
  actions: actions,
278
- creative_ids: creatives.map((c) => c.creative_id),
406
+ creative_ids: creatives.map(c => c.creative_id),
279
407
  }, null, 2);
280
408
  }
281
409
  else if (syncResult && !syncResult.success) {
@@ -285,7 +413,7 @@ async function testCreativeSync(agentUrl, options) {
285
413
  steps.push(syncStep);
286
414
  // Test list_creatives if available
287
415
  if (profile.tools.includes('list_creatives')) {
288
- const { result: listResult, step: listStep } = await (0, client_1.runStep)('List creatives in library', 'list_creatives', async () => client.executeTask('list_creatives', {}));
416
+ const { result: listResult, step: listStep } = await (0, client_1.runStep)('List creatives in library', 'list_creatives', async () => client.listCreatives({}));
289
417
  if (listResult?.success && listResult?.data) {
290
418
  const data = listResult.data;
291
419
  const creatives = data.creatives || [];
@@ -308,7 +436,7 @@ async function testCreativeSync(agentUrl, options) {
308
436
  listStep.response_preview = JSON.stringify({
309
437
  creatives_count: creatives.length,
310
438
  total_matching: totalMatching,
311
- statuses: Array.from(new Set(creatives.map((c) => c.status))),
439
+ statuses: Array.from(new Set(creatives.map(c => c.status))),
312
440
  }, null, 2);
313
441
  }
314
442
  }
@@ -341,12 +469,14 @@ async function testCreativeInline(agentUrl, options) {
341
469
  return { steps, profile };
342
470
  }
343
471
  // Get products
344
- const { result: productsResult } = await (0, client_1.runStep)('Fetch products for inline creative test', 'get_products', async () => client.executeTask('get_products', {
472
+ const { result: productsResult } = await (0, client_1.runStep)('Fetch products for inline creative test', 'get_products', async () => client.getProducts({
345
473
  buying_mode: 'brief',
346
474
  brief: options.brief || 'Looking for display advertising products',
347
475
  brand: (0, client_1.resolveBrand)(options),
476
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- intentional: test request bypasses strict typing
348
477
  }));
349
- const products = productsResult?.data?.products;
478
+ const inlineProductsData = productsResult?.data;
479
+ const products = inlineProductsData?.products;
350
480
  if (!productsResult?.success || !products?.length) {
351
481
  steps.push({
352
482
  step: 'Create media buy with inline creatives',
@@ -358,6 +488,16 @@ async function testCreativeInline(agentUrl, options) {
358
488
  return { steps, profile };
359
489
  }
360
490
  const product = selectProduct(products, options);
491
+ if (!product) {
492
+ steps.push({
493
+ step: 'Create media buy with inline creatives',
494
+ task: 'create_media_buy',
495
+ passed: false,
496
+ duration_ms: 0,
497
+ error: 'No suitable product found',
498
+ });
499
+ return { steps, profile };
500
+ }
361
501
  const pricingOption = selectPricingOption(product, options.pricing_models);
362
502
  if (!pricingOption) {
363
503
  steps.push({
@@ -369,35 +509,53 @@ async function testCreativeInline(agentUrl, options) {
369
509
  });
370
510
  return { steps, profile };
371
511
  }
372
- // Get format for inline creative
373
- let formatId = 'display_300x250';
374
- if (product.format_ids?.length) {
375
- const fid = product.format_ids[0];
376
- formatId = typeof fid === 'string' ? fid : fid.id || fid.format_id || formatId;
512
+ const formatId = selectFormatId(product);
513
+ let inlineCreative = buildStaticInlineCreative(formatId);
514
+ if (profile.tools.includes('build_creative')) {
515
+ const { result: buildResult, step: buildStep } = await (0, client_1.runStep)(`Build creative for inline flow (${formatIdToString(formatId)})`, 'build_creative', async () => client.executeTask('build_creative', {
516
+ target_format_id: formatId,
517
+ brand: (0, client_1.resolveBrand)(options),
518
+ message: `Create an ad creative for the ${formatIdToString(formatId)} format that can be attached to a media buy`,
519
+ quality: 'draft',
520
+ }));
521
+ if (buildResult?.success && buildResult?.data) {
522
+ const manifest = extractCreativeManifest(buildResult.data);
523
+ if (manifest?.assets) {
524
+ inlineCreative = buildSyncCreativeFromManifest(manifest, formatId);
525
+ buildStep.details = `Built creative manifest for ${inlineCreative.format_id}`;
526
+ buildStep.response_preview = JSON.stringify({
527
+ format_id: inlineCreative.format_id,
528
+ asset_keys: Object.keys(inlineCreative.assets || {}),
529
+ }, null, 2);
530
+ }
531
+ else {
532
+ buildStep.passed = false;
533
+ buildStep.error = 'build_creative succeeded but returned no creative_manifest';
534
+ }
535
+ }
536
+ else if (buildResult && !buildResult.success) {
537
+ buildStep.passed = false;
538
+ buildStep.error = buildResult.error || 'build_creative failed';
539
+ }
540
+ steps.push(buildStep);
377
541
  }
378
- // Build inline creative
379
- const inlineCreative = {
380
- name: `Inline Test Creative ${Date.now()}`,
381
- format_id: formatId,
382
- assets: {
383
- primary: {
384
- url: 'https://via.placeholder.com/300x250?text=Inline+Creative',
385
- width: 300,
386
- height: 250,
387
- format: 'png',
388
- },
389
- },
390
- };
391
542
  const createRequest = buildCreateMediaBuyRequest(product, pricingOption, options, {
392
543
  inline_creatives: [inlineCreative],
393
544
  });
394
- const { result: createResult, step: createStep } = await (0, client_1.runStep)('Create media buy with inline creative', 'create_media_buy', async () => client.executeTask('create_media_buy', createRequest));
545
+ const { result: createResult, step: createStep } = await (0, client_1.runStep)('Create media buy with inline creative', 'create_media_buy',
546
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- intentional: test request bypasses strict typing
547
+ async () => client.createMediaBuy(createRequest));
395
548
  if (createResult?.success && createResult?.data) {
396
549
  const mediaBuy = createResult.data;
397
- const mediaBuyId = mediaBuy.media_buy_id || mediaBuy.media_buy?.media_buy_id;
398
- const status = mediaBuy.status || mediaBuy.media_buy?.status;
399
- const packages = mediaBuy.packages || mediaBuy.media_buy?.packages;
400
- const hasCreatives = packages?.some((p) => p.creatives?.length || p.creative_ids?.length);
550
+ const nested = mediaBuy.media_buy;
551
+ const mediaBuyId = (mediaBuy.media_buy_id || nested?.media_buy_id);
552
+ const status = (mediaBuy.status || nested?.status);
553
+ const packages = (mediaBuy.packages || nested?.packages);
554
+ const hasCreatives = packages?.some(p => {
555
+ const creatives = p.creatives;
556
+ const creativeIds = p.creative_ids;
557
+ return creatives?.length || creativeIds?.length;
558
+ });
401
559
  createStep.details = `Created media buy with inline creative: ${mediaBuyId}`;
402
560
  createStep.created_id = mediaBuyId;
403
561
  createStep.response_preview = JSON.stringify({
@@ -414,6 +572,131 @@ async function testCreativeInline(agentUrl, options) {
414
572
  steps.push(createStep);
415
573
  return { steps, profile };
416
574
  }
575
+ /**
576
+ * Test: Creative Reference Flow
577
+ * Builds a creative manifest, syncs it into the seller's library, then references it in create_media_buy.
578
+ */
579
+ async function testCreativeReference(agentUrl, options) {
580
+ const steps = [];
581
+ const client = (0, client_1.createTestClient)(agentUrl, options.protocol || 'mcp', options);
582
+ const { steps: discoverySteps, profile } = await (0, discovery_1.testDiscovery)(agentUrl, options);
583
+ steps.push(...discoverySteps);
584
+ if (!profile?.tools.includes('build_creative') || !profile.tools.includes('sync_creatives')) {
585
+ steps.push({
586
+ step: 'Build and reference creative',
587
+ task: 'build_creative',
588
+ passed: false,
589
+ duration_ms: 0,
590
+ error: 'Agent must support both build_creative and sync_creatives',
591
+ });
592
+ return { steps, profile };
593
+ }
594
+ const { result: productsResult } = await (0, client_1.runStep)('Fetch products for creative reference test', 'get_products', async () => client.executeTask('get_products', {
595
+ buying_mode: 'brief',
596
+ brief: options.brief || 'Looking for products that support generated creative attachments',
597
+ brand: (0, client_1.resolveBrand)(options),
598
+ }));
599
+ const products = productsResult?.data?.products;
600
+ if (!productsResult?.success || !products?.length) {
601
+ steps.push({
602
+ step: 'Build and reference creative',
603
+ task: 'create_media_buy',
604
+ passed: false,
605
+ duration_ms: 0,
606
+ error: 'No products available to test creative references',
607
+ });
608
+ return { steps, profile };
609
+ }
610
+ const product = selectProduct(products, options);
611
+ if (!product) {
612
+ steps.push({
613
+ step: 'Build and reference creative',
614
+ task: 'create_media_buy',
615
+ passed: false,
616
+ duration_ms: 0,
617
+ error: 'No suitable product found for creative reference test',
618
+ });
619
+ return { steps, profile };
620
+ }
621
+ const pricingOption = selectPricingOption(product, options.pricing_models);
622
+ const formatId = selectFormatId(product);
623
+ if (!pricingOption) {
624
+ steps.push({
625
+ step: 'Build and reference creative',
626
+ task: 'create_media_buy',
627
+ passed: false,
628
+ duration_ms: 0,
629
+ error: `Product "${product.name}" has no pricing options`,
630
+ });
631
+ return { steps, profile };
632
+ }
633
+ const { result: buildResult, step: buildStep } = await (0, client_1.runStep)(`Build creative for reference flow (${formatIdToString(formatId)})`, 'build_creative', async () => client.executeTask('build_creative', {
634
+ target_format_id: formatId,
635
+ brand: (0, client_1.resolveBrand)(options),
636
+ message: `Create a reusable ad creative for the ${formatIdToString(formatId)} format`,
637
+ quality: 'draft',
638
+ }));
639
+ if (!buildResult?.success || !buildResult?.data) {
640
+ buildStep.passed = false;
641
+ buildStep.error = buildResult?.error || 'build_creative failed';
642
+ steps.push(buildStep);
643
+ return { steps, profile };
644
+ }
645
+ const manifest = extractCreativeManifest(buildResult.data);
646
+ if (!manifest?.assets) {
647
+ buildStep.passed = false;
648
+ buildStep.error = 'build_creative returned no creative_manifest';
649
+ steps.push(buildStep);
650
+ return { steps, profile };
651
+ }
652
+ const syncedCreative = buildSyncCreativeFromManifest(manifest, formatId);
653
+ buildStep.details = `Built creative manifest for ${syncedCreative.format_id}`;
654
+ buildStep.response_preview = JSON.stringify({
655
+ creative_id: syncedCreative.creative_id,
656
+ format_id: syncedCreative.format_id,
657
+ asset_keys: Object.keys(syncedCreative.assets || {}),
658
+ }, null, 2);
659
+ steps.push(buildStep);
660
+ const { result: syncResult, step: syncStep } = await (0, client_1.runStep)('Sync generated creative to library', 'sync_creatives', async () => client.executeTask('sync_creatives', {
661
+ creatives: [syncedCreative],
662
+ }));
663
+ if (!syncResult?.success || !syncResult?.data) {
664
+ syncStep.passed = false;
665
+ syncStep.error = syncResult?.error || 'sync_creatives failed';
666
+ steps.push(syncStep);
667
+ return { steps, profile };
668
+ }
669
+ syncStep.details = `Synced creative ${syncedCreative.creative_id} to seller library`;
670
+ syncStep.created_id = syncedCreative.creative_id;
671
+ syncStep.response_preview = JSON.stringify({
672
+ creative_id: syncedCreative.creative_id,
673
+ synced_count: (syncResult.data.creatives || []).length,
674
+ }, null, 2);
675
+ steps.push(syncStep);
676
+ const createRequest = buildCreateMediaBuyRequest(product, pricingOption, options, {
677
+ creative_ids: [syncedCreative.creative_id],
678
+ });
679
+ const { result: createResult, step: createStep } = await (0, client_1.runStep)('Create media buy with referenced creative', 'create_media_buy', async () => client.executeTask('create_media_buy', createRequest));
680
+ if (createResult?.success && createResult?.data) {
681
+ const mediaBuy = createResult.data;
682
+ const mediaBuyId = mediaBuy.media_buy_id || mediaBuy.media_buy?.media_buy_id;
683
+ const packages = mediaBuy.packages || mediaBuy.media_buy?.packages;
684
+ const referenced = packages?.some((pkg) => pkg.creative_ids?.includes(syncedCreative.creative_id));
685
+ createStep.details = `Created media buy with referenced creative ${syncedCreative.creative_id}`;
686
+ createStep.created_id = mediaBuyId;
687
+ createStep.response_preview = JSON.stringify({
688
+ media_buy_id: mediaBuyId,
689
+ creative_id: syncedCreative.creative_id,
690
+ referenced,
691
+ }, null, 2);
692
+ }
693
+ else if (createResult && !createResult.success) {
694
+ createStep.passed = false;
695
+ createStep.error = createResult.error || 'create_media_buy with creative_ids failed';
696
+ }
697
+ steps.push(createStep);
698
+ return { steps, profile };
699
+ }
417
700
  // SHA-256 lookalike placeholder for test email/phone hashes (not a real hash)
418
701
  const TEST_HASHED_EMAIL = 'a' + '0'.repeat(63);
419
702
  const TEST_HASHED_PHONE = 'b' + '0'.repeat(63);
@@ -432,7 +715,10 @@ async function resolveAccountForAudiences(options, tools, listAccounts) {
432
715
  if (options.sandbox && tools.includes('list_accounts')) {
433
716
  // Sandbox with list_accounts: try explicit sandbox path first (discover pre-existing test accounts)
434
717
  const { result: sandboxResult, step: sandboxStep } = await (0, client_1.runStep)('Discover sandbox accounts', 'list_accounts', async () => listAccounts({ sandbox: true }));
435
- const sandboxAccounts = sandboxResult?.success ? (sandboxResult.data?.accounts ?? []) : [];
718
+ const sandboxData = sandboxResult?.success
719
+ ? sandboxResult.data
720
+ : undefined;
721
+ const sandboxAccounts = sandboxData?.accounts ?? [];
436
722
  if (sandboxAccounts[0]?.account_id) {
437
723
  sandboxStep.details = `Using sandbox account: ${sandboxAccounts[0].account_id}`;
438
724
  steps.push(sandboxStep);
@@ -459,7 +745,8 @@ async function resolveAccountForAudiences(options, tools, listAccounts) {
459
745
  if (tools.includes('list_accounts')) {
460
746
  const { result: accountsResult, step: accountsStep } = await (0, client_1.runStep)('Discover accounts for audience sync', 'list_accounts', async () => listAccounts({}));
461
747
  if (accountsResult?.success && accountsResult?.data) {
462
- const accounts = accountsResult.data.accounts ?? [];
748
+ const accountsData = accountsResult.data;
749
+ const accounts = accountsData.accounts ?? [];
463
750
  if (accounts[0]?.account_id) {
464
751
  accountsStep.details = `Using account: ${accounts[0].account_id}`;
465
752
  steps.push(accountsStep);
@@ -497,7 +784,9 @@ async function testSyncAudiences(agentUrl, options) {
497
784
  });
498
785
  return { steps, profile };
499
786
  }
500
- const { accountRef, steps: accountSteps } = await resolveAccountForAudiences(options, profile.tools, async (params) => client.executeTask('list_accounts', params));
787
+ const { accountRef, steps: accountSteps } = await resolveAccountForAudiences(options, profile.tools,
788
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- intentional: test request bypasses strict typing
789
+ async (params) => client.listAccounts(params));
501
790
  steps.push(...accountSteps);
502
791
  if (!accountRef) {
503
792
  steps.push({
@@ -510,15 +799,17 @@ async function testSyncAudiences(agentUrl, options) {
510
799
  return { steps, profile };
511
800
  }
512
801
  // Step 1: Discovery call — list existing audiences without modification
513
- const { result: discoveryResult, step: discoveryStep } = await (0, client_1.runStep)('Discover existing audiences (discovery-only)', 'sync_audiences', async () => client.executeTask('sync_audiences', {
802
+ const { result: discoveryResult, step: discoveryStep } = await (0, client_1.runStep)('Discover existing audiences (discovery-only)', 'sync_audiences', async () => client.syncAudiences({
514
803
  account: accountRef,
804
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- intentional: test request bypasses strict typing
515
805
  }));
516
806
  if (discoveryResult?.success && discoveryResult?.data) {
517
- const audiences = discoveryResult.data.audiences ?? [];
807
+ const discoveryData = discoveryResult.data;
808
+ const audiences = discoveryData.audiences ?? [];
518
809
  discoveryStep.details = `Found ${audiences.length} existing audience(s)`;
519
810
  discoveryStep.response_preview = JSON.stringify({
520
811
  existing_audiences: audiences.length,
521
- audience_ids: audiences.map((a) => a.audience_id).slice(0, 5),
812
+ audience_ids: audiences.map(a => a.audience_id).slice(0, 5),
522
813
  }, null, 2);
523
814
  }
524
815
  else if (discoveryResult && !discoveryResult.success) {
@@ -531,7 +822,7 @@ async function testSyncAudiences(agentUrl, options) {
531
822
  }
532
823
  // Step 2: Create a test audience
533
824
  const testAudienceId = `adcp-test-audience-${Date.now()}`;
534
- const { result: createResult, step: createStep } = await (0, client_1.runStep)('Create test audience', 'sync_audiences', async () => client.executeTask('sync_audiences', {
825
+ const { result: createResult, step: createStep } = await (0, client_1.runStep)('Create test audience', 'sync_audiences', async () => client.syncAudiences({
535
826
  account: accountRef,
536
827
  audiences: [
537
828
  {
@@ -540,10 +831,12 @@ async function testSyncAudiences(agentUrl, options) {
540
831
  add: [{ hashed_email: TEST_HASHED_EMAIL }, { hashed_phone: TEST_HASHED_PHONE }],
541
832
  },
542
833
  ],
834
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- intentional: test request bypasses strict typing
543
835
  }));
544
836
  if (createResult?.success && createResult?.data) {
545
- const audiences = createResult.data.audiences ?? [];
546
- const testAudience = audiences.find((a) => a.audience_id === testAudienceId);
837
+ const createData = createResult.data;
838
+ const audiences = createData.audiences ?? [];
839
+ const testAudience = audiences.find(a => a.audience_id === testAudienceId);
547
840
  createStep.details = `Created audience "${testAudienceId}", action: ${testAudience?.action}, status: ${testAudience?.status ?? 'n/a'}`;
548
841
  createStep.created_id = testAudienceId;
549
842
  createStep.response_preview = JSON.stringify({
@@ -562,7 +855,7 @@ async function testSyncAudiences(agentUrl, options) {
562
855
  return { steps, profile };
563
856
  }
564
857
  // Step 3: Delete the test audience
565
- const { result: deleteResult, step: deleteStep } = await (0, client_1.runStep)('Delete test audience', 'sync_audiences', async () => client.executeTask('sync_audiences', {
858
+ const { result: deleteResult, step: deleteStep } = await (0, client_1.runStep)('Delete test audience', 'sync_audiences', async () => client.syncAudiences({
566
859
  account: accountRef,
567
860
  audiences: [
568
861
  {
@@ -570,10 +863,12 @@ async function testSyncAudiences(agentUrl, options) {
570
863
  delete: true,
571
864
  },
572
865
  ],
866
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- intentional: test request bypasses strict typing
573
867
  }));
574
868
  if (deleteResult?.success && deleteResult?.data) {
575
- const audiences = deleteResult.data.audiences ?? [];
576
- const deleted = audiences.find((a) => a.audience_id === testAudienceId);
869
+ const deleteData = deleteResult.data;
870
+ const audiences = deleteData.audiences ?? [];
871
+ const deleted = audiences.find(a => a.audience_id === testAudienceId);
577
872
  deleteStep.details = `Deleted audience "${testAudienceId}", action: ${deleted?.action}`;
578
873
  deleteStep.response_preview = JSON.stringify({
579
874
  audience_id: deleted?.audience_id,