@adcp/client 4.14.0 → 4.15.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 (126) hide show
  1. package/bin/adcp.js +2 -0
  2. package/dist/lib/adapters/governance-adapter.d.ts +2 -2
  3. package/dist/lib/adapters/governance-adapter.d.ts.map +1 -1
  4. package/dist/lib/adapters/governance-adapter.js +1 -3
  5. package/dist/lib/adapters/governance-adapter.js.map +1 -1
  6. package/dist/lib/core/AgentClient.d.ts.map +1 -1
  7. package/dist/lib/core/GovernanceMiddleware.d.ts +2 -10
  8. package/dist/lib/core/GovernanceMiddleware.d.ts.map +1 -1
  9. package/dist/lib/core/GovernanceMiddleware.js +8 -51
  10. package/dist/lib/core/GovernanceMiddleware.js.map +1 -1
  11. package/dist/lib/core/GovernanceTypes.d.ts +4 -4
  12. package/dist/lib/core/GovernanceTypes.d.ts.map +1 -1
  13. package/dist/lib/core/GovernanceTypes.js +1 -0
  14. package/dist/lib/core/GovernanceTypes.js.map +1 -1
  15. package/dist/lib/core/SingleAgentClient.d.ts +1 -1
  16. package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
  17. package/dist/lib/core/SingleAgentClient.js +6 -3
  18. package/dist/lib/core/SingleAgentClient.js.map +1 -1
  19. package/dist/lib/core/TaskExecutor.d.ts +4 -0
  20. package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
  21. package/dist/lib/core/TaskExecutor.js +43 -10
  22. package/dist/lib/core/TaskExecutor.js.map +1 -1
  23. package/dist/lib/index.d.ts +5 -3
  24. package/dist/lib/index.d.ts.map +1 -1
  25. package/dist/lib/index.js +12 -6
  26. package/dist/lib/index.js.map +1 -1
  27. package/dist/lib/protocols/index.d.ts +1 -0
  28. package/dist/lib/protocols/index.d.ts.map +1 -1
  29. package/dist/lib/protocols/index.js +14 -4
  30. package/dist/lib/protocols/index.js.map +1 -1
  31. package/dist/lib/protocols/mcp-tasks.d.ts +55 -0
  32. package/dist/lib/protocols/mcp-tasks.d.ts.map +1 -0
  33. package/dist/lib/protocols/mcp-tasks.js +334 -0
  34. package/dist/lib/protocols/mcp-tasks.js.map +1 -0
  35. package/dist/lib/protocols/mcp.d.ts +9 -0
  36. package/dist/lib/protocols/mcp.d.ts.map +1 -1
  37. package/dist/lib/protocols/mcp.js +4 -0
  38. package/dist/lib/protocols/mcp.js.map +1 -1
  39. package/dist/lib/server/index.d.ts +2 -0
  40. package/dist/lib/server/index.d.ts.map +1 -1
  41. package/dist/lib/server/index.js +7 -1
  42. package/dist/lib/server/index.js.map +1 -1
  43. package/dist/lib/server/tasks.d.ts +86 -0
  44. package/dist/lib/server/tasks.d.ts.map +1 -0
  45. package/dist/lib/server/tasks.js +110 -0
  46. package/dist/lib/server/tasks.js.map +1 -0
  47. package/dist/lib/testing/agent-tester.d.ts +1 -1
  48. package/dist/lib/testing/agent-tester.d.ts.map +1 -1
  49. package/dist/lib/testing/agent-tester.js +8 -2
  50. package/dist/lib/testing/agent-tester.js.map +1 -1
  51. package/dist/lib/testing/client.d.ts +13 -0
  52. package/dist/lib/testing/client.d.ts.map +1 -1
  53. package/dist/lib/testing/client.js +22 -0
  54. package/dist/lib/testing/client.js.map +1 -1
  55. package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
  56. package/dist/lib/testing/compliance/comply.js +172 -4
  57. package/dist/lib/testing/compliance/comply.js.map +1 -1
  58. package/dist/lib/testing/compliance/types.d.ts +1 -1
  59. package/dist/lib/testing/compliance/types.d.ts.map +1 -1
  60. package/dist/lib/testing/index.d.ts +2 -0
  61. package/dist/lib/testing/index.d.ts.map +1 -1
  62. package/dist/lib/testing/index.js +4 -1
  63. package/dist/lib/testing/index.js.map +1 -1
  64. package/dist/lib/testing/orchestrator.d.ts.map +1 -1
  65. package/dist/lib/testing/orchestrator.js +7 -2
  66. package/dist/lib/testing/orchestrator.js.map +1 -1
  67. package/dist/lib/testing/scenarios/capabilities.js +2 -2
  68. package/dist/lib/testing/scenarios/capabilities.js.map +1 -1
  69. package/dist/lib/testing/scenarios/creative.js +4 -4
  70. package/dist/lib/testing/scenarios/creative.js.map +1 -1
  71. package/dist/lib/testing/scenarios/discovery.js +2 -2
  72. package/dist/lib/testing/scenarios/discovery.js.map +1 -1
  73. package/dist/lib/testing/scenarios/edge-cases.js +11 -11
  74. package/dist/lib/testing/scenarios/edge-cases.js.map +1 -1
  75. package/dist/lib/testing/scenarios/error-compliance.d.ts.map +1 -1
  76. package/dist/lib/testing/scenarios/error-compliance.js +6 -7
  77. package/dist/lib/testing/scenarios/error-compliance.js.map +1 -1
  78. package/dist/lib/testing/scenarios/governance.d.ts +15 -0
  79. package/dist/lib/testing/scenarios/governance.d.ts.map +1 -1
  80. package/dist/lib/testing/scenarios/governance.js +386 -49
  81. package/dist/lib/testing/scenarios/governance.js.map +1 -1
  82. package/dist/lib/testing/scenarios/health.js +2 -2
  83. package/dist/lib/testing/scenarios/health.js.map +1 -1
  84. package/dist/lib/testing/scenarios/index.d.ts +1 -1
  85. package/dist/lib/testing/scenarios/index.d.ts.map +1 -1
  86. package/dist/lib/testing/scenarios/index.js +2 -1
  87. package/dist/lib/testing/scenarios/index.js.map +1 -1
  88. package/dist/lib/testing/scenarios/media-buy.d.ts.map +1 -1
  89. package/dist/lib/testing/scenarios/media-buy.js +258 -29
  90. package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
  91. package/dist/lib/testing/scenarios/schema-compliance.js +2 -2
  92. package/dist/lib/testing/scenarios/schema-compliance.js.map +1 -1
  93. package/dist/lib/testing/scenarios/signals.d.ts.map +1 -1
  94. package/dist/lib/testing/scenarios/signals.js +35 -2
  95. package/dist/lib/testing/scenarios/signals.js.map +1 -1
  96. package/dist/lib/testing/scenarios/sponsored-intelligence.js +6 -6
  97. package/dist/lib/testing/scenarios/sponsored-intelligence.js.map +1 -1
  98. package/dist/lib/testing/stubs/governance-agent-stub.d.ts +72 -0
  99. package/dist/lib/testing/stubs/governance-agent-stub.d.ts.map +1 -0
  100. package/dist/lib/testing/stubs/governance-agent-stub.js +295 -0
  101. package/dist/lib/testing/stubs/governance-agent-stub.js.map +1 -0
  102. package/dist/lib/testing/stubs/index.d.ts +3 -0
  103. package/dist/lib/testing/stubs/index.d.ts.map +1 -0
  104. package/dist/lib/testing/stubs/index.js +6 -0
  105. package/dist/lib/testing/stubs/index.js.map +1 -0
  106. package/dist/lib/testing/types.d.ts +5 -1
  107. package/dist/lib/testing/types.d.ts.map +1 -1
  108. package/dist/lib/types/core.generated.d.ts +7890 -92
  109. package/dist/lib/types/core.generated.d.ts.map +1 -1
  110. package/dist/lib/types/core.generated.js +1 -1
  111. package/dist/lib/types/error-codes.d.ts +4 -4
  112. package/dist/lib/types/error-codes.d.ts.map +1 -1
  113. package/dist/lib/types/error-codes.js +26 -2
  114. package/dist/lib/types/error-codes.js.map +1 -1
  115. package/dist/lib/types/schemas.generated.d.ts +7649 -3768
  116. package/dist/lib/types/schemas.generated.d.ts.map +1 -1
  117. package/dist/lib/types/schemas.generated.js +677 -418
  118. package/dist/lib/types/schemas.generated.js.map +1 -1
  119. package/dist/lib/types/tools.generated.d.ts +8325 -450
  120. package/dist/lib/types/tools.generated.d.ts.map +1 -1
  121. package/dist/lib/utils/response-schemas.d.ts.map +1 -1
  122. package/dist/lib/utils/response-schemas.js +1 -0
  123. package/dist/lib/utils/response-schemas.js.map +1 -1
  124. package/dist/lib/version.d.ts +3 -3
  125. package/dist/lib/version.js +3 -3
  126. package/package.json +1 -1
