@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.
- package/bin/adcp.js +346 -0
- package/dist/lib/adapters/governance-adapter.d.ts +88 -0
- package/dist/lib/adapters/governance-adapter.d.ts.map +1 -0
- package/dist/lib/adapters/governance-adapter.js +96 -0
- package/dist/lib/adapters/governance-adapter.js.map +1 -0
- package/dist/lib/adapters/index.d.ts +1 -0
- package/dist/lib/adapters/index.d.ts.map +1 -1
- package/dist/lib/adapters/index.js +7 -1
- package/dist/lib/adapters/index.js.map +1 -1
- package/dist/lib/agents/index.generated.d.ts +33 -1
- package/dist/lib/agents/index.generated.d.ts.map +1 -1
- package/dist/lib/agents/index.generated.js +48 -0
- package/dist/lib/agents/index.generated.js.map +1 -1
- package/dist/lib/core/AgentClient.d.ts +92 -1
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/AgentClient.js +237 -0
- package/dist/lib/core/AgentClient.js.map +1 -1
- package/dist/lib/core/AsyncHandler.d.ts +19 -2
- package/dist/lib/core/AsyncHandler.d.ts.map +1 -1
- package/dist/lib/core/AsyncHandler.js.map +1 -1
- package/dist/lib/core/ConversationTypes.d.ts +8 -2
- package/dist/lib/core/ConversationTypes.d.ts.map +1 -1
- package/dist/lib/core/GovernanceMiddleware.d.ts +86 -0
- package/dist/lib/core/GovernanceMiddleware.d.ts.map +1 -0
- package/dist/lib/core/GovernanceMiddleware.js +289 -0
- package/dist/lib/core/GovernanceMiddleware.js.map +1 -0
- package/dist/lib/core/GovernanceTypes.d.ts +118 -0
- package/dist/lib/core/GovernanceTypes.d.ts.map +1 -0
- package/dist/lib/core/GovernanceTypes.js +69 -0
- package/dist/lib/core/GovernanceTypes.js.map +1 -0
- package/dist/lib/core/SingleAgentClient.d.ts +103 -1
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +151 -0
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/core/TaskExecutor.d.ts +10 -0
- package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
- package/dist/lib/core/TaskExecutor.js +95 -10
- package/dist/lib/core/TaskExecutor.js.map +1 -1
- package/dist/lib/index.d.ts +6 -2
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +22 -4
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/observability/index.d.ts +8 -0
- package/dist/lib/observability/index.d.ts.map +1 -0
- package/dist/lib/observability/index.js +17 -0
- package/dist/lib/observability/index.js.map +1 -0
- package/dist/lib/observability/tracing.d.ts +42 -0
- package/dist/lib/observability/tracing.d.ts.map +1 -0
- package/dist/lib/observability/tracing.js +126 -0
- package/dist/lib/observability/tracing.js.map +1 -0
- package/dist/lib/protocols/a2a.d.ts.map +1 -1
- package/dist/lib/protocols/a2a.js +16 -1
- package/dist/lib/protocols/a2a.js.map +1 -1
- package/dist/lib/protocols/index.d.ts.map +1 -1
- package/dist/lib/protocols/index.js +37 -29
- package/dist/lib/protocols/index.js.map +1 -1
- package/dist/lib/protocols/mcp.d.ts.map +1 -1
- package/dist/lib/protocols/mcp.js +21 -1
- package/dist/lib/protocols/mcp.js.map +1 -1
- package/dist/lib/registry/types.generated.d.ts +754 -5
- package/dist/lib/registry/types.generated.d.ts.map +1 -1
- package/dist/lib/registry/types.generated.js +1 -1
- package/dist/lib/testing/agent-tester.d.ts +1 -1
- package/dist/lib/testing/agent-tester.d.ts.map +1 -1
- package/dist/lib/testing/agent-tester.js +36 -11
- package/dist/lib/testing/agent-tester.js.map +1 -1
- package/dist/lib/testing/client.d.ts +6 -1
- package/dist/lib/testing/client.d.ts.map +1 -1
- package/dist/lib/testing/client.js +36 -19
- package/dist/lib/testing/client.js.map +1 -1
- package/dist/lib/testing/compliance/briefs.d.ts +12 -0
- package/dist/lib/testing/compliance/briefs.d.ts.map +1 -0
- package/dist/lib/testing/compliance/briefs.js +157 -0
- package/dist/lib/testing/compliance/briefs.js.map +1 -0
- package/dist/lib/testing/compliance/comply.d.ts +26 -0
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -0
- package/dist/lib/testing/compliance/comply.js +540 -0
- package/dist/lib/testing/compliance/comply.js.map +1 -0
- package/dist/lib/testing/compliance/convince.d.ts +27 -0
- package/dist/lib/testing/compliance/convince.d.ts.map +1 -0
- package/dist/lib/testing/compliance/convince.js +418 -0
- package/dist/lib/testing/compliance/convince.js.map +1 -0
- package/dist/lib/testing/compliance/index.d.ts +13 -0
- package/dist/lib/testing/compliance/index.d.ts.map +1 -0
- package/dist/lib/testing/compliance/index.js +22 -0
- package/dist/lib/testing/compliance/index.js.map +1 -0
- package/dist/lib/testing/compliance/types.d.ts +123 -0
- package/dist/lib/testing/compliance/types.d.ts.map +1 -0
- package/dist/lib/testing/compliance/types.js +9 -0
- package/dist/lib/testing/compliance/types.js.map +1 -0
- package/dist/lib/testing/index.d.ts +1 -0
- package/dist/lib/testing/index.d.ts.map +1 -1
- package/dist/lib/testing/index.js +15 -1
- package/dist/lib/testing/index.js.map +1 -1
- package/dist/lib/testing/orchestrator.d.ts +0 -2
- package/dist/lib/testing/orchestrator.d.ts.map +1 -1
- package/dist/lib/testing/orchestrator.js +14 -3
- package/dist/lib/testing/orchestrator.js.map +1 -1
- package/dist/lib/testing/scenarios/capabilities.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/capabilities.js +70 -9
- package/dist/lib/testing/scenarios/capabilities.js.map +1 -1
- package/dist/lib/testing/scenarios/creative.d.ts +14 -0
- package/dist/lib/testing/scenarios/creative.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/creative.js +261 -55
- package/dist/lib/testing/scenarios/creative.js.map +1 -1
- package/dist/lib/testing/scenarios/discovery.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/discovery.js +7 -5
- package/dist/lib/testing/scenarios/discovery.js.map +1 -1
- package/dist/lib/testing/scenarios/edge-cases.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/edge-cases.js +90 -112
- package/dist/lib/testing/scenarios/edge-cases.js.map +1 -1
- package/dist/lib/testing/scenarios/governance.d.ts +48 -0
- package/dist/lib/testing/scenarios/governance.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/governance.js +725 -39
- package/dist/lib/testing/scenarios/governance.js.map +1 -1
- package/dist/lib/testing/scenarios/index.d.ts +3 -3
- package/dist/lib/testing/scenarios/index.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/index.js +8 -1
- package/dist/lib/testing/scenarios/index.js.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.d.ts +14 -5
- package/dist/lib/testing/scenarios/media-buy.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.js +358 -63
- package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.js +26 -22
- package/dist/lib/testing/scenarios/schema-compliance.js.map +1 -1
- package/dist/lib/testing/scenarios/signals.d.ts +4 -8
- package/dist/lib/testing/scenarios/signals.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/signals.js +17 -59
- package/dist/lib/testing/scenarios/signals.js.map +1 -1
- package/dist/lib/testing/scenarios/sponsored-intelligence.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/sponsored-intelligence.js +23 -19
- package/dist/lib/testing/scenarios/sponsored-intelligence.js.map +1 -1
- package/dist/lib/testing/types.d.ts +3 -2
- package/dist/lib/testing/types.d.ts.map +1 -1
- package/dist/lib/types/core.generated.d.ts +629 -5
- package/dist/lib/types/core.generated.d.ts.map +1 -1
- package/dist/lib/types/core.generated.js +1 -1
- package/dist/lib/types/schemas.generated.d.ts +13059 -11101
- package/dist/lib/types/schemas.generated.d.ts.map +1 -1
- package/dist/lib/types/schemas.generated.js +573 -107
- package/dist/lib/types/schemas.generated.js.map +1 -1
- package/dist/lib/types/tools.generated.d.ts +1827 -360
- package/dist/lib/types/tools.generated.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.d.ts +17 -2
- package/dist/lib/utils/capabilities.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.js +26 -2
- package/dist/lib/utils/capabilities.js.map +1 -1
- 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.
|
|
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
|
|
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.
|
|
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
|
-
|
|
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:
|
|
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.
|
|
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.
|
|
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:
|
|
178
|
-
returned_count:
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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:
|
|
313
|
-
name:
|
|
314
|
-
features_count:
|
|
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.
|
|
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
|
|
351
|
-
evaluated_artifacts: data.evaluated_artifacts
|
|
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.
|
|
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
|
-
|
|
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:
|
|
389
|
-
passed_records:
|
|
390
|
-
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.
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
543
|
-
|
|
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
|
-
|
|
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.
|
|
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
|