@adcp/client 4.7.2 → 4.9.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 +8 -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 +49 -17
- package/dist/lib/agents/index.generated.d.ts.map +1 -1
- package/dist/lib/agents/index.generated.js +72 -24
- package/dist/lib/agents/index.generated.js.map +1 -1
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/AsyncHandler.d.ts +1 -1
- 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/CreativeAgentClient.d.ts +17 -1
- package/dist/lib/core/CreativeAgentClient.d.ts.map +1 -1
- package/dist/lib/core/CreativeAgentClient.js +21 -0
- package/dist/lib/core/CreativeAgentClient.js.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 +35 -1
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +45 -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 +7 -3
- 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 +750 -1
- 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 +37 -11
- package/dist/lib/testing/agent-tester.js.map +1 -1
- package/dist/lib/testing/formatter.d.ts.map +1 -1
- package/dist/lib/testing/formatter.js +6 -5
- package/dist/lib/testing/formatter.js.map +1 -1
- package/dist/lib/testing/index.d.ts +1 -1
- package/dist/lib/testing/index.d.ts.map +1 -1
- package/dist/lib/testing/index.js +3 -2
- 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 +24 -6
- 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 +111 -40
- 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 +241 -42
- package/dist/lib/testing/scenarios/creative.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 +675 -0
- 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 +9 -1
- package/dist/lib/testing/scenarios/index.js.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.d.ts +21 -1
- package/dist/lib/testing/scenarios/media-buy.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.js +282 -49
- package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
- package/dist/lib/testing/types.d.ts +2 -2
- package/dist/lib/testing/types.d.ts.map +1 -1
- package/dist/lib/types/core.generated.d.ts +747 -8
- 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 +25143 -19145
- package/dist/lib/types/schemas.generated.d.ts.map +1 -1
- package/dist/lib/types/schemas.generated.js +795 -282
- package/dist/lib/types/schemas.generated.js.map +1 -1
- package/dist/lib/types/tools.generated.d.ts +3529 -1964
- 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 +35 -6
- package/dist/lib/utils/capabilities.js.map +1 -1
- package/package.json +14 -21
|
@@ -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',
|
|
@@ -593,10 +599,679 @@ async function testPropertyListFilters(agentUrl, options) {
|
|
|
593
599
|
}
|
|
594
600
|
return { steps, profile };
|
|
595
601
|
}
|
|
602
|
+
// Campaign governance tools
|
|
603
|
+
const CAMPAIGN_GOVERNANCE_TOOLS = [
|
|
604
|
+
'sync_plans',
|
|
605
|
+
'check_governance',
|
|
606
|
+
'report_plan_outcome',
|
|
607
|
+
'get_plan_audit_logs',
|
|
608
|
+
];
|
|
609
|
+
/**
|
|
610
|
+
* Test: Campaign Governance - Full Lifecycle
|
|
611
|
+
*
|
|
612
|
+
* Flow: sync_plans -> check_governance(proposed, approved) -> create_media_buy
|
|
613
|
+
* -> report_plan_outcome(completed)
|
|
614
|
+
*
|
|
615
|
+
* Tests the happy path: buyer syncs a plan, gets approval, executes,
|
|
616
|
+
* and reports the outcome back to the governance agent.
|
|
617
|
+
*/
|
|
618
|
+
async function testCampaignGovernance(agentUrl, options) {
|
|
619
|
+
const steps = [];
|
|
620
|
+
const client = (0, client_1.createTestClient)(agentUrl, options.protocol || 'mcp', options);
|
|
621
|
+
// Discover agent profile
|
|
622
|
+
const { profile, step: profileStep } = await (0, client_1.discoverAgentProfile)(client);
|
|
623
|
+
steps.push(profileStep);
|
|
624
|
+
if (!profileStep.passed) {
|
|
625
|
+
return { steps, profile };
|
|
626
|
+
}
|
|
627
|
+
// Check if agent supports campaign governance tools
|
|
628
|
+
const hasCampaignGovernance = CAMPAIGN_GOVERNANCE_TOOLS.some(t => profile.tools.includes(t));
|
|
629
|
+
if (!hasCampaignGovernance) {
|
|
630
|
+
steps.push({
|
|
631
|
+
step: 'Campaign governance support check',
|
|
632
|
+
passed: false,
|
|
633
|
+
duration_ms: 0,
|
|
634
|
+
error: 'Agent does not support campaign governance tools',
|
|
635
|
+
details: `Required: at least one of ${CAMPAIGN_GOVERNANCE_TOOLS.join(', ')}. Available: ${profile.tools.join(', ')}`,
|
|
636
|
+
});
|
|
637
|
+
return { steps, profile };
|
|
638
|
+
}
|
|
639
|
+
profile.supports_governance = true;
|
|
640
|
+
const testPlanId = `test-plan-${Date.now()}`;
|
|
641
|
+
const testCampaignRef = `test-campaign-${Date.now()}`;
|
|
642
|
+
const callerUrl = 'https://test-orchestrator.example.com';
|
|
643
|
+
const flightStart = new Date();
|
|
644
|
+
const flightEnd = new Date(flightStart.getTime() + 30 * 24 * 60 * 60 * 1000); // 30 days
|
|
645
|
+
// Step 1: sync_plans
|
|
646
|
+
if (profile.tools.includes('sync_plans')) {
|
|
647
|
+
const { result, step } = await (0, client_1.runStep)('Sync campaign governance plan', 'sync_plans', async () => client.executeTask('sync_plans', {
|
|
648
|
+
plans: [
|
|
649
|
+
{
|
|
650
|
+
plan_id: testPlanId,
|
|
651
|
+
brand: options.brand || { domain: 'test.example.com' },
|
|
652
|
+
objectives: 'E2E test campaign for governance protocol validation',
|
|
653
|
+
budget: {
|
|
654
|
+
total: options.budget || 10000,
|
|
655
|
+
currency: 'USD',
|
|
656
|
+
authority_level: 'agent_full',
|
|
657
|
+
},
|
|
658
|
+
flight: {
|
|
659
|
+
start: flightStart.toISOString(),
|
|
660
|
+
end: flightEnd.toISOString(),
|
|
661
|
+
},
|
|
662
|
+
countries: ['US'],
|
|
663
|
+
channels: {
|
|
664
|
+
allowed: ['display', 'video'],
|
|
665
|
+
},
|
|
666
|
+
delegations: [
|
|
667
|
+
{
|
|
668
|
+
agent_url: callerUrl,
|
|
669
|
+
authority: 'full',
|
|
670
|
+
},
|
|
671
|
+
],
|
|
672
|
+
},
|
|
673
|
+
],
|
|
674
|
+
}));
|
|
675
|
+
if (result?.success && result?.data) {
|
|
676
|
+
const data = result.data;
|
|
677
|
+
const plans = data.plans || [];
|
|
678
|
+
const synced = plans.find((p) => p.plan_id === testPlanId);
|
|
679
|
+
if (synced?.status === 'active') {
|
|
680
|
+
step.details = `Plan synced: ${testPlanId}, version ${synced.version}, ${(synced.categories || []).length} categories active`;
|
|
681
|
+
step.response_preview = JSON.stringify({
|
|
682
|
+
plan_id: synced.plan_id,
|
|
683
|
+
status: synced.status,
|
|
684
|
+
version: synced.version,
|
|
685
|
+
categories: synced.categories?.map((c) => c.category_id),
|
|
686
|
+
resolved_policies: synced.resolved_policies?.length || 0,
|
|
687
|
+
}, null, 2);
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
step.passed = false;
|
|
691
|
+
step.error = synced
|
|
692
|
+
? `Plan sync returned status '${synced.status}' instead of 'active'`
|
|
693
|
+
: 'Plan not found in sync response';
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
else if (result && !result.success) {
|
|
697
|
+
step.passed = false;
|
|
698
|
+
step.error = result.error || 'sync_plans failed';
|
|
699
|
+
}
|
|
700
|
+
steps.push(step);
|
|
701
|
+
}
|
|
702
|
+
// Step 2: check_governance (proposed, expecting approved)
|
|
703
|
+
let checkId;
|
|
704
|
+
if (profile.tools.includes('check_governance')) {
|
|
705
|
+
const { result, step } = await (0, client_1.runStep)('Check governance (proposed buy)', 'check_governance', async () => client.executeTask('check_governance', {
|
|
706
|
+
plan_id: testPlanId,
|
|
707
|
+
buyer_campaign_ref: testCampaignRef,
|
|
708
|
+
binding: 'proposed',
|
|
709
|
+
caller: callerUrl,
|
|
710
|
+
tool: 'create_media_buy',
|
|
711
|
+
payload: {
|
|
712
|
+
buyer_ref: `e2e-test-${Date.now()}`,
|
|
713
|
+
channel: 'display',
|
|
714
|
+
budget: { total: 1000, currency: 'USD' },
|
|
715
|
+
flight: {
|
|
716
|
+
start: flightStart.toISOString(),
|
|
717
|
+
end: flightEnd.toISOString(),
|
|
718
|
+
},
|
|
719
|
+
countries: ['US'],
|
|
720
|
+
},
|
|
721
|
+
}));
|
|
722
|
+
if (result?.success && result?.data) {
|
|
723
|
+
const data = result.data;
|
|
724
|
+
checkId = data.check_id;
|
|
725
|
+
const status = data.status;
|
|
726
|
+
step.details = `Governance check: status=${status}, binding=${data.binding}, mode=${data.mode || 'unknown'}`;
|
|
727
|
+
step.response_preview = JSON.stringify({
|
|
728
|
+
check_id: data.check_id,
|
|
729
|
+
status: data.status,
|
|
730
|
+
binding: data.binding,
|
|
731
|
+
mode: data.mode,
|
|
732
|
+
explanation: data.explanation,
|
|
733
|
+
findings_count: data.findings?.length || 0,
|
|
734
|
+
expires_at: data.expires_at,
|
|
735
|
+
}, null, 2);
|
|
736
|
+
// Any status is valid — we're testing the protocol, not the policy
|
|
737
|
+
if (!['approved', 'denied', 'conditions', 'escalated'].includes(status)) {
|
|
738
|
+
step.passed = false;
|
|
739
|
+
step.error = `Unexpected governance status: ${status}`;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
else if (result && !result.success) {
|
|
743
|
+
step.passed = false;
|
|
744
|
+
step.error = result.error || 'check_governance failed';
|
|
745
|
+
}
|
|
746
|
+
steps.push(step);
|
|
747
|
+
}
|
|
748
|
+
// Step 3: report_plan_outcome (completed)
|
|
749
|
+
if (profile.tools.includes('report_plan_outcome') && checkId) {
|
|
750
|
+
const { result, step } = await (0, client_1.runStep)('Report plan outcome (completed)', 'report_plan_outcome', async () => client.executeTask('report_plan_outcome', {
|
|
751
|
+
plan_id: testPlanId,
|
|
752
|
+
check_id: checkId,
|
|
753
|
+
buyer_campaign_ref: testCampaignRef,
|
|
754
|
+
outcome: 'completed',
|
|
755
|
+
seller_response: {
|
|
756
|
+
media_buy_id: `test-mb-${Date.now()}`,
|
|
757
|
+
buyer_ref: `e2e-test-${Date.now()}`,
|
|
758
|
+
packages: [
|
|
759
|
+
{
|
|
760
|
+
package_id: 'test-pkg-1',
|
|
761
|
+
name: 'E2E Test Package',
|
|
762
|
+
budget: { total: 1000, currency: 'USD' },
|
|
763
|
+
},
|
|
764
|
+
],
|
|
765
|
+
},
|
|
766
|
+
}));
|
|
767
|
+
if (result?.success && result?.data) {
|
|
768
|
+
step.details = 'Outcome reported to governance agent';
|
|
769
|
+
step.response_preview = JSON.stringify(result.data, null, 2);
|
|
770
|
+
}
|
|
771
|
+
else if (result && !result.success) {
|
|
772
|
+
step.passed = false;
|
|
773
|
+
step.error = result.error || 'report_plan_outcome failed';
|
|
774
|
+
}
|
|
775
|
+
steps.push(step);
|
|
776
|
+
}
|
|
777
|
+
// Step 4: get_plan_audit_logs
|
|
778
|
+
if (profile.tools.includes('get_plan_audit_logs')) {
|
|
779
|
+
const { result, step } = await (0, client_1.runStep)('Get plan audit logs', 'get_plan_audit_logs', async () => client.executeTask('get_plan_audit_logs', {
|
|
780
|
+
plan_ids: [testPlanId],
|
|
781
|
+
buyer_campaign_ref: testCampaignRef,
|
|
782
|
+
include_entries: true,
|
|
783
|
+
}));
|
|
784
|
+
if (result?.success && result?.data) {
|
|
785
|
+
const data = result.data;
|
|
786
|
+
step.details = 'Audit logs retrieved';
|
|
787
|
+
step.response_preview = JSON.stringify({
|
|
788
|
+
plans_returned: data.plans?.length || 0,
|
|
789
|
+
has_entries: !!data.plans?.[0]?.entries?.length,
|
|
790
|
+
budget: data.plans?.[0]?.budget,
|
|
791
|
+
}, null, 2);
|
|
792
|
+
}
|
|
793
|
+
else if (result && !result.success) {
|
|
794
|
+
step.passed = false;
|
|
795
|
+
step.error = result.error || 'get_plan_audit_logs failed';
|
|
796
|
+
}
|
|
797
|
+
steps.push(step);
|
|
798
|
+
}
|
|
799
|
+
return { steps, profile };
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Test: Campaign Governance - Denied Flow
|
|
803
|
+
*
|
|
804
|
+
* Sends a check_governance request that should be denied (budget exceeds plan,
|
|
805
|
+
* unauthorized market). Validates that the governance agent returns meaningful
|
|
806
|
+
* findings and explanations.
|
|
807
|
+
*/
|
|
808
|
+
async function testCampaignGovernanceDenied(agentUrl, options) {
|
|
809
|
+
const steps = [];
|
|
810
|
+
const client = (0, client_1.createTestClient)(agentUrl, options.protocol || 'mcp', options);
|
|
811
|
+
const { profile, step: profileStep } = await (0, client_1.discoverAgentProfile)(client);
|
|
812
|
+
steps.push(profileStep);
|
|
813
|
+
if (!profileStep.passed) {
|
|
814
|
+
return { steps, profile };
|
|
815
|
+
}
|
|
816
|
+
if (!profile.tools.includes('sync_plans') || !profile.tools.includes('check_governance')) {
|
|
817
|
+
steps.push({
|
|
818
|
+
step: 'Campaign governance denied flow support check',
|
|
819
|
+
passed: false,
|
|
820
|
+
duration_ms: 0,
|
|
821
|
+
error: 'Agent requires sync_plans + check_governance for denied flow testing',
|
|
822
|
+
});
|
|
823
|
+
return { steps, profile };
|
|
824
|
+
}
|
|
825
|
+
profile.supports_governance = true;
|
|
826
|
+
const testPlanId = `test-denied-plan-${Date.now()}`;
|
|
827
|
+
const testCampaignRef = `test-denied-campaign-${Date.now()}`;
|
|
828
|
+
const callerUrl = 'https://test-orchestrator.example.com';
|
|
829
|
+
const flightStart = new Date();
|
|
830
|
+
const flightEnd = new Date(flightStart.getTime() + 7 * 24 * 60 * 60 * 1000);
|
|
831
|
+
// Sync a restrictive plan: small budget, US only
|
|
832
|
+
const { result: syncResult, step: syncStep } = await (0, client_1.runStep)('Sync restrictive plan (small budget, US only)', 'sync_plans', async () => client.executeTask('sync_plans', {
|
|
833
|
+
plans: [
|
|
834
|
+
{
|
|
835
|
+
plan_id: testPlanId,
|
|
836
|
+
brand: options.brand || { domain: 'test.example.com' },
|
|
837
|
+
objectives: 'E2E denial test: budget and geo restrictions',
|
|
838
|
+
budget: {
|
|
839
|
+
total: 500,
|
|
840
|
+
currency: 'USD',
|
|
841
|
+
authority_level: 'agent_limited',
|
|
842
|
+
reallocation_threshold: 100,
|
|
843
|
+
},
|
|
844
|
+
flight: {
|
|
845
|
+
start: flightStart.toISOString(),
|
|
846
|
+
end: flightEnd.toISOString(),
|
|
847
|
+
},
|
|
848
|
+
countries: ['US'],
|
|
849
|
+
channels: {
|
|
850
|
+
allowed: ['display'],
|
|
851
|
+
},
|
|
852
|
+
},
|
|
853
|
+
],
|
|
854
|
+
}));
|
|
855
|
+
steps.push(syncStep);
|
|
856
|
+
if (!syncResult?.success) {
|
|
857
|
+
return { steps, profile };
|
|
858
|
+
}
|
|
859
|
+
// Check governance with over-budget request
|
|
860
|
+
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', {
|
|
861
|
+
plan_id: testPlanId,
|
|
862
|
+
buyer_campaign_ref: testCampaignRef,
|
|
863
|
+
binding: 'proposed',
|
|
864
|
+
caller: callerUrl,
|
|
865
|
+
tool: 'create_media_buy',
|
|
866
|
+
payload: {
|
|
867
|
+
buyer_ref: `e2e-overbudget-${Date.now()}`,
|
|
868
|
+
channel: 'display',
|
|
869
|
+
budget: { total: 50000, currency: 'USD' },
|
|
870
|
+
flight: {
|
|
871
|
+
start: flightStart.toISOString(),
|
|
872
|
+
end: flightEnd.toISOString(),
|
|
873
|
+
},
|
|
874
|
+
countries: ['US'],
|
|
875
|
+
},
|
|
876
|
+
}));
|
|
877
|
+
if (overBudgetResult?.success && overBudgetResult?.data) {
|
|
878
|
+
const data = overBudgetResult.data;
|
|
879
|
+
overBudgetStep.details = `Over-budget check: status=${data.status}, explanation: ${data.explanation}`;
|
|
880
|
+
overBudgetStep.response_preview = JSON.stringify({
|
|
881
|
+
status: data.status,
|
|
882
|
+
explanation: data.explanation,
|
|
883
|
+
findings: data.findings?.map((f) => ({
|
|
884
|
+
category_id: f.category_id,
|
|
885
|
+
severity: f.severity,
|
|
886
|
+
explanation: f.explanation,
|
|
887
|
+
})),
|
|
888
|
+
conditions: data.conditions,
|
|
889
|
+
}, null, 2);
|
|
890
|
+
if (data.status === 'approved' && data.mode !== 'advisory' && data.mode !== 'audit') {
|
|
891
|
+
overBudgetStep.passed = false;
|
|
892
|
+
overBudgetStep.error =
|
|
893
|
+
'Governance approved a $50,000 buy against a $500 plan in enforce mode — expected denial or conditions';
|
|
894
|
+
}
|
|
895
|
+
else if (data.status === 'approved') {
|
|
896
|
+
overBudgetStep.warnings = [
|
|
897
|
+
'Governance approved a $50,000 buy against a $500 plan — advisory/audit mode detected',
|
|
898
|
+
];
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
else if (overBudgetResult && !overBudgetResult.success) {
|
|
902
|
+
overBudgetStep.passed = false;
|
|
903
|
+
overBudgetStep.error = overBudgetResult.error || 'check_governance failed';
|
|
904
|
+
}
|
|
905
|
+
steps.push(overBudgetStep);
|
|
906
|
+
// Check governance with unauthorized market
|
|
907
|
+
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', {
|
|
908
|
+
plan_id: testPlanId,
|
|
909
|
+
buyer_campaign_ref: testCampaignRef,
|
|
910
|
+
binding: 'proposed',
|
|
911
|
+
caller: callerUrl,
|
|
912
|
+
tool: 'create_media_buy',
|
|
913
|
+
payload: {
|
|
914
|
+
buyer_ref: `e2e-badgeo-${Date.now()}`,
|
|
915
|
+
channel: 'display',
|
|
916
|
+
budget: { total: 100, currency: 'USD' },
|
|
917
|
+
flight: {
|
|
918
|
+
start: flightStart.toISOString(),
|
|
919
|
+
end: flightEnd.toISOString(),
|
|
920
|
+
},
|
|
921
|
+
countries: ['CN', 'RU'],
|
|
922
|
+
},
|
|
923
|
+
}));
|
|
924
|
+
if (geoResult?.success && geoResult?.data) {
|
|
925
|
+
const data = geoResult.data;
|
|
926
|
+
geoStep.details = `Unauthorized market check: status=${data.status}`;
|
|
927
|
+
geoStep.response_preview = JSON.stringify({
|
|
928
|
+
status: data.status,
|
|
929
|
+
explanation: data.explanation,
|
|
930
|
+
findings: data.findings?.map((f) => ({
|
|
931
|
+
category_id: f.category_id,
|
|
932
|
+
explanation: f.explanation,
|
|
933
|
+
})),
|
|
934
|
+
}, null, 2);
|
|
935
|
+
if (data.status === 'approved') {
|
|
936
|
+
geoStep.warnings = [
|
|
937
|
+
'Governance approved targeting CN/RU against US-only plan — may indicate audit/advisory mode',
|
|
938
|
+
];
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
else if (geoResult && !geoResult.success) {
|
|
942
|
+
geoStep.passed = false;
|
|
943
|
+
geoStep.error = geoResult.error || 'check_governance failed';
|
|
944
|
+
}
|
|
945
|
+
steps.push(geoStep);
|
|
946
|
+
// Report failed outcome
|
|
947
|
+
if (profile.tools.includes('report_plan_outcome') && overBudgetResult?.data?.check_id) {
|
|
948
|
+
const { step: outcomeStep } = await (0, client_1.runStep)('Report failed outcome for denied check', 'report_plan_outcome', async () => client.executeTask('report_plan_outcome', {
|
|
949
|
+
plan_id: testPlanId,
|
|
950
|
+
check_id: overBudgetResult.data.check_id,
|
|
951
|
+
buyer_campaign_ref: testCampaignRef,
|
|
952
|
+
outcome: 'failed',
|
|
953
|
+
error: {
|
|
954
|
+
code: 'governance_denied',
|
|
955
|
+
message: 'Action blocked by governance check',
|
|
956
|
+
},
|
|
957
|
+
}));
|
|
958
|
+
steps.push(outcomeStep);
|
|
959
|
+
}
|
|
960
|
+
return { steps, profile };
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Test: Campaign Governance - Conditions Flow
|
|
964
|
+
*
|
|
965
|
+
* Syncs a plan, sends a check that may trigger conditions (e.g., budget
|
|
966
|
+
* concentration limit), applies machine-actionable conditions, and re-checks.
|
|
967
|
+
*/
|
|
968
|
+
async function testCampaignGovernanceConditions(agentUrl, options) {
|
|
969
|
+
const steps = [];
|
|
970
|
+
const client = (0, client_1.createTestClient)(agentUrl, options.protocol || 'mcp', options);
|
|
971
|
+
const { profile, step: profileStep } = await (0, client_1.discoverAgentProfile)(client);
|
|
972
|
+
steps.push(profileStep);
|
|
973
|
+
if (!profileStep.passed) {
|
|
974
|
+
return { steps, profile };
|
|
975
|
+
}
|
|
976
|
+
if (!profile.tools.includes('sync_plans') || !profile.tools.includes('check_governance')) {
|
|
977
|
+
steps.push({
|
|
978
|
+
step: 'Campaign governance conditions flow support check',
|
|
979
|
+
passed: false,
|
|
980
|
+
duration_ms: 0,
|
|
981
|
+
error: 'Agent requires sync_plans + check_governance for conditions flow testing',
|
|
982
|
+
});
|
|
983
|
+
return { steps, profile };
|
|
984
|
+
}
|
|
985
|
+
profile.supports_governance = true;
|
|
986
|
+
const testPlanId = `test-conditions-plan-${Date.now()}`;
|
|
987
|
+
const testCampaignRef = `test-conditions-campaign-${Date.now()}`;
|
|
988
|
+
const callerUrl = 'https://test-orchestrator.example.com';
|
|
989
|
+
const flightStart = new Date();
|
|
990
|
+
const flightEnd = new Date(flightStart.getTime() + 14 * 24 * 60 * 60 * 1000);
|
|
991
|
+
// Sync a plan with per_seller_max_pct constraint
|
|
992
|
+
const { result: syncResult, step: syncStep } = await (0, client_1.runStep)('Sync plan with seller concentration limit', 'sync_plans', async () => client.executeTask('sync_plans', {
|
|
993
|
+
plans: [
|
|
994
|
+
{
|
|
995
|
+
plan_id: testPlanId,
|
|
996
|
+
brand: options.brand || { domain: 'test.example.com' },
|
|
997
|
+
objectives: 'E2E conditions test: budget cap per seller',
|
|
998
|
+
budget: {
|
|
999
|
+
total: 5000,
|
|
1000
|
+
currency: 'USD',
|
|
1001
|
+
authority_level: 'agent_limited',
|
|
1002
|
+
per_seller_max_pct: 50,
|
|
1003
|
+
reallocation_threshold: 500,
|
|
1004
|
+
},
|
|
1005
|
+
flight: {
|
|
1006
|
+
start: flightStart.toISOString(),
|
|
1007
|
+
end: flightEnd.toISOString(),
|
|
1008
|
+
},
|
|
1009
|
+
countries: ['US'],
|
|
1010
|
+
channels: {
|
|
1011
|
+
allowed: ['display', 'video'],
|
|
1012
|
+
mix_targets: {
|
|
1013
|
+
display: { min_pct: 30, max_pct: 70 },
|
|
1014
|
+
video: { min_pct: 30, max_pct: 70 },
|
|
1015
|
+
},
|
|
1016
|
+
},
|
|
1017
|
+
},
|
|
1018
|
+
],
|
|
1019
|
+
}));
|
|
1020
|
+
steps.push(syncStep);
|
|
1021
|
+
if (!syncResult?.success) {
|
|
1022
|
+
return { steps, profile };
|
|
1023
|
+
}
|
|
1024
|
+
// First check: send budget that might trigger conditions
|
|
1025
|
+
const { result: checkResult, step: checkStep } = await (0, client_1.runStep)('Check governance (may trigger conditions)', 'check_governance', async () => client.executeTask('check_governance', {
|
|
1026
|
+
plan_id: testPlanId,
|
|
1027
|
+
buyer_campaign_ref: testCampaignRef,
|
|
1028
|
+
binding: 'proposed',
|
|
1029
|
+
caller: callerUrl,
|
|
1030
|
+
tool: 'create_media_buy',
|
|
1031
|
+
payload: {
|
|
1032
|
+
buyer_ref: `e2e-conditions-${Date.now()}`,
|
|
1033
|
+
channel: 'display',
|
|
1034
|
+
budget: { total: 4000, currency: 'USD' },
|
|
1035
|
+
flight: {
|
|
1036
|
+
start: flightStart.toISOString(),
|
|
1037
|
+
end: flightEnd.toISOString(),
|
|
1038
|
+
},
|
|
1039
|
+
countries: ['US'],
|
|
1040
|
+
},
|
|
1041
|
+
}));
|
|
1042
|
+
if (checkResult?.success && checkResult?.data) {
|
|
1043
|
+
const data = checkResult.data;
|
|
1044
|
+
checkStep.details = `Initial check: status=${data.status}`;
|
|
1045
|
+
checkStep.response_preview = JSON.stringify({
|
|
1046
|
+
check_id: data.check_id,
|
|
1047
|
+
status: data.status,
|
|
1048
|
+
explanation: data.explanation,
|
|
1049
|
+
conditions: data.conditions,
|
|
1050
|
+
findings: data.findings?.map((f) => ({
|
|
1051
|
+
category_id: f.category_id,
|
|
1052
|
+
explanation: f.explanation,
|
|
1053
|
+
})),
|
|
1054
|
+
}, null, 2);
|
|
1055
|
+
// If we got conditions, apply them and re-check
|
|
1056
|
+
if (data.status === 'conditions' && data.conditions?.length > 0) {
|
|
1057
|
+
const conditions = data.conditions;
|
|
1058
|
+
const appliedConditions = conditions
|
|
1059
|
+
.filter((c) => c.required_value !== undefined)
|
|
1060
|
+
.map((c) => `${c.field}=${JSON.stringify(c.required_value)}`);
|
|
1061
|
+
checkStep.details += `. Conditions received: ${conditions.length} (${appliedConditions.length} machine-actionable)`;
|
|
1062
|
+
// Build adjusted payload by applying conditions
|
|
1063
|
+
const adjustedPayload = {
|
|
1064
|
+
buyer_ref: `e2e-conditions-adjusted-${Date.now()}`,
|
|
1065
|
+
channel: 'display',
|
|
1066
|
+
budget: { total: 4000, currency: 'USD' },
|
|
1067
|
+
flight: {
|
|
1068
|
+
start: flightStart.toISOString(),
|
|
1069
|
+
end: flightEnd.toISOString(),
|
|
1070
|
+
},
|
|
1071
|
+
countries: ['US'],
|
|
1072
|
+
};
|
|
1073
|
+
for (const condition of conditions) {
|
|
1074
|
+
if (condition.required_value !== undefined) {
|
|
1075
|
+
(0, GovernanceMiddleware_1.setAtPath)(adjustedPayload, condition.field, condition.required_value);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
// Re-check with adjusted parameters
|
|
1079
|
+
const { result: recheckResult, step: recheckStep } = await (0, client_1.runStep)('Re-check governance (after applying conditions)', 'check_governance', async () => client.executeTask('check_governance', {
|
|
1080
|
+
plan_id: testPlanId,
|
|
1081
|
+
buyer_campaign_ref: testCampaignRef,
|
|
1082
|
+
binding: 'proposed',
|
|
1083
|
+
caller: callerUrl,
|
|
1084
|
+
tool: 'create_media_buy',
|
|
1085
|
+
payload: adjustedPayload,
|
|
1086
|
+
}));
|
|
1087
|
+
if (recheckResult?.success && recheckResult?.data) {
|
|
1088
|
+
const recheckData = recheckResult.data;
|
|
1089
|
+
recheckStep.details = `Re-check after conditions: status=${recheckData.status}`;
|
|
1090
|
+
recheckStep.response_preview = JSON.stringify({
|
|
1091
|
+
check_id: recheckData.check_id,
|
|
1092
|
+
status: recheckData.status,
|
|
1093
|
+
explanation: recheckData.explanation,
|
|
1094
|
+
}, null, 2);
|
|
1095
|
+
}
|
|
1096
|
+
else if (recheckResult && !recheckResult.success) {
|
|
1097
|
+
recheckStep.passed = false;
|
|
1098
|
+
recheckStep.error = recheckResult.error || 'Re-check after conditions failed';
|
|
1099
|
+
}
|
|
1100
|
+
steps.push(recheckStep);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
else if (checkResult && !checkResult.success) {
|
|
1104
|
+
checkStep.passed = false;
|
|
1105
|
+
checkStep.error = checkResult.error || 'check_governance failed';
|
|
1106
|
+
}
|
|
1107
|
+
steps.push(checkStep);
|
|
1108
|
+
return { steps, profile };
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Test: Campaign Governance - Delivery Monitoring
|
|
1112
|
+
*
|
|
1113
|
+
* Tests delivery-phase check_governance with delivery_metrics, including
|
|
1114
|
+
* normal pacing and overspend drift detection.
|
|
1115
|
+
*/
|
|
1116
|
+
async function testCampaignGovernanceDelivery(agentUrl, options) {
|
|
1117
|
+
const steps = [];
|
|
1118
|
+
const client = (0, client_1.createTestClient)(agentUrl, options.protocol || 'mcp', options);
|
|
1119
|
+
const { profile, step: profileStep } = await (0, client_1.discoverAgentProfile)(client);
|
|
1120
|
+
steps.push(profileStep);
|
|
1121
|
+
if (!profileStep.passed) {
|
|
1122
|
+
return { steps, profile };
|
|
1123
|
+
}
|
|
1124
|
+
if (!profile.tools.includes('check_governance')) {
|
|
1125
|
+
steps.push({
|
|
1126
|
+
step: 'Campaign governance delivery monitoring support check',
|
|
1127
|
+
passed: false,
|
|
1128
|
+
duration_ms: 0,
|
|
1129
|
+
error: 'Agent requires check_governance for delivery monitoring testing',
|
|
1130
|
+
});
|
|
1131
|
+
return { steps, profile };
|
|
1132
|
+
}
|
|
1133
|
+
profile.supports_governance = true;
|
|
1134
|
+
const testPlanId = `test-delivery-plan-${Date.now()}`;
|
|
1135
|
+
const testCampaignRef = `test-delivery-campaign-${Date.now()}`;
|
|
1136
|
+
const callerUrl = 'https://test-seller.example.com';
|
|
1137
|
+
const flightStart = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
|
|
1138
|
+
const flightEnd = new Date(Date.now() + 23 * 24 * 60 * 60 * 1000);
|
|
1139
|
+
// Sync plan if supported
|
|
1140
|
+
if (profile.tools.includes('sync_plans')) {
|
|
1141
|
+
const { result: syncResult, step: syncStep } = await (0, client_1.runStep)('Sync plan for delivery monitoring', 'sync_plans', async () => client.executeTask('sync_plans', {
|
|
1142
|
+
plans: [
|
|
1143
|
+
{
|
|
1144
|
+
plan_id: testPlanId,
|
|
1145
|
+
brand: options.brand || { domain: 'test.example.com' },
|
|
1146
|
+
objectives: 'E2E delivery monitoring test',
|
|
1147
|
+
budget: {
|
|
1148
|
+
total: 10000,
|
|
1149
|
+
currency: 'USD',
|
|
1150
|
+
authority_level: 'agent_full',
|
|
1151
|
+
},
|
|
1152
|
+
flight: {
|
|
1153
|
+
start: flightStart.toISOString(),
|
|
1154
|
+
end: flightEnd.toISOString(),
|
|
1155
|
+
},
|
|
1156
|
+
countries: ['US'],
|
|
1157
|
+
},
|
|
1158
|
+
],
|
|
1159
|
+
}));
|
|
1160
|
+
steps.push(syncStep);
|
|
1161
|
+
if (!syncResult?.success) {
|
|
1162
|
+
return { steps, profile };
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
// Delivery-phase committed check with metrics
|
|
1166
|
+
const reportingStart = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
|
1167
|
+
const reportingEnd = new Date();
|
|
1168
|
+
const { result: deliveryResult, step: deliveryStep } = await (0, client_1.runStep)('Check governance (delivery phase with metrics)', 'check_governance', async () => client.executeTask('check_governance', {
|
|
1169
|
+
plan_id: testPlanId,
|
|
1170
|
+
buyer_campaign_ref: testCampaignRef,
|
|
1171
|
+
binding: 'committed',
|
|
1172
|
+
caller: callerUrl,
|
|
1173
|
+
media_buy_id: `test-mb-${Date.now()}`,
|
|
1174
|
+
phase: 'delivery',
|
|
1175
|
+
planned_delivery: {
|
|
1176
|
+
total_budget: 3000,
|
|
1177
|
+
currency: 'USD',
|
|
1178
|
+
channels: ['display'],
|
|
1179
|
+
geo: { countries: ['US'] },
|
|
1180
|
+
},
|
|
1181
|
+
delivery_metrics: {
|
|
1182
|
+
reporting_period: {
|
|
1183
|
+
start: reportingStart.toISOString(),
|
|
1184
|
+
end: reportingEnd.toISOString(),
|
|
1185
|
+
},
|
|
1186
|
+
spend: 450,
|
|
1187
|
+
cumulative_spend: 2800,
|
|
1188
|
+
impressions: 15000,
|
|
1189
|
+
cumulative_impressions: 85000,
|
|
1190
|
+
geo_distribution: { US: 100 },
|
|
1191
|
+
channel_distribution: { display: 100 },
|
|
1192
|
+
pacing: 'on_track',
|
|
1193
|
+
},
|
|
1194
|
+
}));
|
|
1195
|
+
if (deliveryResult?.success && deliveryResult?.data) {
|
|
1196
|
+
const data = deliveryResult.data;
|
|
1197
|
+
deliveryStep.details = `Delivery check: status=${data.status}, next_check=${data.next_check || 'not specified'}`;
|
|
1198
|
+
deliveryStep.response_preview = JSON.stringify({
|
|
1199
|
+
check_id: data.check_id,
|
|
1200
|
+
status: data.status,
|
|
1201
|
+
binding: data.binding,
|
|
1202
|
+
explanation: data.explanation,
|
|
1203
|
+
findings: data.findings?.map((f) => ({
|
|
1204
|
+
category_id: f.category_id,
|
|
1205
|
+
severity: f.severity,
|
|
1206
|
+
explanation: f.explanation,
|
|
1207
|
+
})),
|
|
1208
|
+
next_check: data.next_check,
|
|
1209
|
+
}, null, 2);
|
|
1210
|
+
}
|
|
1211
|
+
else if (deliveryResult && !deliveryResult.success) {
|
|
1212
|
+
deliveryStep.passed = false;
|
|
1213
|
+
deliveryStep.error = deliveryResult.error || 'Delivery-phase check_governance failed';
|
|
1214
|
+
}
|
|
1215
|
+
steps.push(deliveryStep);
|
|
1216
|
+
// Delivery-phase check with drift (overspend)
|
|
1217
|
+
const { result: driftResult, step: driftStep } = await (0, client_1.runStep)('Check governance (delivery phase with overspend drift)', 'check_governance', async () => client.executeTask('check_governance', {
|
|
1218
|
+
plan_id: testPlanId,
|
|
1219
|
+
buyer_campaign_ref: testCampaignRef,
|
|
1220
|
+
binding: 'committed',
|
|
1221
|
+
caller: callerUrl,
|
|
1222
|
+
media_buy_id: `test-mb-${Date.now()}`,
|
|
1223
|
+
phase: 'delivery',
|
|
1224
|
+
planned_delivery: {
|
|
1225
|
+
total_budget: 3000,
|
|
1226
|
+
currency: 'USD',
|
|
1227
|
+
channels: ['display'],
|
|
1228
|
+
geo: { countries: ['US'] },
|
|
1229
|
+
},
|
|
1230
|
+
delivery_metrics: {
|
|
1231
|
+
reporting_period: {
|
|
1232
|
+
start: reportingStart.toISOString(),
|
|
1233
|
+
end: reportingEnd.toISOString(),
|
|
1234
|
+
},
|
|
1235
|
+
spend: 2000,
|
|
1236
|
+
cumulative_spend: 9500,
|
|
1237
|
+
impressions: 5000,
|
|
1238
|
+
cumulative_impressions: 90000,
|
|
1239
|
+
pacing: 'ahead',
|
|
1240
|
+
},
|
|
1241
|
+
}));
|
|
1242
|
+
if (driftResult?.success && driftResult?.data) {
|
|
1243
|
+
const data = driftResult.data;
|
|
1244
|
+
driftStep.details = `Overspend drift check: status=${data.status}`;
|
|
1245
|
+
driftStep.response_preview = JSON.stringify({
|
|
1246
|
+
status: data.status,
|
|
1247
|
+
explanation: data.explanation,
|
|
1248
|
+
findings: data.findings?.map((f) => ({
|
|
1249
|
+
category_id: f.category_id,
|
|
1250
|
+
severity: f.severity,
|
|
1251
|
+
explanation: f.explanation,
|
|
1252
|
+
})),
|
|
1253
|
+
}, null, 2);
|
|
1254
|
+
if (data.status === 'approved' && !data.findings?.length) {
|
|
1255
|
+
driftStep.warnings = ['Governance approved delivery at 95% budget with no findings — verify drift detection'];
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
else if (driftResult && !driftResult.success) {
|
|
1259
|
+
driftStep.passed = false;
|
|
1260
|
+
driftStep.error = driftResult.error || 'Drift detection check_governance failed';
|
|
1261
|
+
}
|
|
1262
|
+
steps.push(driftStep);
|
|
1263
|
+
return { steps, profile };
|
|
1264
|
+
}
|
|
596
1265
|
/**
|
|
597
1266
|
* Check if agent has any governance protocol tools
|
|
598
1267
|
*/
|
|
599
1268
|
function hasGovernanceTools(tools) {
|
|
600
1269
|
return capabilities_1.GOVERNANCE_TOOLS.some(t => tools.includes(t));
|
|
601
1270
|
}
|
|
1271
|
+
/**
|
|
1272
|
+
* Check if agent has campaign governance tools
|
|
1273
|
+
*/
|
|
1274
|
+
function hasCampaignGovernanceTools(tools) {
|
|
1275
|
+
return CAMPAIGN_GOVERNANCE_TOOLS.some(t => tools.includes(t));
|
|
1276
|
+
}
|
|
602
1277
|
//# sourceMappingURL=governance.js.map
|