@@ -16,10 +16,13 @@ exports.testCampaignGovernance = testCampaignGovernance;
16
16
  exports.testCampaignGovernanceDenied = testCampaignGovernanceDenied;
17
17
  exports.testCampaignGovernanceConditions = testCampaignGovernanceConditions;
18
18
  exports.testCampaignGovernanceDelivery = testCampaignGovernanceDelivery;
19
+ exports.testSellerGovernanceContext = testSellerGovernanceContext;
19
20
  exports.hasGovernanceTools = hasGovernanceTools;
20
21
  exports.hasCampaignGovernanceTools = hasCampaignGovernanceTools;
21
22
  const client_1 = require("../client");
22
23
  const capabilities_1 = require("../../utils/capabilities");
24
+ const stubs_1 = require("../stubs");
25
+ const mcp_1 = require("../../protocols/mcp");
23
26
  const GovernanceMiddleware_1 = require("../../core/GovernanceMiddleware");
24
27
  // Property list tools
25
28
  const PROPERTY_LIST_TOOLS = [
@@ -46,9 +49,9 @@ const CONTENT_STANDARDS_TOOLS = [
46
49
  */
47
50
  async function testGovernancePropertyLists(agentUrl, options) {
48
51
  const steps = [];
49
- const client = (0, client_1.createTestClient)(agentUrl, options.protocol || 'mcp', options);
52
+ const client = (0, client_1.getOrCreateClient)(agentUrl, options);
50
53
  // Discover agent profile
51
- const { profile, step: profileStep } = await (0, client_1.discoverAgentProfile)(client);
54
+ const { profile, step: profileStep } = await (0, client_1.getOrDiscoverProfile)(client, options);
52
55
  steps.push(profileStep);
53
56
  if (!profileStep.passed) {
54
57
  return { steps, profile };
@@ -250,9 +253,9 @@ async function testGovernancePropertyLists(agentUrl, options) {
250
253
  */
251
254
  async function testGovernanceContentStandards(agentUrl, options) {
252
255
  const steps = [];
253
- const client = (0, client_1.createTestClient)(agentUrl, options.protocol || 'mcp', options);
256
+ const client = (0, client_1.getOrCreateClient)(agentUrl, options);
254
257
  // Discover agent profile
255
- const { profile, step: profileStep } = await (0, client_1.discoverAgentProfile)(client);
258
+ const { profile, step: profileStep } = await (0, client_1.getOrDiscoverProfile)(client, options);
256
259
  steps.push(profileStep);
257
260
  if (!profileStep.passed) {
258
261
  return { steps, profile };
@@ -445,8 +448,8 @@ async function testGovernanceContentStandards(agentUrl, options) {
445
448
  */
446
449
  async function testPropertyListFilters(agentUrl, options) {
447
450
  const steps = [];
448
- const client = (0, client_1.createTestClient)(agentUrl, options.protocol || 'mcp', options);
449
- const { profile, step: profileStep } = await (0, client_1.discoverAgentProfile)(client);
451
+ const client = (0, client_1.getOrCreateClient)(agentUrl, options);
452
+ const { profile, step: profileStep } = await (0, client_1.getOrDiscoverProfile)(client, options);
450
453
  steps.push(profileStep);
451
454
  if (!profileStep.passed) {
452
455
  return { steps, profile };
@@ -628,9 +631,9 @@ const CAMPAIGN_GOVERNANCE_TOOLS = [
628
631
  */
629
632
  async function testCampaignGovernance(agentUrl, options) {
630
633
  const steps = [];
631
- const client = (0, client_1.createTestClient)(agentUrl, options.protocol || 'mcp', options);
634
+ const client = (0, client_1.getOrCreateClient)(agentUrl, options);
632
635
  // Discover agent profile
633
- const { profile, step: profileStep } = await (0, client_1.discoverAgentProfile)(client);
636
+ const { profile, step: profileStep } = await (0, client_1.getOrDiscoverProfile)(client, options);
634
637
  steps.push(profileStep);
635
638
  if (!profileStep.passed) {
636
639
  return { steps, profile };
@@ -649,7 +652,7 @@ async function testCampaignGovernance(agentUrl, options) {
649
652
  }
650
653
  profile.supports_governance = true;
651
654
  const testPlanId = `test-plan-${Date.now()}`;
652
- const testCampaignRef = `test-campaign-${Date.now()}`;
655
+ const testMediaBuyId = `test-mb-${Date.now()}`;
653
656
  const callerUrl = 'https://test-orchestrator.example.com';
654
657
  const flightStart = new Date();
655
658
  const flightEnd = new Date(flightStart.getTime() + 30 * 24 * 60 * 60 * 1000); // 30 days
@@ -712,15 +715,14 @@ async function testCampaignGovernance(agentUrl, options) {
712
715
  }
713
716
  // Step 2: check_governance (proposed, expecting approved)
714
717
  let checkId;
718
+ let governanceContext;
715
719
  if (profile.tools.includes('check_governance')) {
716
720
  const { result, step } = await (0, client_1.runStep)('Check governance (proposed buy)', 'check_governance', async () => client.executeTask('check_governance', {
717
721
  plan_id: testPlanId,
718
- buyer_campaign_ref: testCampaignRef,
719
722
  binding: 'proposed',
720
723
  caller: callerUrl,
721
724
  tool: 'create_media_buy',
722
725
  payload: {
723
- buyer_ref: `e2e-test-${Date.now()}`,
724
726
  channel: 'display',
725
727
  budget: { total: 1000, currency: 'USD' },
726
728
  flight: {
@@ -733,6 +735,7 @@ async function testCampaignGovernance(agentUrl, options) {
733
735
  if (result?.success && result?.data) {
734
736
  const data = result.data;
735
737
  checkId = data.check_id;
738
+ governanceContext = data.governance_context;
736
739
  const status = data.status;
737
740
  step.details = `Governance check: status=${status}, binding=${data.binding}, mode=${data.mode || 'unknown'}`;
738
741
  step.response_preview = JSON.stringify({
@@ -743,12 +746,25 @@ async function testCampaignGovernance(agentUrl, options) {
743
746
  explanation: data.explanation,
744
747
  findings_count: data.findings?.length || 0,
745
748
  expires_at: data.expires_at,
749
+ governance_context: data.governance_context ? '(present)' : '(absent)',
746
750
  }, null, 2);
747
751
  // Any status is valid — we're testing the protocol, not the policy
748
752
  if (!['approved', 'denied', 'conditions', 'escalated'].includes(status)) {
749
753
  step.passed = false;
750
754
  step.error = `Unexpected governance status: ${status}`;
751
755
  }
756
+ // Validate governance_context format if present
757
+ if (governanceContext !== undefined) {
758
+ if (typeof governanceContext !== 'string') {
759
+ step.warnings = [...(step.warnings || []), 'governance_context is not a string'];
760
+ }
761
+ else if (governanceContext.length > 4096) {
762
+ step.warnings = [
763
+ ...(step.warnings || []),
764
+ `governance_context exceeds 4096 chars (${governanceContext.length})`,
765
+ ];
766
+ }
767
+ }
752
768
  }
753
769
  else if (result && !result.success) {
754
770
  step.passed = false;
@@ -756,27 +772,25 @@ async function testCampaignGovernance(agentUrl, options) {
756
772
  }
757
773
  steps.push(step);
758
774
  }
759
- // Step 3: report_plan_outcome (completed)
775
+ // Step 3: report_plan_outcome (completed) — thread governance_context
760
776
  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', {
777
+ const outcomeRequest = {
762
778
  plan_id: testPlanId,
763
779
  check_id: checkId,
764
- buyer_campaign_ref: testCampaignRef,
765
780
  outcome: 'completed',
781
+ governance_context: governanceContext || '',
766
782
  seller_response: {
767
- media_buy_id: `test-mb-${Date.now()}`,
768
- buyer_ref: `e2e-test-${Date.now()}`,
783
+ media_buy_id: testMediaBuyId,
769
784
  packages: [
770
785
  {
771
- package_id: 'test-pkg-1',
772
- name: 'E2E Test Package',
773
- budget: { total: 1000, currency: 'USD' },
786
+ budget: 1000,
774
787
  },
775
788
  ],
776
789
  },
777
- }));
790
+ };
791
+ const { result, step } = await (0, client_1.runStep)('Report plan outcome (completed)', 'report_plan_outcome', async () => client.executeTask('report_plan_outcome', outcomeRequest));
778
792
  if (result?.success && result?.data) {
779
- step.details = 'Outcome reported to governance agent';
793
+ step.details = `Outcome reported with governance_context=${governanceContext ? 'present' : 'absent'}`;
780
794
  step.response_preview = JSON.stringify(result.data, null, 2);
781
795
  }
782
796
  else if (result && !result.success) {
@@ -785,11 +799,11 @@ async function testCampaignGovernance(agentUrl, options) {
785
799
  }
786
800
  steps.push(step);
787
801
  }
788
- // Step 4: get_plan_audit_logs
802
+ // Step 4: get_plan_audit_logs — query by media_buy_id
789
803
  if (profile.tools.includes('get_plan_audit_logs')) {
790
804
  const { result, step } = await (0, client_1.runStep)('Get plan audit logs', 'get_plan_audit_logs', async () => client.executeTask('get_plan_audit_logs', {
791
805
  plan_ids: [testPlanId],
792
- buyer_campaign_ref: testCampaignRef,
806
+ media_buy_id: testMediaBuyId,
793
807
  include_entries: true,
794
808
  }));
795
809
  if (result?.success && result?.data) {
@@ -818,8 +832,8 @@ async function testCampaignGovernance(agentUrl, options) {
818
832
  */
819
833
  async function testCampaignGovernanceDenied(agentUrl, options) {
820
834
  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);
835
+ const client = (0, client_1.getOrCreateClient)(agentUrl, options);
836
+ const { profile, step: profileStep } = await (0, client_1.getOrDiscoverProfile)(client, options);
823
837
  steps.push(profileStep);
824
838
  if (!profileStep.passed) {
825
839
  return { steps, profile };
@@ -835,7 +849,6 @@ async function testCampaignGovernanceDenied(agentUrl, options) {
835
849
  }
836
850
  profile.supports_governance = true;
837
851
  const testPlanId = `test-denied-plan-${Date.now()}`;
838
- const testCampaignRef = `test-denied-campaign-${Date.now()}`;
839
852
  const callerUrl = 'https://test-orchestrator.example.com';
840
853
  const flightStart = new Date();
841
854
  const flightEnd = new Date(flightStart.getTime() + 7 * 24 * 60 * 60 * 1000);
@@ -870,12 +883,10 @@ async function testCampaignGovernanceDenied(agentUrl, options) {
870
883
  // Check governance with over-budget request
871
884
  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
885
  plan_id: testPlanId,
873
- buyer_campaign_ref: testCampaignRef,
874
886
  binding: 'proposed',
875
887
  caller: callerUrl,
876
888
  tool: 'create_media_buy',
877
889
  payload: {
878
- buyer_ref: `e2e-overbudget-${Date.now()}`,
879
890
  channel: 'display',
880
891
  budget: { total: 50000, currency: 'USD' },
881
892
  flight: {
@@ -885,8 +896,10 @@ async function testCampaignGovernanceDenied(agentUrl, options) {
885
896
  countries: ['US'],
886
897
  },
887
898
  }));
899
+ let overBudgetGovernanceContext;
888
900
  if (overBudgetResult?.success && overBudgetResult?.data) {
889
901
  const data = overBudgetResult.data;
902
+ overBudgetGovernanceContext = data.governance_context;
890
903
  overBudgetStep.details = `Over-budget check: status=${data.status}, explanation: ${data.explanation}`;
891
904
  overBudgetStep.response_preview = JSON.stringify({
892
905
  status: data.status,
@@ -897,6 +910,7 @@ async function testCampaignGovernanceDenied(agentUrl, options) {
897
910
  explanation: f.explanation,
898
911
  })),
899
912
  conditions: data.conditions,
913
+ governance_context: data.governance_context ? '(present)' : '(absent)',
900
914
  }, null, 2);
901
915
  if (data.status === 'approved' && data.mode !== 'advisory' && data.mode !== 'audit') {
902
916
  overBudgetStep.passed = false;
@@ -917,12 +931,10 @@ async function testCampaignGovernanceDenied(agentUrl, options) {
917
931
  // Check governance with unauthorized market
918
932
  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
933
  plan_id: testPlanId,
920
- buyer_campaign_ref: testCampaignRef,
921
934
  binding: 'proposed',
922
935
  caller: callerUrl,
923
936
  tool: 'create_media_buy',
924
937
  payload: {
925
- buyer_ref: `e2e-badgeo-${Date.now()}`,
926
938
  channel: 'display',
927
939
  budget: { total: 100, currency: 'USD' },
928
940
  flight: {
@@ -954,13 +966,13 @@ async function testCampaignGovernanceDenied(agentUrl, options) {
954
966
  geoStep.error = geoResult.error || 'check_governance failed';
955
967
  }
956
968
  steps.push(geoStep);
957
- // Report failed outcome
969
+ // Report failed outcome — thread governance_context
958
970
  if (profile.tools.includes('report_plan_outcome') && overBudgetResult?.data?.check_id) {
959
971
  const { step: outcomeStep } = await (0, client_1.runStep)('Report failed outcome for denied check', 'report_plan_outcome', async () => client.executeTask('report_plan_outcome', {
960
972
  plan_id: testPlanId,
961
973
  check_id: overBudgetResult.data.check_id,
962
- buyer_campaign_ref: testCampaignRef,
963
974
  outcome: 'failed',
975
+ governance_context: overBudgetGovernanceContext || '',
964
976
  error: {
965
977
  code: 'governance_denied',
966
978
  message: 'Action blocked by governance check',
@@ -978,8 +990,8 @@ async function testCampaignGovernanceDenied(agentUrl, options) {
978
990
  */
979
991
  async function testCampaignGovernanceConditions(agentUrl, options) {
980
992
  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);
993
+ const client = (0, client_1.getOrCreateClient)(agentUrl, options);
994
+ const { profile, step: profileStep } = await (0, client_1.getOrDiscoverProfile)(client, options);
983
995
  steps.push(profileStep);
984
996
  if (!profileStep.passed) {
985
997
  return { steps, profile };
@@ -995,7 +1007,6 @@ async function testCampaignGovernanceConditions(agentUrl, options) {
995
1007
  }
996
1008
  profile.supports_governance = true;
997
1009
  const testPlanId = `test-conditions-plan-${Date.now()}`;
998
- const testCampaignRef = `test-conditions-campaign-${Date.now()}`;
999
1010
  const callerUrl = 'https://test-orchestrator.example.com';
1000
1011
  const flightStart = new Date();
1001
1012
  const flightEnd = new Date(flightStart.getTime() + 14 * 24 * 60 * 60 * 1000);
@@ -1035,12 +1046,10 @@ async function testCampaignGovernanceConditions(agentUrl, options) {
1035
1046
  // First check: send budget that might trigger conditions
1036
1047
  const { result: checkResult, step: checkStep } = await (0, client_1.runStep)('Check governance (may trigger conditions)', 'check_governance', async () => client.executeTask('check_governance', {
1037
1048
  plan_id: testPlanId,
1038
- buyer_campaign_ref: testCampaignRef,
1039
1049
  binding: 'proposed',
1040
1050
  caller: callerUrl,
1041
1051
  tool: 'create_media_buy',
1042
1052
  payload: {
1043
- buyer_ref: `e2e-conditions-${Date.now()}`,
1044
1053
  channel: 'display',
1045
1054
  budget: { total: 4000, currency: 'USD' },
1046
1055
  flight: {
@@ -1052,6 +1061,7 @@ async function testCampaignGovernanceConditions(agentUrl, options) {
1052
1061
  }));
1053
1062
  if (checkResult?.success && checkResult?.data) {
1054
1063
  const data = checkResult.data;
1064
+ const initialGovernanceContext = data.governance_context;
1055
1065
  checkStep.details = `Initial check: status=${data.status}`;
1056
1066
  checkStep.response_preview = JSON.stringify({
1057
1067
  check_id: data.check_id,
@@ -1062,8 +1072,9 @@ async function testCampaignGovernanceConditions(agentUrl, options) {
1062
1072
  category_id: f.category_id,
1063
1073
  explanation: f.explanation,
1064
1074
  })),
1075
+ governance_context: initialGovernanceContext ? '(present)' : '(absent)',
1065
1076
  }, null, 2);
1066
- // If we got conditions, apply them and re-check
1077
+ // If we got conditions, apply them and re-check with governance_context round-trip
1067
1078
  if (data.status === 'conditions' && data.conditions?.length > 0) {
1068
1079
  const conditions = data.conditions;
1069
1080
  const appliedConditions = conditions
@@ -1072,7 +1083,6 @@ async function testCampaignGovernanceConditions(agentUrl, options) {
1072
1083
  checkStep.details += `. Conditions received: ${conditions.length} (${appliedConditions.length} machine-actionable)`;
1073
1084
  // Build adjusted payload by applying conditions
1074
1085
  const adjustedPayload = {
1075
- buyer_ref: `e2e-conditions-adjusted-${Date.now()}`,
1076
1086
  channel: 'display',
1077
1087
  budget: { total: 4000, currency: 'USD' },
1078
1088
  flight: {
@@ -1086,14 +1096,14 @@ async function testCampaignGovernanceConditions(agentUrl, options) {
1086
1096
  (0, GovernanceMiddleware_1.setAtPath)(adjustedPayload, condition.field, condition.required_value);
1087
1097
  }
1088
1098
  }
1089
- // Re-check with adjusted parameters
1099
+ // Re-check with adjusted parameters — thread governance_context from initial check
1090
1100
  const { result: recheckResult, step: recheckStep } = await (0, client_1.runStep)('Re-check governance (after applying conditions)', 'check_governance', async () => client.executeTask('check_governance', {
1091
1101
  plan_id: testPlanId,
1092
- buyer_campaign_ref: testCampaignRef,
1093
1102
  binding: 'proposed',
1094
1103
  caller: callerUrl,
1095
1104
  tool: 'create_media_buy',
1096
1105
  payload: adjustedPayload,
1106
+ governance_context: initialGovernanceContext,
1097
1107
  }));
1098
1108
  if (recheckResult?.success && recheckResult?.data) {
1099
1109
  const recheckData = recheckResult.data;
@@ -1102,6 +1112,7 @@ async function testCampaignGovernanceConditions(agentUrl, options) {
1102
1112
  check_id: recheckData.check_id,
1103
1113
  status: recheckData.status,
1104
1114
  explanation: recheckData.explanation,
1115
+ governance_context: recheckData.governance_context ? '(present)' : '(absent)',
1105
1116
  }, null, 2);
1106
1117
  }
1107
1118
  else if (recheckResult && !recheckResult.success) {
@@ -1126,8 +1137,8 @@ async function testCampaignGovernanceConditions(agentUrl, options) {
1126
1137
  */
1127
1138
  async function testCampaignGovernanceDelivery(agentUrl, options) {
1128
1139
  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);
1140
+ const client = (0, client_1.getOrCreateClient)(agentUrl, options);
1141
+ const { profile, step: profileStep } = await (0, client_1.getOrDiscoverProfile)(client, options);
1131
1142
  steps.push(profileStep);
1132
1143
  if (!profileStep.passed) {
1133
1144
  return { steps, profile };
@@ -1143,7 +1154,7 @@ async function testCampaignGovernanceDelivery(agentUrl, options) {
1143
1154
  }
1144
1155
  profile.supports_governance = true;
1145
1156
  const testPlanId = `test-delivery-plan-${Date.now()}`;
1146
- const testCampaignRef = `test-delivery-campaign-${Date.now()}`;
1157
+ const testMediaBuyId = `test-mb-${Date.now()}`;
1147
1158
  const callerUrl = 'https://test-seller.example.com';
1148
1159
  const flightStart = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
1149
1160
  const flightEnd = new Date(Date.now() + 23 * 24 * 60 * 60 * 1000);
@@ -1178,10 +1189,9 @@ async function testCampaignGovernanceDelivery(agentUrl, options) {
1178
1189
  const reportingEnd = new Date();
1179
1190
  const { result: deliveryResult, step: deliveryStep } = await (0, client_1.runStep)('Check governance (delivery phase with metrics)', 'check_governance', async () => client.executeTask('check_governance', {
1180
1191
  plan_id: testPlanId,
1181
- buyer_campaign_ref: testCampaignRef,
1182
1192
  binding: 'committed',
1183
1193
  caller: callerUrl,
1184
- media_buy_id: `test-mb-${Date.now()}`,
1194
+ media_buy_id: testMediaBuyId,
1185
1195
  phase: 'delivery',
1186
1196
  planned_delivery: {
1187
1197
  total_budget: 3000,
@@ -1203,8 +1213,10 @@ async function testCampaignGovernanceDelivery(agentUrl, options) {
1203
1213
  pacing: 'on_track',
1204
1214
  },
1205
1215
  }));
1216
+ let deliveryGovernanceContext;
1206
1217
  if (deliveryResult?.success && deliveryResult?.data) {
1207
1218
  const data = deliveryResult.data;
1219
+ deliveryGovernanceContext = data.governance_context;
1208
1220
  deliveryStep.details = `Delivery check: status=${data.status}, next_check=${data.next_check || 'not specified'}`;
1209
1221
  deliveryStep.response_preview = JSON.stringify({
1210
1222
  check_id: data.check_id,
@@ -1217,6 +1229,7 @@ async function testCampaignGovernanceDelivery(agentUrl, options) {
1217
1229
  explanation: f.explanation,
1218
1230
  })),
1219
1231
  next_check: data.next_check,
1232
+ governance_context: data.governance_context ? '(present)' : '(absent)',
1220
1233
  }, null, 2);
1221
1234
  }
1222
1235
  else if (deliveryResult && !deliveryResult.success) {
@@ -1224,14 +1237,14 @@ async function testCampaignGovernanceDelivery(agentUrl, options) {
1224
1237
  deliveryStep.error = deliveryResult.error || 'Delivery-phase check_governance failed';
1225
1238
  }
1226
1239
  steps.push(deliveryStep);
1227
- // Delivery-phase check with drift (overspend)
1240
+ // Delivery-phase check with drift (overspend) — thread governance_context from first check
1228
1241
  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
1242
  plan_id: testPlanId,
1230
- buyer_campaign_ref: testCampaignRef,
1231
1243
  binding: 'committed',
1232
1244
  caller: callerUrl,
1233
- media_buy_id: `test-mb-${Date.now()}`,
1245
+ media_buy_id: testMediaBuyId,
1234
1246
  phase: 'delivery',
1247
+ governance_context: deliveryGovernanceContext,
1235
1248
  planned_delivery: {
1236
1249
  total_budget: 3000,
1237
1250
  currency: 'USD',
@@ -1273,6 +1286,330 @@ async function testCampaignGovernanceDelivery(agentUrl, options) {
1273
1286
  steps.push(driftStep);
1274
1287
  return { steps, profile };
1275
1288
  }
1289
+ /**
1290
+ * Test: Seller Governance Context Round-Trip
1291
+ *
1292
+ * Two-tier test:
1293
+ * 1. (Active) If seller supports register_governance: starts a stub governance
1294
+ * agent, registers it with the seller, creates a media buy, and verifies
1295
+ * the seller called check_governance on the stub with the correct
1296
+ * governance_context.
1297
+ * 2. (Passive) Verifies the seller persists governance_context from
1298
+ * create_media_buy and returns it on get_media_buys.
1299
+ */
1300
+ async function testSellerGovernanceContext(agentUrl, options) {
1301
+ const steps = [];
1302
+ const client = (0, client_1.getOrCreateClient)(agentUrl, options);
1303
+ const { profile, step: profileStep } = await (0, client_1.getOrDiscoverProfile)(client, options);
1304
+ steps.push(profileStep);
1305
+ if (!profileStep.passed) {
1306
+ return { steps, profile };
1307
+ }
1308
+ // This scenario requires create_media_buy + get_media_buys (seller tools)
1309
+ if (!profile.tools.includes('create_media_buy') || !profile.tools.includes('get_media_buys')) {
1310
+ steps.push({
1311
+ step: 'Seller governance_context support check',
1312
+ passed: false,
1313
+ duration_ms: 0,
1314
+ error: 'Agent requires create_media_buy + get_media_buys for governance_context persistence testing',
1315
+ });
1316
+ return { steps, profile };
1317
+ }
1318
+ // Need get_products to create a valid media buy
1319
+ if (!profile.tools.includes('get_products')) {
1320
+ steps.push({
1321
+ step: 'Seller governance_context support check',
1322
+ passed: false,
1323
+ duration_ms: 0,
1324
+ error: 'Agent requires get_products for governance_context persistence testing',
1325
+ });
1326
+ return { steps, profile };
1327
+ }
1328
+ // Step 1: Get products
1329
+ const { result: productsResult, step: productsStep } = await (0, client_1.runStep)('Fetch products for governance context test', 'get_products', async () => client.executeTask('get_products', {
1330
+ buying_mode: 'brief',
1331
+ brief: options.brief || 'display advertising',
1332
+ brand: options.brand || { domain: 'test.example.com' },
1333
+ }));
1334
+ steps.push(productsStep);
1335
+ const products = productsResult?.data?.products;
1336
+ if (!products?.length) {
1337
+ return { steps, profile };
1338
+ }
1339
+ // Pick first product with pricing
1340
+ const product = products[0];
1341
+ const pricingOptions = product.pricing_options;
1342
+ const pricing = pricingOptions?.[0];
1343
+ if (!pricing) {
1344
+ steps.push({
1345
+ step: 'Select product for governance context test',
1346
+ passed: false,
1347
+ duration_ms: 0,
1348
+ error: 'No products with pricing options available',
1349
+ });
1350
+ return { steps, profile };
1351
+ }
1352
+ // Step 2: Resolve an account to use for the test
1353
+ const account = await resolveTestAccount(client, profile, options);
1354
+ // Step 3: If seller supports register_governance, start stub and register it
1355
+ let stub = null;
1356
+ let stubUrl = null;
1357
+ let governanceContext = null;
1358
+ const planId = `plan-comply-gc-${Date.now()}`;
1359
+ // register_governance is from AdCP PR #1644 — merged but schemas not yet deployed.
1360
+ // Once deployed and synced, sellers that implement it will get the active stub test.
1361
+ const hasRegisterGovernance = profile.tools.includes('register_governance');
1362
+ if (hasRegisterGovernance) {
1363
+ stub = new stubs_1.GovernanceAgentStub();
1364
+ try {
1365
+ const info = await stub.startHttps();
1366
+ stubUrl = info.url;
1367
+ }
1368
+ catch {
1369
+ // HTTPS generation failed (no openssl?) — fall back to HTTP
1370
+ try {
1371
+ const info = await stub.start();
1372
+ stubUrl = info.url;
1373
+ }
1374
+ catch (err) {
1375
+ steps.push({
1376
+ step: 'Start governance agent stub',
1377
+ passed: false,
1378
+ duration_ms: 0,
1379
+ error: `Failed to start governance agent stub: ${err.message}`,
1380
+ });
1381
+ return { steps, profile };
1382
+ }
1383
+ }
1384
+ steps.push({
1385
+ step: 'Start governance agent stub',
1386
+ passed: true,
1387
+ duration_ms: 0,
1388
+ details: `Stub running at ${stubUrl}`,
1389
+ });
1390
+ // Register the stub with the seller
1391
+ const { result: registerResult, step: registerStep } = await (0, client_1.runStep)('Register governance agent with seller', 'register_governance', async () => client.executeTask('register_governance', {
1392
+ accounts: [
1393
+ {
1394
+ account,
1395
+ governance_agents: [
1396
+ {
1397
+ url: stubUrl,
1398
+ authentication: {
1399
+ schemes: ['Bearer'],
1400
+ credentials: stub.authToken,
1401
+ },
1402
+ },
1403
+ ],
1404
+ },
1405
+ ],
1406
+ }));
1407
+ steps.push(registerStep);
1408
+ if (!registerResult?.success) {
1409
+ // Registration failed — fall through to passive test
1410
+ steps.push({
1411
+ step: 'Governance agent registration',
1412
+ passed: true,
1413
+ duration_ms: 0,
1414
+ details: 'register_governance failed — falling back to passive governance_context persistence test',
1415
+ warnings: ['Cannot verify seller calls governance agent — register_governance returned an error'],
1416
+ });
1417
+ }
1418
+ else {
1419
+ // Get governance_context from the stub (simulate buyer's proposed check)
1420
+ try {
1421
+ const checkResult = await (0, mcp_1.callMCPTool)(stubUrl, 'check_governance', {
1422
+ plan_id: planId,
1423
+ binding: 'proposed',
1424
+ caller: 'buyer',
1425
+ tool: 'create_media_buy',
1426
+ payload: {},
1427
+ });
1428
+ const parsed = JSON.parse(checkResult.content[0].text);
1429
+ governanceContext = parsed.governance_context;
1430
+ steps.push({
1431
+ step: 'Obtain governance_context from stub (proposed check)',
1432
+ passed: true,
1433
+ duration_ms: 0,
1434
+ details: `governance_context received (${governanceContext?.length ?? 0} chars)`,
1435
+ });
1436
+ }
1437
+ catch (err) {
1438
+ steps.push({
1439
+ step: 'Obtain governance_context from stub (proposed check)',
1440
+ passed: false,
1441
+ duration_ms: 0,
1442
+ error: `Failed to call check_governance on stub: ${err.message}`,
1443
+ });
1444
+ }
1445
+ }
1446
+ }
1447
+ else {
1448
+ steps.push({
1449
+ step: 'Check register_governance support',
1450
+ passed: true,
1451
+ duration_ms: 0,
1452
+ details: 'Seller does not support register_governance — running passive governance_context persistence test only',
1453
+ warnings: ['Cannot verify seller calls governance agent — register_governance not supported'],
1454
+ });
1455
+ }
1456
+ // Step 4: Create media buy WITH governance_context
1457
+ const testGovernanceContext = governanceContext || `test-gc-comply-${Date.now()}`;
1458
+ const flightStart = new Date();
1459
+ const flightEnd = new Date(flightStart.getTime() + 30 * 24 * 60 * 60 * 1000);
1460
+ const { result: createResult, step: createStep } = await (0, client_1.runStep)('Create media buy with governance_context', 'create_media_buy', async () => client.executeTask('create_media_buy', {
1461
+ account,
1462
+ brand: options.brand || { domain: 'test.example.com' },
1463
+ start_time: flightStart.toISOString(),
1464
+ end_time: flightEnd.toISOString(),
1465
+ plan_id: governanceContext ? planId : undefined,
1466
+ packages: [
1467
+ {
1468
+ product_id: product.product_id,
1469
+ pricing_option_id: pricing.pricing_option_id,
1470
+ budget: pricing.min_spend_per_package || 500,
1471
+ bid_price: (pricing.floor_price || pricing.fixed_price || 10) + 1,
1472
+ name: 'Governance context test package',
1473
+ },
1474
+ ],
1475
+ governance_context: testGovernanceContext,
1476
+ }));
1477
+ if (!createResult?.success || !createResult?.data) {
1478
+ createStep.passed = false;
1479
+ createStep.error = createResult?.error || 'create_media_buy failed';
1480
+ steps.push(createStep);
1481
+ await stopStub(stub);
1482
+ return { steps, profile };
1483
+ }
1484
+ const mediaBuyId = createResult.data.media_buy_id;
1485
+ createStep.details = `Created media buy ${mediaBuyId} with governance_context`;
1486
+ steps.push(createStep);
1487
+ // Step 5: If stub is active, verify seller called check_governance(committed)
1488
+ if (stub && governanceContext && stubUrl) {
1489
+ // Poll the stub's call log for the committed check (100ms interval, 5s timeout)
1490
+ const pollStart = Date.now();
1491
+ const pollTimeout = 5000;
1492
+ const pollInterval = 100;
1493
+ while (Date.now() - pollStart < pollTimeout) {
1494
+ const calls = stub.getCallsForTool('check_governance');
1495
+ if (calls.some(c => c.params.binding === 'committed'))
1496
+ break;
1497
+ await new Promise(r => setTimeout(r, pollInterval));
1498
+ }
1499
+ const committedCalls = stub.getCallsForTool('check_governance').filter(c => c.params.binding === 'committed');
1500
+ const { step: callbackStep } = await (0, client_1.runStep)('Verify seller called check_governance(committed) on governance agent', 'check_governance (callback)', async () => {
1501
+ if (committedCalls.length === 0) {
1502
+ throw new Error('Seller did not call check_governance(committed) on the registered governance agent. ' +
1503
+ 'Sellers MUST call check_governance with binding="committed" before executing a media buy.');
1504
+ }
1505
+ const callWithContext = committedCalls.find(c => c.params.governance_context === governanceContext);
1506
+ if (!callWithContext) {
1507
+ throw new Error(`Seller called check_governance(committed) but with wrong governance_context. ` +
1508
+ `Expected "${governanceContext}", got: ${committedCalls.map(c => JSON.stringify(c.params.governance_context)).join(', ')}`);
1509
+ }
1510
+ });
1511
+ if (committedCalls.length > 0) {
1512
+ callbackStep.response_preview = JSON.stringify(committedCalls[0].params, null, 2);
1513
+ }
1514
+ steps.push(callbackStep);
1515
+ }
1516
+ // Step 6: Retrieve the media buy and check for governance_context persistence
1517
+ const { result: getResult, step: getStep } = await (0, client_1.runStep)('Verify governance_context persisted on get_media_buys', 'get_media_buys', async () => client.executeTask('get_media_buys', {
1518
+ media_buy_ids: [mediaBuyId],
1519
+ }));
1520
+ if (getResult?.success && getResult?.data) {
1521
+ const buys = getResult.data.media_buys || [];
1522
+ const buy = buys.find((b) => b.media_buy_id === mediaBuyId) ||
1523
+ (buys.length === 1 ? buys[0] : undefined);
1524
+ if (buy) {
1525
+ const returnedGC = buy.governance_context;
1526
+ if (returnedGC === testGovernanceContext) {
1527
+ getStep.details = 'governance_context persisted and returned correctly';
1528
+ }
1529
+ else if (returnedGC) {
1530
+ getStep.passed = false;
1531
+ getStep.error = `governance_context returned but value changed: expected "${testGovernanceContext}", got "${returnedGC}"`;
1532
+ }
1533
+ else {
1534
+ getStep.passed = false;
1535
+ getStep.error =
1536
+ 'Seller did not return governance_context on get_media_buys. ' +
1537
+ 'Sellers MUST persist governance_context from create_media_buy and include it on all subsequent responses.';
1538
+ }
1539
+ getStep.response_preview = JSON.stringify({
1540
+ media_buy_id: buy.media_buy_id,
1541
+ governance_context: returnedGC ? `(present, ${returnedGC.length} chars)` : '(absent)',
1542
+ status: buy.status,
1543
+ }, null, 2);
1544
+ }
1545
+ else if (buys.length === 0) {
1546
+ getStep.details =
1547
+ 'get_media_buys returned 0 buys — agent may be stateless (test agent). Cannot verify governance_context persistence.';
1548
+ getStep.warnings = [
1549
+ 'governance_context persistence could not be verified — agent does not persist media buys across requests',
1550
+ ];
1551
+ }
1552
+ else {
1553
+ getStep.passed = false;
1554
+ getStep.error = `Media buy ${mediaBuyId} not found among ${buys.length} returned buys`;
1555
+ }
1556
+ }
1557
+ else if (getResult && !getResult.success) {
1558
+ getStep.passed = false;
1559
+ getStep.error = getResult.error || 'get_media_buys failed';
1560
+ }
1561
+ steps.push(getStep);
1562
+ await stopStub(stub);
1563
+ return { steps, profile };
1564
+ }
1565
+ /**
1566
+ * Resolve a test account from the seller. Tries list_accounts first,
1567
+ * then sync_accounts, then falls back to a static account reference.
1568
+ */
1569
+ async function resolveTestAccount(client, profile, options) {
1570
+ if (profile.tools.includes('list_accounts')) {
1571
+ try {
1572
+ const result = (await client.executeTask('list_accounts', {}));
1573
+ const accounts = result?.data?.accounts;
1574
+ if (accounts?.length && accounts[0]) {
1575
+ return { account_id: accounts[0].account_id };
1576
+ }
1577
+ }
1578
+ catch {
1579
+ // Fall through
1580
+ }
1581
+ }
1582
+ if (profile.tools.includes('sync_accounts')) {
1583
+ try {
1584
+ const result = (await client.executeTask('sync_accounts', {
1585
+ accounts: [
1586
+ {
1587
+ brand: options.brand || { domain: 'test.example.com' },
1588
+ operator: 'comply-test',
1589
+ billing: 'operator',
1590
+ sandbox: true,
1591
+ },
1592
+ ],
1593
+ }));
1594
+ const accounts = result?.data?.accounts;
1595
+ if (accounts?.length && accounts[0] && accounts[0].account_id) {
1596
+ return { account_id: accounts[0].account_id };
1597
+ }
1598
+ }
1599
+ catch {
1600
+ // Fall through
1601
+ }
1602
+ }
1603
+ return { account_id: 'test-gc-acct' };
1604
+ }
1605
+ async function stopStub(stub) {
1606
+ if (stub) {
1607
+ try {
1608
+ await stub.stop();
1609
+ }
1610
+ catch { }
1611
+ }
1612
+ }
1276
1613
  /**
1277
1614
  * Check if agent has any governance protocol tools
1278
1615
  */