@adcp/client 4.8.0 → 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.
Files changed (102) hide show
  1. package/bin/adcp.js +8 -0
  2. package/dist/lib/adapters/governance-adapter.d.ts +88 -0
  3. package/dist/lib/adapters/governance-adapter.d.ts.map +1 -0
  4. package/dist/lib/adapters/governance-adapter.js +96 -0
  5. package/dist/lib/adapters/governance-adapter.js.map +1 -0
  6. package/dist/lib/adapters/index.d.ts +1 -0
  7. package/dist/lib/adapters/index.d.ts.map +1 -1
  8. package/dist/lib/adapters/index.js +7 -1
  9. package/dist/lib/adapters/index.js.map +1 -1
  10. package/dist/lib/agents/index.generated.d.ts +33 -1
  11. package/dist/lib/agents/index.generated.d.ts.map +1 -1
  12. package/dist/lib/agents/index.generated.js +48 -0
  13. package/dist/lib/agents/index.generated.js.map +1 -1
  14. package/dist/lib/core/AgentClient.d.ts.map +1 -1
  15. package/dist/lib/core/AsyncHandler.d.ts +1 -1
  16. package/dist/lib/core/AsyncHandler.d.ts.map +1 -1
  17. package/dist/lib/core/AsyncHandler.js.map +1 -1
  18. package/dist/lib/core/ConversationTypes.d.ts +8 -2
  19. package/dist/lib/core/ConversationTypes.d.ts.map +1 -1
  20. package/dist/lib/core/GovernanceMiddleware.d.ts +86 -0
  21. package/dist/lib/core/GovernanceMiddleware.d.ts.map +1 -0
  22. package/dist/lib/core/GovernanceMiddleware.js +289 -0
  23. package/dist/lib/core/GovernanceMiddleware.js.map +1 -0
  24. package/dist/lib/core/GovernanceTypes.d.ts +118 -0
  25. package/dist/lib/core/GovernanceTypes.d.ts.map +1 -0
  26. package/dist/lib/core/GovernanceTypes.js +69 -0
  27. package/dist/lib/core/GovernanceTypes.js.map +1 -0
  28. package/dist/lib/core/SingleAgentClient.d.ts +35 -1
  29. package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
  30. package/dist/lib/core/SingleAgentClient.js +45 -0
  31. package/dist/lib/core/SingleAgentClient.js.map +1 -1
  32. package/dist/lib/core/TaskExecutor.d.ts +10 -0
  33. package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
  34. package/dist/lib/core/TaskExecutor.js +95 -10
  35. package/dist/lib/core/TaskExecutor.js.map +1 -1
  36. package/dist/lib/index.d.ts +6 -2
  37. package/dist/lib/index.d.ts.map +1 -1
  38. package/dist/lib/index.js +22 -4
  39. package/dist/lib/index.js.map +1 -1
  40. package/dist/lib/observability/index.d.ts +8 -0
  41. package/dist/lib/observability/index.d.ts.map +1 -0
  42. package/dist/lib/observability/index.js +17 -0
  43. package/dist/lib/observability/index.js.map +1 -0
  44. package/dist/lib/observability/tracing.d.ts +42 -0
  45. package/dist/lib/observability/tracing.d.ts.map +1 -0
  46. package/dist/lib/observability/tracing.js +126 -0
  47. package/dist/lib/observability/tracing.js.map +1 -0
  48. package/dist/lib/protocols/a2a.d.ts.map +1 -1
  49. package/dist/lib/protocols/a2a.js +16 -1
  50. package/dist/lib/protocols/a2a.js.map +1 -1
  51. package/dist/lib/protocols/index.d.ts.map +1 -1
  52. package/dist/lib/protocols/index.js +37 -29
  53. package/dist/lib/protocols/index.js.map +1 -1
  54. package/dist/lib/protocols/mcp.d.ts.map +1 -1
  55. package/dist/lib/protocols/mcp.js +21 -1
  56. package/dist/lib/protocols/mcp.js.map +1 -1
  57. package/dist/lib/registry/types.generated.d.ts +750 -1
  58. package/dist/lib/registry/types.generated.d.ts.map +1 -1
  59. package/dist/lib/registry/types.generated.js +1 -1
  60. package/dist/lib/testing/agent-tester.d.ts +1 -1
  61. package/dist/lib/testing/agent-tester.d.ts.map +1 -1
  62. package/dist/lib/testing/agent-tester.js +36 -11
  63. package/dist/lib/testing/agent-tester.js.map +1 -1
  64. package/dist/lib/testing/orchestrator.d.ts +0 -2
  65. package/dist/lib/testing/orchestrator.d.ts.map +1 -1
  66. package/dist/lib/testing/orchestrator.js +14 -3
  67. package/dist/lib/testing/orchestrator.js.map +1 -1
  68. package/dist/lib/testing/scenarios/capabilities.d.ts.map +1 -1
  69. package/dist/lib/testing/scenarios/capabilities.js +64 -4
  70. package/dist/lib/testing/scenarios/capabilities.js.map +1 -1
  71. package/dist/lib/testing/scenarios/creative.d.ts +14 -0
  72. package/dist/lib/testing/scenarios/creative.d.ts.map +1 -1
  73. package/dist/lib/testing/scenarios/creative.js +241 -42
  74. package/dist/lib/testing/scenarios/creative.js.map +1 -1
  75. package/dist/lib/testing/scenarios/governance.d.ts +48 -0
  76. package/dist/lib/testing/scenarios/governance.d.ts.map +1 -1
  77. package/dist/lib/testing/scenarios/governance.js +675 -0
  78. package/dist/lib/testing/scenarios/governance.js.map +1 -1
  79. package/dist/lib/testing/scenarios/index.d.ts +3 -3
  80. package/dist/lib/testing/scenarios/index.d.ts.map +1 -1
  81. package/dist/lib/testing/scenarios/index.js +8 -1
  82. package/dist/lib/testing/scenarios/index.js.map +1 -1
  83. package/dist/lib/testing/scenarios/media-buy.d.ts +8 -0
  84. package/dist/lib/testing/scenarios/media-buy.d.ts.map +1 -1
  85. package/dist/lib/testing/scenarios/media-buy.js +225 -19
  86. package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
  87. package/dist/lib/testing/types.d.ts +1 -1
  88. package/dist/lib/testing/types.d.ts.map +1 -1
  89. package/dist/lib/types/core.generated.d.ts +629 -5
  90. package/dist/lib/types/core.generated.d.ts.map +1 -1
  91. package/dist/lib/types/core.generated.js +1 -1
  92. package/dist/lib/types/schemas.generated.d.ts +13059 -11101
  93. package/dist/lib/types/schemas.generated.d.ts.map +1 -1
  94. package/dist/lib/types/schemas.generated.js +573 -107
  95. package/dist/lib/types/schemas.generated.js.map +1 -1
  96. package/dist/lib/types/tools.generated.d.ts +1827 -360
  97. package/dist/lib/types/tools.generated.d.ts.map +1 -1
  98. package/dist/lib/utils/capabilities.d.ts +17 -2
  99. package/dist/lib/utils/capabilities.d.ts.map +1 -1
  100. package/dist/lib/utils/capabilities.js +26 -2
  101. package/dist/lib/utils/capabilities.js.map +1 -1
  102. package/package.json +9 -2
@@ -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