@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
@@ -12,9 +12,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.testGovernancePropertyLists = testGovernancePropertyLists;
13
13
  exports.testGovernanceContentStandards = testGovernanceContentStandards;
14
14
  exports.testPropertyListFilters = testPropertyListFilters;
15
+ exports.testCampaignGovernance = testCampaignGovernance;
16
+ exports.testCampaignGovernanceDenied = testCampaignGovernanceDenied;
17
+ exports.testCampaignGovernanceConditions = testCampaignGovernanceConditions;
18
+ exports.testCampaignGovernanceDelivery = testCampaignGovernanceDelivery;
15
19
  exports.hasGovernanceTools = hasGovernanceTools;
20
+ exports.hasCampaignGovernanceTools = hasCampaignGovernanceTools;
16
21
  const client_1 = require("../client");
17
22
  const capabilities_1 = require("../../utils/capabilities");
23
+ const GovernanceMiddleware_1 = require("../../core/GovernanceMiddleware");
18
24
  // Property list tools
19
25
  const PROPERTY_LIST_TOOLS = [
20
26
  'create_property_list',
@@ -65,7 +71,7 @@ async function testGovernancePropertyLists(agentUrl, options) {
65
71
  // Test: create_property_list
66
72
  if (profile.tools.includes('create_property_list')) {
67
73
  const listName = options.property_list_name || `E2E Test List ${Date.now()}`;
68
- const { result, step } = await (0, client_1.runStep)('Create property list', 'create_property_list', async () => client.executeTask('create_property_list', {
74
+ const { result, step } = await (0, client_1.runStep)('Create property list', 'create_property_list', async () => client.createPropertyList({
69
75
  name: listName,
70
76
  description: 'E2E test property list for governance testing',
71
77
  base_properties: {
@@ -89,7 +95,7 @@ async function testGovernancePropertyLists(agentUrl, options) {
89
95
  }));
90
96
  if (result?.success && result?.data) {
91
97
  const data = result.data;
92
- createdListId = data.list?.list_id || data.list_id;
98
+ createdListId = data.list?.list_id;
93
99
  authToken = data.auth_token;
94
100
  step.created_id = createdListId;
95
101
  step.details = `Created list: ${createdListId}`;
@@ -107,18 +113,19 @@ async function testGovernancePropertyLists(agentUrl, options) {
107
113
  }
108
114
  // Test: get_property_list
109
115
  if (profile.tools.includes('get_property_list') && createdListId) {
110
- const { result, step } = await (0, client_1.runStep)('Get property list', 'get_property_list', async () => client.executeTask('get_property_list', {
116
+ const { result, step } = await (0, client_1.runStep)('Get property list', 'get_property_list', async () => client.getPropertyList({
111
117
  list_id: createdListId,
112
118
  resolve: true,
113
119
  max_results: 10,
114
120
  }));
115
121
  if (result?.success && result?.data) {
116
122
  const data = result.data;
117
- step.details = `Retrieved list with ${data.total_count || 0} properties`;
123
+ const dataRecord = result.data;
124
+ step.details = `Retrieved list with ${dataRecord.total_count || 0} properties`;
118
125
  step.response_preview = JSON.stringify({
119
126
  list_id: data.list?.list_id,
120
127
  name: data.list?.name,
121
- property_count: data.total_count,
128
+ property_count: dataRecord.total_count,
122
129
  has_identifiers: !!data.identifiers?.length,
123
130
  }, null, 2);
124
131
  }
@@ -130,7 +137,7 @@ async function testGovernancePropertyLists(agentUrl, options) {
130
137
  }
131
138
  // Test: update_property_list
132
139
  if (profile.tools.includes('update_property_list') && createdListId) {
133
- const { result, step } = await (0, client_1.runStep)('Update property list', 'update_property_list', async () => client.executeTask('update_property_list', {
140
+ const { result, step } = await (0, client_1.runStep)('Update property list', 'update_property_list', async () => client.updatePropertyList({
134
141
  list_id: createdListId,
135
142
  auth_token: authToken,
136
143
  description: 'Updated E2E test property list',
@@ -166,16 +173,17 @@ async function testGovernancePropertyLists(agentUrl, options) {
166
173
  }
167
174
  // Test: list_property_lists
168
175
  if (profile.tools.includes('list_property_lists')) {
169
- const { result, step } = await (0, client_1.runStep)('List property lists', 'list_property_lists', async () => client.executeTask('list_property_lists', {
176
+ const { result, step } = await (0, client_1.runStep)('List property lists', 'list_property_lists', async () => client.listPropertyLists({
170
177
  max_results: 10,
171
178
  }));
172
179
  if (result?.success && result?.data) {
173
180
  const data = result.data;
181
+ const dataRecord = result.data;
174
182
  const lists = data.lists || [];
175
183
  step.details = `Found ${lists.length} property list(s)`;
176
184
  step.response_preview = JSON.stringify({
177
- total_count: data.total_count,
178
- returned_count: data.returned_count || lists.length,
185
+ total_count: dataRecord.total_count,
186
+ returned_count: dataRecord.returned_count || lists.length,
179
187
  lists: lists.slice(0, 3).map((l) => ({
180
188
  list_id: l.list_id,
181
189
  name: l.name,
@@ -190,7 +198,7 @@ async function testGovernancePropertyLists(agentUrl, options) {
190
198
  }
191
199
  // Test: delete_property_list
192
200
  if (profile.tools.includes('delete_property_list') && createdListId && options.dry_run === false) {
193
- const { result, step } = await (0, client_1.runStep)('Delete property list', 'delete_property_list', async () => client.executeTask('delete_property_list', {
201
+ const { result, step } = await (0, client_1.runStep)('Delete property list', 'delete_property_list', async () => client.deletePropertyList({
194
202
  list_id: createdListId,
195
203
  auth_token: authToken,
196
204
  }));
@@ -209,7 +217,7 @@ async function testGovernancePropertyLists(agentUrl, options) {
209
217
  steps.push(step);
210
218
  // Test: get deleted list (should fail)
211
219
  if (profile.tools.includes('get_property_list')) {
212
- const { result: errorResult, step: errorStep } = await (0, client_1.runStep)('Get deleted list (error expected)', 'get_property_list', async () => client.executeTask('get_property_list', {
220
+ const { result: errorResult, step: errorStep } = await (0, client_1.runStep)('Get deleted list (error expected)', 'get_property_list', async () => client.getPropertyList({
213
221
  list_id: createdListId,
214
222
  }));
215
223
  if (errorResult?.success) {
@@ -265,13 +273,13 @@ async function testGovernanceContentStandards(agentUrl, options) {
265
273
  let discoveredStandardsId;
266
274
  // Test: list_content_standards
267
275
  if (profile.tools.includes('list_content_standards')) {
268
- const { result, step } = await (0, client_1.runStep)('List content standards', 'list_content_standards', async () => client.executeTask('list_content_standards', {
276
+ const { result, step } = await (0, client_1.runStep)('List content standards', 'list_content_standards', async () => client.listContentStandards({
269
277
  context: 'E2E testing - list available brand safety configurations',
270
278
  max_results: 10,
271
279
  }));
272
280
  if (result?.success && result?.data) {
273
281
  const data = result.data;
274
- const standards = data.standards || [];
282
+ const standards = 'standards' in data ? data.standards : [];
275
283
  if (standards.length > 0) {
276
284
  discoveredStandardsId = standards[0].standards_id;
277
285
  }
@@ -302,16 +310,18 @@ async function testGovernanceContentStandards(agentUrl, options) {
302
310
  // Test: get_content_standards (if we found one)
303
311
  const standardsIdToTest = options.content_standards_id || discoveredStandardsId;
304
312
  if (profile.tools.includes('get_content_standards') && standardsIdToTest) {
305
- const { result, step } = await (0, client_1.runStep)('Get content standards', 'get_content_standards', async () => client.executeTask('get_content_standards', {
313
+ const { result, step } = await (0, client_1.runStep)('Get content standards', 'get_content_standards', async () => client.getContentStandards({
306
314
  standards_id: standardsIdToTest,
307
315
  }));
308
316
  if (result?.success && result?.data) {
309
317
  const data = result.data;
310
- step.details = `Retrieved standards: ${data.standards?.name || standardsIdToTest}`;
318
+ // Response may be a ContentStandards directly or wrapped in { standards: ... }
319
+ const standards = data.standards || data;
320
+ step.details = `Retrieved standards: ${String(standards.name || standardsIdToTest)}`;
311
321
  step.response_preview = JSON.stringify({
312
- standards_id: data.standards?.standards_id,
313
- name: data.standards?.name,
314
- features_count: data.standards?.features?.length || 0,
322
+ standards_id: standards.standards_id,
323
+ name: standards.name,
324
+ features_count: Array.isArray(standards.features) ? standards.features.length : 0,
315
325
  }, null, 2);
316
326
  }
317
327
  else if (result && !result.success) {
@@ -322,7 +332,7 @@ async function testGovernanceContentStandards(agentUrl, options) {
322
332
  }
323
333
  // Test: calibrate_content
324
334
  if (profile.tools.includes('calibrate_content')) {
325
- const { result, step } = await (0, client_1.runStep)('Calibrate content', 'calibrate_content', async () => client.executeTask('calibrate_content', {
335
+ const { result, step } = await (0, client_1.runStep)('Calibrate content', 'calibrate_content', async () => client.calibrateContent({
326
336
  context: 'E2E testing - evaluate sample content for brand safety',
327
337
  standards_id: standardsIdToTest,
328
338
  artifacts: [
@@ -343,12 +353,12 @@ async function testGovernanceContentStandards(agentUrl, options) {
343
353
  }));
344
354
  if (result?.success && result?.data) {
345
355
  const data = result.data;
346
- step.details = `Calibration session: ${data.session_status || 'completed'}`;
356
+ step.details = `Calibration session: ${String(data.session_status || 'completed')}`;
347
357
  step.response_preview = JSON.stringify({
348
358
  session_id: data.session_id,
349
359
  session_status: data.session_status,
350
- pending_artifacts: data.pending_artifacts?.length || 0,
351
- evaluated_artifacts: data.evaluated_artifacts?.length || 0,
360
+ pending_artifacts: Array.isArray(data.pending_artifacts) ? data.pending_artifacts.length : 0,
361
+ evaluated_artifacts: Array.isArray(data.evaluated_artifacts) ? data.evaluated_artifacts.length : 0,
352
362
  }, null, 2);
353
363
  }
354
364
  else if (result && !result.success) {
@@ -366,7 +376,7 @@ async function testGovernanceContentStandards(agentUrl, options) {
366
376
  }
367
377
  // Test: validate_content_delivery
368
378
  if (profile.tools.includes('validate_content_delivery')) {
369
- const { result, step } = await (0, client_1.runStep)('Validate content delivery', 'validate_content_delivery', async () => client.executeTask('validate_content_delivery', {
379
+ const { result, step } = await (0, client_1.runStep)('Validate content delivery', 'validate_content_delivery', async () => client.validateContentDelivery({
370
380
  context: 'E2E testing - validate delivery records against content standards',
371
381
  standards_id: standardsIdToTest,
372
382
  records: [
@@ -383,11 +393,12 @@ async function testGovernanceContentStandards(agentUrl, options) {
383
393
  }));
384
394
  if (result?.success && result?.data) {
385
395
  const data = result.data;
386
- step.details = `Validated ${data.summary?.total_records || 0} record(s)`;
396
+ const summary = data.summary;
397
+ step.details = `Validated ${summary?.total_records || 0} record(s)`;
387
398
  step.response_preview = JSON.stringify({
388
- total_records: data.summary?.total_records,
389
- passed_records: data.summary?.passed_records,
390
- failed_records: data.summary?.failed_records,
399
+ total_records: summary?.total_records,
400
+ passed_records: summary?.passed_records,
401
+ failed_records: summary?.failed_records,
391
402
  }, null, 2);
392
403
  }
393
404
  else if (result && !result.success) {
@@ -405,7 +416,7 @@ async function testGovernanceContentStandards(agentUrl, options) {
405
416
  }
406
417
  // Test: get_content_standards with invalid ID (error expected)
407
418
  if (profile.tools.includes('get_content_standards')) {
408
- const { result: errorResult, step: errorStep } = await (0, client_1.runStep)('Get invalid content standards (error expected)', 'get_content_standards', async () => client.executeTask('get_content_standards', {
419
+ const { result: errorResult, step: errorStep } = await (0, client_1.runStep)('Get invalid content standards (error expected)', 'get_content_standards', async () => client.getContentStandards({
409
420
  standards_id: 'INVALID_STANDARDS_ID_DOES_NOT_EXIST_12345',
410
421
  }));
411
422
  if (errorResult?.success) {
@@ -454,12 +465,15 @@ async function testPropertyListFilters(agentUrl, options) {
454
465
  // Optionally discover feature IDs from get_adcp_capabilities
455
466
  let featureRequirements;
456
467
  if (profile.tools.includes('get_adcp_capabilities')) {
457
- const { result: capResult, step: capStep } = await (0, client_1.runStep)('Discover available features (for feature_requirements filter)', 'get_adcp_capabilities', async () => client.executeTask('get_adcp_capabilities', {}));
468
+ const { result: capResult, step: capStep } = await (0, client_1.runStep)('Discover available features (for feature_requirements filter)', 'get_adcp_capabilities', async () => client.getAdcpCapabilities({}));
458
469
  if (capResult?.success && capResult?.data) {
459
- const rawFeatures = capResult.data?.media_buy?.features;
470
+ const capData = capResult.data;
471
+ const mediaBuy = capData.media_buy;
472
+ const rawFeatures = mediaBuy?.features;
460
473
  if (rawFeatures && typeof rawFeatures === 'object') {
461
474
  // Use boolean feature keys as feature_ids (agents that support dynamic features will have IDs)
462
- const featureIds = Object.keys(rawFeatures).filter(k => rawFeatures[k] === true);
475
+ const featuresRecord = rawFeatures;
476
+ const featureIds = Object.keys(featuresRecord).filter(k => featuresRecord[k] === true);
463
477
  if (featureIds.length > 0) {
464
478
  featureRequirements = featureIds.map(id => ({
465
479
  feature_id: id,
@@ -498,7 +512,7 @@ async function testPropertyListFilters(agentUrl, options) {
498
512
  let authToken;
499
513
  // Create property list with all filters
500
514
  const listName = `E2E Filter Test ${Date.now()}`;
501
- const { result: createResult, step: createStep } = await (0, client_1.runStep)('Create property list with all filter types', 'create_property_list', async () => client.executeTask('create_property_list', {
515
+ const { result: createResult, step: createStep } = await (0, client_1.runStep)('Create property list with all filter types', 'create_property_list', async () => client.createPropertyList({
502
516
  name: listName,
503
517
  description: 'E2E filter round-trip test',
504
518
  base_properties: {
@@ -512,7 +526,7 @@ async function testPropertyListFilters(agentUrl, options) {
512
526
  }));
513
527
  if (createResult?.success && createResult?.data) {
514
528
  const data = createResult.data;
515
- createdListId = data.list?.list_id || data.list_id;
529
+ createdListId = data.list?.list_id;
516
530
  authToken = data.auth_token;
517
531
  createStep.created_id = createdListId;
518
532
  createStep.details = `Created list: ${createdListId} with ${Object.keys(filters).length} filter type(s)`;
@@ -526,7 +540,7 @@ async function testPropertyListFilters(agentUrl, options) {
526
540
  return { steps, profile };
527
541
  }
528
542
  // Retrieve with resolve:true and validate filter round-trip
529
- const { result: getResult, step: getStep } = await (0, client_1.runStep)('Get property list (resolve: true, validate filter round-trip)', 'get_property_list', async () => client.executeTask('get_property_list', {
543
+ const { result: getResult, step: getStep } = await (0, client_1.runStep)('Get property list (resolve: true, validate filter round-trip)', 'get_property_list', async () => client.getPropertyList({
530
544
  list_id: createdListId,
531
545
  resolve: true,
532
546
  max_results: 5,
@@ -536,16 +550,19 @@ async function testPropertyListFilters(agentUrl, options) {
536
550
  const returnedFilters = data.list?.filters;
537
551
  const issues = [];
538
552
  if (returnedFilters) {
539
- if (!returnedFilters.garm_categories?.exclude?.length) {
553
+ const garmCategories = returnedFilters.garm_categories;
554
+ if (!Array.isArray(garmCategories?.exclude) || !garmCategories.exclude.length) {
540
555
  issues.push('garm_categories.exclude not preserved');
541
556
  }
542
- if (returnedFilters.mfa_thresholds?.min_score !== 0.75) {
543
- issues.push(`mfa_thresholds.min_score mismatch: got ${returnedFilters.mfa_thresholds?.min_score}, expected 0.75`);
557
+ const mfaThresholds = returnedFilters.mfa_thresholds;
558
+ if (mfaThresholds?.min_score !== 0.75) {
559
+ issues.push(`mfa_thresholds.min_score mismatch: got ${mfaThresholds?.min_score}, expected 0.75`);
544
560
  }
545
561
  if (!returnedFilters.custom_tags) {
546
562
  issues.push('custom_tags filter not preserved');
547
563
  }
548
- if (featureRequirements && !returnedFilters.feature_requirements?.length) {
564
+ const featureReqs = returnedFilters.feature_requirements;
565
+ if (featureRequirements && !(Array.isArray(featureReqs) && featureReqs.length)) {
549
566
  issues.push('feature_requirements filter not preserved');
550
567
  }
551
568
  }
@@ -571,7 +588,7 @@ async function testPropertyListFilters(agentUrl, options) {
571
588
  steps.push(getStep);
572
589
  // Cleanup: delete if not dry-run
573
590
  if (options.dry_run === false && profile.tools.includes('delete_property_list')) {
574
- const { result: delResult, step: delStep } = await (0, client_1.runStep)('Delete test property list (cleanup)', 'delete_property_list', async () => client.executeTask('delete_property_list', {
591
+ const { result: delResult, step: delStep } = await (0, client_1.runStep)('Delete test property list (cleanup)', 'delete_property_list', async () => client.deletePropertyList({
575
592
  list_id: createdListId,
576
593
  auth_token: authToken,
577
594
  }));
@@ -593,10 +610,679 @@ async function testPropertyListFilters(agentUrl, options) {
593
610
  }
594
611
  return { steps, profile };
595
612
  }
613
+ // Campaign governance tools
614
+ const CAMPAIGN_GOVERNANCE_TOOLS = [
615
+ 'sync_plans',
616
+ 'check_governance',
617
+ 'report_plan_outcome',
618
+ 'get_plan_audit_logs',
619
+ ];
620
+ /**
621
+ * Test: Campaign Governance - Full Lifecycle
622
+ *
623
+ * Flow: sync_plans -> check_governance(proposed, approved) -> create_media_buy
624
+ * -> report_plan_outcome(completed)
625
+ *
626
+ * Tests the happy path: buyer syncs a plan, gets approval, executes,
627
+ * and reports the outcome back to the governance agent.
628
+ */
629
+ async function testCampaignGovernance(agentUrl, options) {
630
+ const steps = [];
631
+ const client = (0, client_1.createTestClient)(agentUrl, options.protocol || 'mcp', options);
632
+ // Discover agent profile
633
+ const { profile, step: profileStep } = await (0, client_1.discoverAgentProfile)(client);
634
+ steps.push(profileStep);
635
+ if (!profileStep.passed) {
636
+ return { steps, profile };
637
+ }
638
+ // Check if agent supports campaign governance tools
639
+ const hasCampaignGovernance = CAMPAIGN_GOVERNANCE_TOOLS.some(t => profile.tools.includes(t));
640
+ if (!hasCampaignGovernance) {
641
+ steps.push({
642
+ step: 'Campaign governance support check',
643
+ passed: false,
644
+ duration_ms: 0,
645
+ error: 'Agent does not support campaign governance tools',
646
+ details: `Required: at least one of ${CAMPAIGN_GOVERNANCE_TOOLS.join(', ')}. Available: ${profile.tools.join(', ')}`,
647
+ });
648
+ return { steps, profile };
649
+ }
650
+ profile.supports_governance = true;
651
+ const testPlanId = `test-plan-${Date.now()}`;
652
+ const testCampaignRef = `test-campaign-${Date.now()}`;
653
+ const callerUrl = 'https://test-orchestrator.example.com';
654
+ const flightStart = new Date();
655
+ const flightEnd = new Date(flightStart.getTime() + 30 * 24 * 60 * 60 * 1000); // 30 days
656
+ // Step 1: sync_plans
657
+ if (profile.tools.includes('sync_plans')) {
658
+ const { result, step } = await (0, client_1.runStep)('Sync campaign governance plan', 'sync_plans', async () => client.executeTask('sync_plans', {
659
+ plans: [
660
+ {
661
+ plan_id: testPlanId,
662
+ brand: options.brand || { domain: 'test.example.com' },
663
+ objectives: 'E2E test campaign for governance protocol validation',
664
+ budget: {
665
+ total: options.budget || 10000,
666
+ currency: 'USD',
667
+ authority_level: 'agent_full',
668
+ },
669
+ flight: {
670
+ start: flightStart.toISOString(),
671
+ end: flightEnd.toISOString(),
672
+ },
673
+ countries: ['US'],
674
+ channels: {
675
+ allowed: ['display', 'video'],
676
+ },
677
+ delegations: [
678
+ {
679
+ agent_url: callerUrl,
680
+ authority: 'full',
681
+ },
682
+ ],
683
+ },
684
+ ],
685
+ }));
686
+ if (result?.success && result?.data) {
687
+ const data = result.data;
688
+ const plans = data.plans || [];
689
+ const synced = plans.find((p) => p.plan_id === testPlanId);
690
+ if (synced?.status === 'active') {
691
+ step.details = `Plan synced: ${testPlanId}, version ${synced.version}, ${(synced.categories || []).length} categories active`;
692
+ step.response_preview = JSON.stringify({
693
+ plan_id: synced.plan_id,
694
+ status: synced.status,
695
+ version: synced.version,
696
+ categories: synced.categories?.map((c) => c.category_id),
697
+ resolved_policies: synced.resolved_policies?.length || 0,
698
+ }, null, 2);
699
+ }
700
+ else {
701
+ step.passed = false;
702
+ step.error = synced
703
+ ? `Plan sync returned status '${synced.status}' instead of 'active'`
704
+ : 'Plan not found in sync response';
705
+ }
706
+ }
707
+ else if (result && !result.success) {
708
+ step.passed = false;
709
+ step.error = result.error || 'sync_plans failed';
710
+ }
711
+ steps.push(step);
712
+ }
713
+ // Step 2: check_governance (proposed, expecting approved)
714
+ let checkId;
715
+ if (profile.tools.includes('check_governance')) {
716
+ const { result, step } = await (0, client_1.runStep)('Check governance (proposed buy)', 'check_governance', async () => client.executeTask('check_governance', {
717
+ plan_id: testPlanId,
718
+ buyer_campaign_ref: testCampaignRef,
719
+ binding: 'proposed',
720
+ caller: callerUrl,
721
+ tool: 'create_media_buy',
722
+ payload: {
723
+ buyer_ref: `e2e-test-${Date.now()}`,
724
+ channel: 'display',
725
+ budget: { total: 1000, currency: 'USD' },
726
+ flight: {
727
+ start: flightStart.toISOString(),
728
+ end: flightEnd.toISOString(),
729
+ },
730
+ countries: ['US'],
731
+ },
732
+ }));
733
+ if (result?.success && result?.data) {
734
+ const data = result.data;
735
+ checkId = data.check_id;
736
+ const status = data.status;
737
+ step.details = `Governance check: status=${status}, binding=${data.binding}, mode=${data.mode || 'unknown'}`;
738
+ step.response_preview = JSON.stringify({
739
+ check_id: data.check_id,
740
+ status: data.status,
741
+ binding: data.binding,
742
+ mode: data.mode,
743
+ explanation: data.explanation,
744
+ findings_count: data.findings?.length || 0,
745
+ expires_at: data.expires_at,
746
+ }, null, 2);
747
+ // Any status is valid — we're testing the protocol, not the policy
748
+ if (!['approved', 'denied', 'conditions', 'escalated'].includes(status)) {
749
+ step.passed = false;
750
+ step.error = `Unexpected governance status: ${status}`;
751
+ }
752
+ }
753
+ else if (result && !result.success) {
754
+ step.passed = false;
755
+ step.error = result.error || 'check_governance failed';
756
+ }
757
+ steps.push(step);
758
+ }
759
+ // Step 3: report_plan_outcome (completed)
760
+ if (profile.tools.includes('report_plan_outcome') && checkId) {
761
+ const { result, step } = await (0, client_1.runStep)('Report plan outcome (completed)', 'report_plan_outcome', async () => client.executeTask('report_plan_outcome', {
762
+ plan_id: testPlanId,
763
+ check_id: checkId,
764
+ buyer_campaign_ref: testCampaignRef,
765
+ outcome: 'completed',
766
+ seller_response: {
767
+ media_buy_id: `test-mb-${Date.now()}`,
768
+ buyer_ref: `e2e-test-${Date.now()}`,
769
+ packages: [
770
+ {
771
+ package_id: 'test-pkg-1',
772
+ name: 'E2E Test Package',
773
+ budget: { total: 1000, currency: 'USD' },
774
+ },
775
+ ],
776
+ },
777
+ }));
778
+ if (result?.success && result?.data) {
779
+ step.details = 'Outcome reported to governance agent';
780
+ step.response_preview = JSON.stringify(result.data, null, 2);
781
+ }
782
+ else if (result && !result.success) {
783
+ step.passed = false;
784
+ step.error = result.error || 'report_plan_outcome failed';
785
+ }
786
+ steps.push(step);
787
+ }
788
+ // Step 4: get_plan_audit_logs
789
+ if (profile.tools.includes('get_plan_audit_logs')) {
790
+ const { result, step } = await (0, client_1.runStep)('Get plan audit logs', 'get_plan_audit_logs', async () => client.executeTask('get_plan_audit_logs', {
791
+ plan_ids: [testPlanId],
792
+ buyer_campaign_ref: testCampaignRef,
793
+ include_entries: true,
794
+ }));
795
+ if (result?.success && result?.data) {
796
+ const data = result.data;
797
+ step.details = 'Audit logs retrieved';
798
+ step.response_preview = JSON.stringify({
799
+ plans_returned: data.plans?.length || 0,
800
+ has_entries: !!data.plans?.[0]?.entries?.length,
801
+ budget: data.plans?.[0]?.budget,
802
+ }, null, 2);
803
+ }
804
+ else if (result && !result.success) {
805
+ step.passed = false;
806
+ step.error = result.error || 'get_plan_audit_logs failed';
807
+ }
808
+ steps.push(step);
809
+ }
810
+ return { steps, profile };
811
+ }
812
+ /**
813
+ * Test: Campaign Governance - Denied Flow
814
+ *
815
+ * Sends a check_governance request that should be denied (budget exceeds plan,
816
+ * unauthorized market). Validates that the governance agent returns meaningful
817
+ * findings and explanations.
818
+ */
819
+ async function testCampaignGovernanceDenied(agentUrl, options) {
820
+ const steps = [];
821
+ const client = (0, client_1.createTestClient)(agentUrl, options.protocol || 'mcp', options);
822
+ const { profile, step: profileStep } = await (0, client_1.discoverAgentProfile)(client);
823
+ steps.push(profileStep);
824
+ if (!profileStep.passed) {
825
+ return { steps, profile };
826
+ }
827
+ if (!profile.tools.includes('sync_plans') || !profile.tools.includes('check_governance')) {
828
+ steps.push({
829
+ step: 'Campaign governance denied flow support check',
830
+ passed: false,
831
+ duration_ms: 0,
832
+ error: 'Agent requires sync_plans + check_governance for denied flow testing',
833
+ });
834
+ return { steps, profile };
835
+ }
836
+ profile.supports_governance = true;
837
+ const testPlanId = `test-denied-plan-${Date.now()}`;
838
+ const testCampaignRef = `test-denied-campaign-${Date.now()}`;
839
+ const callerUrl = 'https://test-orchestrator.example.com';
840
+ const flightStart = new Date();
841
+ const flightEnd = new Date(flightStart.getTime() + 7 * 24 * 60 * 60 * 1000);
842
+ // Sync a restrictive plan: small budget, US only
843
+ const { result: syncResult, step: syncStep } = await (0, client_1.runStep)('Sync restrictive plan (small budget, US only)', 'sync_plans', async () => client.executeTask('sync_plans', {
844
+ plans: [
845
+ {
846
+ plan_id: testPlanId,
847
+ brand: options.brand || { domain: 'test.example.com' },
848
+ objectives: 'E2E denial test: budget and geo restrictions',
849
+ budget: {
850
+ total: 500,
851
+ currency: 'USD',
852
+ authority_level: 'agent_limited',
853
+ reallocation_threshold: 100,
854
+ },
855
+ flight: {
856
+ start: flightStart.toISOString(),
857
+ end: flightEnd.toISOString(),
858
+ },
859
+ countries: ['US'],
860
+ channels: {
861
+ allowed: ['display'],
862
+ },
863
+ },
864
+ ],
865
+ }));
866
+ steps.push(syncStep);
867
+ if (!syncResult?.success) {
868
+ return { steps, profile };
869
+ }
870
+ // Check governance with over-budget request
871
+ const { result: overBudgetResult, step: overBudgetStep } = await (0, client_1.runStep)('Check governance (over-budget, expecting denial or conditions)', 'check_governance', async () => client.executeTask('check_governance', {
872
+ plan_id: testPlanId,
873
+ buyer_campaign_ref: testCampaignRef,
874
+ binding: 'proposed',
875
+ caller: callerUrl,
876
+ tool: 'create_media_buy',
877
+ payload: {
878
+ buyer_ref: `e2e-overbudget-${Date.now()}`,
879
+ channel: 'display',
880
+ budget: { total: 50000, currency: 'USD' },
881
+ flight: {
882
+ start: flightStart.toISOString(),
883
+ end: flightEnd.toISOString(),
884
+ },
885
+ countries: ['US'],
886
+ },
887
+ }));
888
+ if (overBudgetResult?.success && overBudgetResult?.data) {
889
+ const data = overBudgetResult.data;
890
+ overBudgetStep.details = `Over-budget check: status=${data.status}, explanation: ${data.explanation}`;
891
+ overBudgetStep.response_preview = JSON.stringify({
892
+ status: data.status,
893
+ explanation: data.explanation,
894
+ findings: data.findings?.map((f) => ({
895
+ category_id: f.category_id,
896
+ severity: f.severity,
897
+ explanation: f.explanation,
898
+ })),
899
+ conditions: data.conditions,
900
+ }, null, 2);
901
+ if (data.status === 'approved' && data.mode !== 'advisory' && data.mode !== 'audit') {
902
+ overBudgetStep.passed = false;
903
+ overBudgetStep.error =
904
+ 'Governance approved a $50,000 buy against a $500 plan in enforce mode — expected denial or conditions';
905
+ }
906
+ else if (data.status === 'approved') {
907
+ overBudgetStep.warnings = [
908
+ 'Governance approved a $50,000 buy against a $500 plan — advisory/audit mode detected',
909
+ ];
910
+ }
911
+ }
912
+ else if (overBudgetResult && !overBudgetResult.success) {
913
+ overBudgetStep.passed = false;
914
+ overBudgetStep.error = overBudgetResult.error || 'check_governance failed';
915
+ }
916
+ steps.push(overBudgetStep);
917
+ // Check governance with unauthorized market
918
+ const { result: geoResult, step: geoStep } = await (0, client_1.runStep)('Check governance (unauthorized market, expecting denial or conditions)', 'check_governance', async () => client.executeTask('check_governance', {
919
+ plan_id: testPlanId,
920
+ buyer_campaign_ref: testCampaignRef,
921
+ binding: 'proposed',
922
+ caller: callerUrl,
923
+ tool: 'create_media_buy',
924
+ payload: {
925
+ buyer_ref: `e2e-badgeo-${Date.now()}`,
926
+ channel: 'display',
927
+ budget: { total: 100, currency: 'USD' },
928
+ flight: {
929
+ start: flightStart.toISOString(),
930
+ end: flightEnd.toISOString(),
931
+ },
932
+ countries: ['CN', 'RU'],
933
+ },
934
+ }));
935
+ if (geoResult?.success && geoResult?.data) {
936
+ const data = geoResult.data;
937
+ geoStep.details = `Unauthorized market check: status=${data.status}`;
938
+ geoStep.response_preview = JSON.stringify({
939
+ status: data.status,
940
+ explanation: data.explanation,
941
+ findings: data.findings?.map((f) => ({
942
+ category_id: f.category_id,
943
+ explanation: f.explanation,
944
+ })),
945
+ }, null, 2);
946
+ if (data.status === 'approved') {
947
+ geoStep.warnings = [
948
+ 'Governance approved targeting CN/RU against US-only plan — may indicate audit/advisory mode',
949
+ ];
950
+ }
951
+ }
952
+ else if (geoResult && !geoResult.success) {
953
+ geoStep.passed = false;
954
+ geoStep.error = geoResult.error || 'check_governance failed';
955
+ }
956
+ steps.push(geoStep);
957
+ // Report failed outcome
958
+ if (profile.tools.includes('report_plan_outcome') && overBudgetResult?.data?.check_id) {
959
+ const { step: outcomeStep } = await (0, client_1.runStep)('Report failed outcome for denied check', 'report_plan_outcome', async () => client.executeTask('report_plan_outcome', {
960
+ plan_id: testPlanId,
961
+ check_id: overBudgetResult.data.check_id,
962
+ buyer_campaign_ref: testCampaignRef,
963
+ outcome: 'failed',
964
+ error: {
965
+ code: 'governance_denied',
966
+ message: 'Action blocked by governance check',
967
+ },
968
+ }));
969
+ steps.push(outcomeStep);
970
+ }
971
+ return { steps, profile };
972
+ }
973
+ /**
974
+ * Test: Campaign Governance - Conditions Flow
975
+ *
976
+ * Syncs a plan, sends a check that may trigger conditions (e.g., budget
977
+ * concentration limit), applies machine-actionable conditions, and re-checks.
978
+ */
979
+ async function testCampaignGovernanceConditions(agentUrl, options) {
980
+ const steps = [];
981
+ const client = (0, client_1.createTestClient)(agentUrl, options.protocol || 'mcp', options);
982
+ const { profile, step: profileStep } = await (0, client_1.discoverAgentProfile)(client);
983
+ steps.push(profileStep);
984
+ if (!profileStep.passed) {
985
+ return { steps, profile };
986
+ }
987
+ if (!profile.tools.includes('sync_plans') || !profile.tools.includes('check_governance')) {
988
+ steps.push({
989
+ step: 'Campaign governance conditions flow support check',
990
+ passed: false,
991
+ duration_ms: 0,
992
+ error: 'Agent requires sync_plans + check_governance for conditions flow testing',
993
+ });
994
+ return { steps, profile };
995
+ }
996
+ profile.supports_governance = true;
997
+ const testPlanId = `test-conditions-plan-${Date.now()}`;
998
+ const testCampaignRef = `test-conditions-campaign-${Date.now()}`;
999
+ const callerUrl = 'https://test-orchestrator.example.com';
1000
+ const flightStart = new Date();
1001
+ const flightEnd = new Date(flightStart.getTime() + 14 * 24 * 60 * 60 * 1000);
1002
+ // Sync a plan with per_seller_max_pct constraint
1003
+ const { result: syncResult, step: syncStep } = await (0, client_1.runStep)('Sync plan with seller concentration limit', 'sync_plans', async () => client.executeTask('sync_plans', {
1004
+ plans: [
1005
+ {
1006
+ plan_id: testPlanId,
1007
+ brand: options.brand || { domain: 'test.example.com' },
1008
+ objectives: 'E2E conditions test: budget cap per seller',
1009
+ budget: {
1010
+ total: 5000,
1011
+ currency: 'USD',
1012
+ authority_level: 'agent_limited',
1013
+ per_seller_max_pct: 50,
1014
+ reallocation_threshold: 500,
1015
+ },
1016
+ flight: {
1017
+ start: flightStart.toISOString(),
1018
+ end: flightEnd.toISOString(),
1019
+ },
1020
+ countries: ['US'],
1021
+ channels: {
1022
+ allowed: ['display', 'video'],
1023
+ mix_targets: {
1024
+ display: { min_pct: 30, max_pct: 70 },
1025
+ video: { min_pct: 30, max_pct: 70 },
1026
+ },
1027
+ },
1028
+ },
1029
+ ],
1030
+ }));
1031
+ steps.push(syncStep);
1032
+ if (!syncResult?.success) {
1033
+ return { steps, profile };
1034
+ }
1035
+ // First check: send budget that might trigger conditions
1036
+ const { result: checkResult, step: checkStep } = await (0, client_1.runStep)('Check governance (may trigger conditions)', 'check_governance', async () => client.executeTask('check_governance', {
1037
+ plan_id: testPlanId,
1038
+ buyer_campaign_ref: testCampaignRef,
1039
+ binding: 'proposed',
1040
+ caller: callerUrl,
1041
+ tool: 'create_media_buy',
1042
+ payload: {
1043
+ buyer_ref: `e2e-conditions-${Date.now()}`,
1044
+ channel: 'display',
1045
+ budget: { total: 4000, currency: 'USD' },
1046
+ flight: {
1047
+ start: flightStart.toISOString(),
1048
+ end: flightEnd.toISOString(),
1049
+ },
1050
+ countries: ['US'],
1051
+ },
1052
+ }));
1053
+ if (checkResult?.success && checkResult?.data) {
1054
+ const data = checkResult.data;
1055
+ checkStep.details = `Initial check: status=${data.status}`;
1056
+ checkStep.response_preview = JSON.stringify({
1057
+ check_id: data.check_id,
1058
+ status: data.status,
1059
+ explanation: data.explanation,
1060
+ conditions: data.conditions,
1061
+ findings: data.findings?.map((f) => ({
1062
+ category_id: f.category_id,
1063
+ explanation: f.explanation,
1064
+ })),
1065
+ }, null, 2);
1066
+ // If we got conditions, apply them and re-check
1067
+ if (data.status === 'conditions' && data.conditions?.length > 0) {
1068
+ const conditions = data.conditions;
1069
+ const appliedConditions = conditions
1070
+ .filter((c) => c.required_value !== undefined)
1071
+ .map((c) => `${c.field}=${JSON.stringify(c.required_value)}`);
1072
+ checkStep.details += `. Conditions received: ${conditions.length} (${appliedConditions.length} machine-actionable)`;
1073
+ // Build adjusted payload by applying conditions
1074
+ const adjustedPayload = {
1075
+ buyer_ref: `e2e-conditions-adjusted-${Date.now()}`,
1076
+ channel: 'display',
1077
+ budget: { total: 4000, currency: 'USD' },
1078
+ flight: {
1079
+ start: flightStart.toISOString(),
1080
+ end: flightEnd.toISOString(),
1081
+ },
1082
+ countries: ['US'],
1083
+ };
1084
+ for (const condition of conditions) {
1085
+ if (condition.required_value !== undefined) {
1086
+ (0, GovernanceMiddleware_1.setAtPath)(adjustedPayload, condition.field, condition.required_value);
1087
+ }
1088
+ }
1089
+ // Re-check with adjusted parameters
1090
+ const { result: recheckResult, step: recheckStep } = await (0, client_1.runStep)('Re-check governance (after applying conditions)', 'check_governance', async () => client.executeTask('check_governance', {
1091
+ plan_id: testPlanId,
1092
+ buyer_campaign_ref: testCampaignRef,
1093
+ binding: 'proposed',
1094
+ caller: callerUrl,
1095
+ tool: 'create_media_buy',
1096
+ payload: adjustedPayload,
1097
+ }));
1098
+ if (recheckResult?.success && recheckResult?.data) {
1099
+ const recheckData = recheckResult.data;
1100
+ recheckStep.details = `Re-check after conditions: status=${recheckData.status}`;
1101
+ recheckStep.response_preview = JSON.stringify({
1102
+ check_id: recheckData.check_id,
1103
+ status: recheckData.status,
1104
+ explanation: recheckData.explanation,
1105
+ }, null, 2);
1106
+ }
1107
+ else if (recheckResult && !recheckResult.success) {
1108
+ recheckStep.passed = false;
1109
+ recheckStep.error = recheckResult.error || 'Re-check after conditions failed';
1110
+ }
1111
+ steps.push(recheckStep);
1112
+ }
1113
+ }
1114
+ else if (checkResult && !checkResult.success) {
1115
+ checkStep.passed = false;
1116
+ checkStep.error = checkResult.error || 'check_governance failed';
1117
+ }
1118
+ steps.push(checkStep);
1119
+ return { steps, profile };
1120
+ }
1121
+ /**
1122
+ * Test: Campaign Governance - Delivery Monitoring
1123
+ *
1124
+ * Tests delivery-phase check_governance with delivery_metrics, including
1125
+ * normal pacing and overspend drift detection.
1126
+ */
1127
+ async function testCampaignGovernanceDelivery(agentUrl, options) {
1128
+ const steps = [];
1129
+ const client = (0, client_1.createTestClient)(agentUrl, options.protocol || 'mcp', options);
1130
+ const { profile, step: profileStep } = await (0, client_1.discoverAgentProfile)(client);
1131
+ steps.push(profileStep);
1132
+ if (!profileStep.passed) {
1133
+ return { steps, profile };
1134
+ }
1135
+ if (!profile.tools.includes('check_governance')) {
1136
+ steps.push({
1137
+ step: 'Campaign governance delivery monitoring support check',
1138
+ passed: false,
1139
+ duration_ms: 0,
1140
+ error: 'Agent requires check_governance for delivery monitoring testing',
1141
+ });
1142
+ return { steps, profile };
1143
+ }
1144
+ profile.supports_governance = true;
1145
+ const testPlanId = `test-delivery-plan-${Date.now()}`;
1146
+ const testCampaignRef = `test-delivery-campaign-${Date.now()}`;
1147
+ const callerUrl = 'https://test-seller.example.com';
1148
+ const flightStart = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
1149
+ const flightEnd = new Date(Date.now() + 23 * 24 * 60 * 60 * 1000);
1150
+ // Sync plan if supported
1151
+ if (profile.tools.includes('sync_plans')) {
1152
+ const { result: syncResult, step: syncStep } = await (0, client_1.runStep)('Sync plan for delivery monitoring', 'sync_plans', async () => client.executeTask('sync_plans', {
1153
+ plans: [
1154
+ {
1155
+ plan_id: testPlanId,
1156
+ brand: options.brand || { domain: 'test.example.com' },
1157
+ objectives: 'E2E delivery monitoring test',
1158
+ budget: {
1159
+ total: 10000,
1160
+ currency: 'USD',
1161
+ authority_level: 'agent_full',
1162
+ },
1163
+ flight: {
1164
+ start: flightStart.toISOString(),
1165
+ end: flightEnd.toISOString(),
1166
+ },
1167
+ countries: ['US'],
1168
+ },
1169
+ ],
1170
+ }));
1171
+ steps.push(syncStep);
1172
+ if (!syncResult?.success) {
1173
+ return { steps, profile };
1174
+ }
1175
+ }
1176
+ // Delivery-phase committed check with metrics
1177
+ const reportingStart = new Date(Date.now() - 24 * 60 * 60 * 1000);
1178
+ const reportingEnd = new Date();
1179
+ const { result: deliveryResult, step: deliveryStep } = await (0, client_1.runStep)('Check governance (delivery phase with metrics)', 'check_governance', async () => client.executeTask('check_governance', {
1180
+ plan_id: testPlanId,
1181
+ buyer_campaign_ref: testCampaignRef,
1182
+ binding: 'committed',
1183
+ caller: callerUrl,
1184
+ media_buy_id: `test-mb-${Date.now()}`,
1185
+ phase: 'delivery',
1186
+ planned_delivery: {
1187
+ total_budget: 3000,
1188
+ currency: 'USD',
1189
+ channels: ['display'],
1190
+ geo: { countries: ['US'] },
1191
+ },
1192
+ delivery_metrics: {
1193
+ reporting_period: {
1194
+ start: reportingStart.toISOString(),
1195
+ end: reportingEnd.toISOString(),
1196
+ },
1197
+ spend: 450,
1198
+ cumulative_spend: 2800,
1199
+ impressions: 15000,
1200
+ cumulative_impressions: 85000,
1201
+ geo_distribution: { US: 100 },
1202
+ channel_distribution: { display: 100 },
1203
+ pacing: 'on_track',
1204
+ },
1205
+ }));
1206
+ if (deliveryResult?.success && deliveryResult?.data) {
1207
+ const data = deliveryResult.data;
1208
+ deliveryStep.details = `Delivery check: status=${data.status}, next_check=${data.next_check || 'not specified'}`;
1209
+ deliveryStep.response_preview = JSON.stringify({
1210
+ check_id: data.check_id,
1211
+ status: data.status,
1212
+ binding: data.binding,
1213
+ explanation: data.explanation,
1214
+ findings: data.findings?.map((f) => ({
1215
+ category_id: f.category_id,
1216
+ severity: f.severity,
1217
+ explanation: f.explanation,
1218
+ })),
1219
+ next_check: data.next_check,
1220
+ }, null, 2);
1221
+ }
1222
+ else if (deliveryResult && !deliveryResult.success) {
1223
+ deliveryStep.passed = false;
1224
+ deliveryStep.error = deliveryResult.error || 'Delivery-phase check_governance failed';
1225
+ }
1226
+ steps.push(deliveryStep);
1227
+ // Delivery-phase check with drift (overspend)
1228
+ const { result: driftResult, step: driftStep } = await (0, client_1.runStep)('Check governance (delivery phase with overspend drift)', 'check_governance', async () => client.executeTask('check_governance', {
1229
+ plan_id: testPlanId,
1230
+ buyer_campaign_ref: testCampaignRef,
1231
+ binding: 'committed',
1232
+ caller: callerUrl,
1233
+ media_buy_id: `test-mb-${Date.now()}`,
1234
+ phase: 'delivery',
1235
+ planned_delivery: {
1236
+ total_budget: 3000,
1237
+ currency: 'USD',
1238
+ channels: ['display'],
1239
+ geo: { countries: ['US'] },
1240
+ },
1241
+ delivery_metrics: {
1242
+ reporting_period: {
1243
+ start: reportingStart.toISOString(),
1244
+ end: reportingEnd.toISOString(),
1245
+ },
1246
+ spend: 2000,
1247
+ cumulative_spend: 9500,
1248
+ impressions: 5000,
1249
+ cumulative_impressions: 90000,
1250
+ pacing: 'ahead',
1251
+ },
1252
+ }));
1253
+ if (driftResult?.success && driftResult?.data) {
1254
+ const data = driftResult.data;
1255
+ driftStep.details = `Overspend drift check: status=${data.status}`;
1256
+ driftStep.response_preview = JSON.stringify({
1257
+ status: data.status,
1258
+ explanation: data.explanation,
1259
+ findings: data.findings?.map((f) => ({
1260
+ category_id: f.category_id,
1261
+ severity: f.severity,
1262
+ explanation: f.explanation,
1263
+ })),
1264
+ }, null, 2);
1265
+ if (data.status === 'approved' && !data.findings?.length) {
1266
+ driftStep.warnings = ['Governance approved delivery at 95% budget with no findings — verify drift detection'];
1267
+ }
1268
+ }
1269
+ else if (driftResult && !driftResult.success) {
1270
+ driftStep.passed = false;
1271
+ driftStep.error = driftResult.error || 'Drift detection check_governance failed';
1272
+ }
1273
+ steps.push(driftStep);
1274
+ return { steps, profile };
1275
+ }
596
1276
  /**
597
1277
  * Check if agent has any governance protocol tools
598
1278
  */
599
1279
  function hasGovernanceTools(tools) {
600
1280
  return capabilities_1.GOVERNANCE_TOOLS.some(t => tools.includes(t));
601
1281
  }
1282
+ /**
1283
+ * Check if agent has campaign governance tools
1284
+ */
1285
+ function hasCampaignGovernanceTools(tools) {
1286
+ return CAMPAIGN_GOVERNANCE_TOOLS.some(t => tools.includes(t));
1287
+ }
602
1288
  //# sourceMappingURL=governance.js.map