@adcp/sdk 13.0.0-rc.8 → 13.0.0-rc.9

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 (200) hide show
  1. package/dist/lib/adapters/roster-account-store.d.mts +9 -8
  2. package/dist/lib/adapters/roster-account-store.d.ts +9 -8
  3. package/dist/lib/adapters/roster-account-store.d.ts.map +1 -1
  4. package/dist/lib/adapters/roster-account-store.js +2 -1
  5. package/dist/lib/adapters/roster-account-store.js.map +1 -1
  6. package/dist/lib/adapters/roster-account-store.mjs +2 -1
  7. package/dist/lib/adapters/roster-account-store.mjs.map +1 -1
  8. package/dist/lib/net/address-guards.d.ts.map +1 -1
  9. package/dist/lib/net/address-guards.js +6 -0
  10. package/dist/lib/net/address-guards.js.map +1 -1
  11. package/dist/lib/net/address-guards.mjs +6 -0
  12. package/dist/lib/net/address-guards.mjs.map +1 -1
  13. package/dist/lib/net/ssrf-fetch.d.ts.map +1 -1
  14. package/dist/lib/net/ssrf-fetch.js +66 -53
  15. package/dist/lib/net/ssrf-fetch.js.map +1 -1
  16. package/dist/lib/net/ssrf-fetch.mjs +66 -53
  17. package/dist/lib/net/ssrf-fetch.mjs.map +1 -1
  18. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  19. package/dist/lib/server/account-mode.d.mts +2 -1
  20. package/dist/lib/server/account-mode.d.ts +2 -1
  21. package/dist/lib/server/account-mode.d.ts.map +1 -1
  22. package/dist/lib/server/account-mode.js +1 -0
  23. package/dist/lib/server/account-mode.js.map +1 -1
  24. package/dist/lib/server/account-mode.mjs +1 -0
  25. package/dist/lib/server/account-mode.mjs.map +1 -1
  26. package/dist/lib/server/create-adcp-server.d.mts +10 -6
  27. package/dist/lib/server/create-adcp-server.d.ts +10 -6
  28. package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
  29. package/dist/lib/server/create-adcp-server.js +2 -1
  30. package/dist/lib/server/create-adcp-server.js.map +1 -1
  31. package/dist/lib/server/create-adcp-server.mjs +2 -1
  32. package/dist/lib/server/create-adcp-server.mjs.map +1 -1
  33. package/dist/lib/server/decisioning/account.d.mts +16 -11
  34. package/dist/lib/server/decisioning/account.d.ts +16 -11
  35. package/dist/lib/server/decisioning/account.d.ts.map +1 -1
  36. package/dist/lib/server/decisioning/account.js.map +1 -1
  37. package/dist/lib/server/decisioning/account.mjs.map +1 -1
  38. package/dist/lib/server/decisioning/pagination.d.mts +2 -0
  39. package/dist/lib/server/decisioning/pagination.d.ts +2 -0
  40. package/dist/lib/server/decisioning/pagination.d.ts.map +1 -1
  41. package/dist/lib/server/decisioning/pagination.js.map +1 -1
  42. package/dist/lib/server/decisioning/runtime/from-platform.d.mts +20 -14
  43. package/dist/lib/server/decisioning/runtime/from-platform.d.ts +20 -14
  44. package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
  45. package/dist/lib/server/decisioning/runtime/from-platform.js +32 -6
  46. package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
  47. package/dist/lib/server/decisioning/runtime/from-platform.mjs +32 -6
  48. package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
  49. package/dist/lib/server/decisioning/specialisms/sales.d.mts +16 -4
  50. package/dist/lib/server/decisioning/specialisms/sales.d.ts +16 -4
  51. package/dist/lib/server/decisioning/specialisms/sales.d.ts.map +1 -1
  52. package/dist/lib/server/decisioning/specialisms/sales.js.map +1 -1
  53. package/dist/lib/server/index.d.ts.map +1 -1
  54. package/dist/lib/server/index.js.map +1 -1
  55. package/dist/lib/server/index.mjs.map +1 -1
  56. package/dist/lib/server/legacy/v5/index.d.mts +2 -1
  57. package/dist/lib/server/legacy/v5/index.d.ts +2 -1
  58. package/dist/lib/server/legacy/v5/index.d.ts.map +1 -1
  59. package/dist/lib/server/legacy/v5/index.js.map +1 -1
  60. package/dist/lib/server/legacy/v5/index.mjs.map +1 -1
  61. package/dist/lib/testing/compliance/comply.d.mts +2 -0
  62. package/dist/lib/testing/compliance/comply.d.ts +2 -0
  63. package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
  64. package/dist/lib/testing/compliance/comply.js +45 -14
  65. package/dist/lib/testing/compliance/comply.js.map +1 -1
  66. package/dist/lib/testing/compliance/comply.mjs +45 -14
  67. package/dist/lib/testing/compliance/comply.mjs.map +1 -1
  68. package/dist/lib/testing/compliance/index.d.mts +1 -1
  69. package/dist/lib/testing/compliance/index.d.ts +1 -1
  70. package/dist/lib/testing/compliance/index.d.ts.map +1 -1
  71. package/dist/lib/testing/compliance/index.js.map +1 -1
  72. package/dist/lib/testing/compliance/index.mjs.map +1 -1
  73. package/dist/lib/testing/compliance/storyboard-tracks.d.ts.map +1 -1
  74. package/dist/lib/testing/compliance/storyboard-tracks.js +9 -1
  75. package/dist/lib/testing/compliance/storyboard-tracks.js.map +1 -1
  76. package/dist/lib/testing/compliance/storyboard-tracks.mjs +9 -1
  77. package/dist/lib/testing/compliance/storyboard-tracks.mjs.map +1 -1
  78. package/dist/lib/testing/compliance/types.d.mts +26 -25
  79. package/dist/lib/testing/compliance/types.d.ts +26 -25
  80. package/dist/lib/testing/compliance/types.d.ts.map +1 -1
  81. package/dist/lib/testing/compliance/types.js.map +1 -1
  82. package/dist/lib/testing/index.d.mts +2 -2
  83. package/dist/lib/testing/index.d.ts +2 -2
  84. package/dist/lib/testing/index.d.ts.map +1 -1
  85. package/dist/lib/testing/index.js +16 -0
  86. package/dist/lib/testing/index.js.map +1 -1
  87. package/dist/lib/testing/index.mjs +16 -0
  88. package/dist/lib/testing/index.mjs.map +1 -1
  89. package/dist/lib/testing/scenarios/media-buy.d.ts.map +1 -1
  90. package/dist/lib/testing/scenarios/media-buy.js +20 -12
  91. package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
  92. package/dist/lib/testing/scenarios/media-buy.mjs +20 -12
  93. package/dist/lib/testing/scenarios/media-buy.mjs.map +1 -1
  94. package/dist/lib/testing/storyboard/creative-assets.d.mts +23 -0
  95. package/dist/lib/testing/storyboard/creative-assets.d.ts +23 -0
  96. package/dist/lib/testing/storyboard/creative-assets.d.ts.map +1 -1
  97. package/dist/lib/testing/storyboard/creative-assets.js +322 -45
  98. package/dist/lib/testing/storyboard/creative-assets.js.map +1 -1
  99. package/dist/lib/testing/storyboard/creative-assets.mjs +321 -45
  100. package/dist/lib/testing/storyboard/creative-assets.mjs.map +1 -1
  101. package/dist/lib/testing/storyboard/index.d.mts +4 -1
  102. package/dist/lib/testing/storyboard/index.d.ts +4 -1
  103. package/dist/lib/testing/storyboard/index.d.ts.map +1 -1
  104. package/dist/lib/testing/storyboard/index.js +20 -0
  105. package/dist/lib/testing/storyboard/index.js.map +1 -1
  106. package/dist/lib/testing/storyboard/index.mjs +19 -0
  107. package/dist/lib/testing/storyboard/index.mjs.map +1 -1
  108. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.d.mts +21 -0
  109. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.d.ts +22 -0
  110. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.d.ts.map +1 -0
  111. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.js +668 -0
  112. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.js.map +1 -0
  113. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.mjs +638 -0
  114. package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.mjs.map +1 -0
  115. package/dist/lib/testing/storyboard/oauth-metadata-graph/index.d.mts +5 -0
  116. package/dist/lib/testing/storyboard/oauth-metadata-graph/index.d.ts +6 -0
  117. package/dist/lib/testing/storyboard/oauth-metadata-graph/index.d.ts.map +1 -0
  118. package/dist/lib/testing/storyboard/oauth-metadata-graph/index.js +44 -0
  119. package/dist/lib/testing/storyboard/oauth-metadata-graph/index.js.map +1 -0
  120. package/dist/lib/testing/storyboard/oauth-metadata-graph/index.mjs +20 -0
  121. package/dist/lib/testing/storyboard/oauth-metadata-graph/index.mjs.map +1 -0
  122. package/dist/lib/testing/storyboard/oauth-metadata-graph/types.d.mts +38 -0
  123. package/dist/lib/testing/storyboard/oauth-metadata-graph/types.d.ts +39 -0
  124. package/dist/lib/testing/storyboard/oauth-metadata-graph/types.d.ts.map +1 -0
  125. package/dist/lib/testing/storyboard/oauth-metadata-graph/types.js +17 -0
  126. package/dist/lib/testing/storyboard/oauth-metadata-graph/types.js.map +1 -0
  127. package/dist/lib/testing/storyboard/oauth-metadata-graph/types.mjs +1 -0
  128. package/dist/lib/testing/storyboard/oauth-metadata-graph/types.mjs.map +1 -0
  129. package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.d.mts +32 -0
  130. package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.d.ts +33 -0
  131. package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.d.ts.map +1 -0
  132. package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.js +105 -0
  133. package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.js.map +1 -0
  134. package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.mjs +80 -0
  135. package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.mjs.map +1 -0
  136. package/dist/lib/testing/storyboard/probes.d.ts.map +1 -1
  137. package/dist/lib/testing/storyboard/probes.js +2 -1
  138. package/dist/lib/testing/storyboard/probes.js.map +1 -1
  139. package/dist/lib/testing/storyboard/probes.mjs +2 -1
  140. package/dist/lib/testing/storyboard/probes.mjs.map +1 -1
  141. package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
  142. package/dist/lib/testing/storyboard/runner.js +382 -69
  143. package/dist/lib/testing/storyboard/runner.js.map +1 -1
  144. package/dist/lib/testing/storyboard/runner.mjs +387 -70
  145. package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
  146. package/dist/lib/testing/storyboard/seeding.js +3 -1
  147. package/dist/lib/testing/storyboard/seeding.js.map +1 -1
  148. package/dist/lib/testing/storyboard/seeding.mjs +3 -1
  149. package/dist/lib/testing/storyboard/seeding.mjs.map +1 -1
  150. package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -1
  151. package/dist/lib/testing/storyboard/task-map.js +16 -2
  152. package/dist/lib/testing/storyboard/task-map.js.map +1 -1
  153. package/dist/lib/testing/storyboard/task-map.mjs +16 -2
  154. package/dist/lib/testing/storyboard/task-map.mjs.map +1 -1
  155. package/dist/lib/testing/storyboard/trusted-match-context-replay.d.mts +11 -0
  156. package/dist/lib/testing/storyboard/trusted-match-context-replay.d.ts +12 -0
  157. package/dist/lib/testing/storyboard/trusted-match-context-replay.d.ts.map +1 -0
  158. package/dist/lib/testing/storyboard/trusted-match-context-replay.js +340 -0
  159. package/dist/lib/testing/storyboard/trusted-match-context-replay.js.map +1 -0
  160. package/dist/lib/testing/storyboard/trusted-match-context-replay.mjs +316 -0
  161. package/dist/lib/testing/storyboard/trusted-match-context-replay.mjs.map +1 -0
  162. package/dist/lib/testing/storyboard/types.d.mts +84 -15
  163. package/dist/lib/testing/storyboard/types.d.ts +84 -15
  164. package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
  165. package/dist/lib/testing/storyboard/types.js +2 -0
  166. package/dist/lib/testing/storyboard/types.js.map +1 -1
  167. package/dist/lib/testing/storyboard/types.mjs +2 -0
  168. package/dist/lib/testing/storyboard/types.mjs.map +1 -1
  169. package/dist/lib/testing/storyboard/validations.d.mts +3 -0
  170. package/dist/lib/testing/storyboard/validations.d.ts +3 -0
  171. package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
  172. package/dist/lib/testing/storyboard/validations.js +64 -24
  173. package/dist/lib/testing/storyboard/validations.js.map +1 -1
  174. package/dist/lib/testing/storyboard/validations.mjs +64 -24
  175. package/dist/lib/testing/storyboard/validations.mjs.map +1 -1
  176. package/dist/lib/types/server-payload-aliases.d.mts +8 -2
  177. package/dist/lib/types/server-payload-aliases.d.ts +8 -2
  178. package/dist/lib/types/server-payload-aliases.d.ts.map +1 -1
  179. package/dist/lib/types/server-payload-aliases.js.map +1 -1
  180. package/dist/lib/upstream-recorder/recorder.d.mts +7 -18
  181. package/dist/lib/upstream-recorder/recorder.d.ts +7 -18
  182. package/dist/lib/upstream-recorder/recorder.d.ts.map +1 -1
  183. package/dist/lib/upstream-recorder/recorder.js +17 -10
  184. package/dist/lib/upstream-recorder/recorder.js.map +1 -1
  185. package/dist/lib/upstream-recorder/recorder.mjs +12 -5
  186. package/dist/lib/upstream-recorder/recorder.mjs.map +1 -1
  187. package/dist/lib/utils/redact-secrets.d.ts.map +1 -1
  188. package/dist/lib/utils/redact-secrets.js +7 -1
  189. package/dist/lib/utils/redact-secrets.js.map +1 -1
  190. package/dist/lib/utils/redact-secrets.mjs +7 -1
  191. package/dist/lib/utils/redact-secrets.mjs.map +1 -1
  192. package/dist/lib/version.d.mts +3 -3
  193. package/dist/lib/version.d.ts +3 -3
  194. package/dist/lib/version.js +3 -3
  195. package/dist/lib/version.js.map +1 -1
  196. package/dist/lib/version.mjs +3 -3
  197. package/dist/lib/version.mjs.map +1 -1
  198. package/docs/guides/BUILD-AN-AGENT.md +4 -2
  199. package/docs/llms.txt +1 -1
  200. package/package.json +2 -2
@@ -30,7 +30,7 @@ import { IDENTIFIER_DIGEST_LIMIT } from "../../upstream-recorder/constants.mjs";
30
30
  import { enrichRequest, hasRequestEnricher } from "./request-builder.mjs";
31
31
  import {
32
32
  BUILD_ASSETS_FROM_FORMAT_DIRECTIVE,
33
- expandCreativeAssetDirectives,
33
+ expandCreativeAssetDirectivesWithDiagnostics,
34
34
  findUnresolvedCreativeAssetDirectives
35
35
  } from "./creative-assets.mjs";
36
36
  import { resolveAccount, resolveBrand } from "../client.mjs";
@@ -62,6 +62,12 @@ import { runControllerSeeding } from "./seeding.mjs";
62
62
  import { applyFixtureBindingsToRequest } from "./fixture-resolution.mjs";
63
63
  import { getComplianceCacheDir } from "./compliance.mjs";
64
64
  import { signWebhook } from "../../signing/client.mjs";
65
+ import { replayTrustedMatchContextVector } from "./trusted-match-context-replay.mjs";
66
+ import {
67
+ gradeOAuthMetadataGraph,
68
+ redactOAuthUrlForOutput,
69
+ redactOAuthUrlsInText
70
+ } from "./oauth-metadata-graph/index.mjs";
65
71
  import {
66
72
  buildRoutingContext,
67
73
  DiscoveryFailure,
@@ -80,6 +86,7 @@ const SKIP_DETAILS = {
80
86
  prerequisite_failed: "Skipped: a prerequisite step or contract did not pass.",
81
87
  missing_tool: "Skipped: agent did not advertise the required tool.",
82
88
  missing_test_controller: "Skipped: deterministic_testing phase requires comply_test_controller, which the agent did not advertise.",
89
+ fixture_unavailable: "Skipped: the runner test kit cannot synthesize a valid input for the seller-declared contract.",
83
90
  unsatisfied_contract: "Skipped: test-kit contract is out of scope for this grading run.",
84
91
  requirement_unmet: "Skipped: a requires: tag named a runtime requirement that is not available on this run (see RunnerSkipResult.requirement).",
85
92
  peer_branch_taken: "Skipped: a peer branch in the same any_of branch set already contributed the aggregation flag.",
@@ -129,6 +136,7 @@ const DETAILED_SKIP_DETAILS = {
129
136
  rate_limit_not_triggered: "No RATE_LIMITED response was observed within the configured max_attempts."
130
137
  };
131
138
  const REPLAY_WEBHOOK_VECTOR_TASK = "replay_webhook_vector";
139
+ const REPLAY_TRUSTED_MATCH_CONTEXT_VECTOR_TASK = "replay_trusted_match_context_vector";
132
140
  const WEBHOOK_REPLAY_DEFAULT_TIMEOUT_MS = 1e4;
133
141
  function selectionForProbeSkip(reason, detail) {
134
142
  switch (reason) {
@@ -209,6 +217,9 @@ function evaluateRequiresCapabilityGate(predicate, profile, agentTools, adcpVers
209
217
  const actual = resolveCapabilityPathForGate(rawCaps, predicate, adcpVersion);
210
218
  return evaluateCapabilityPredicate(predicate, actual);
211
219
  }
220
+ if ("equals" in predicate && predicate.path === "oauth.supported" && predicate.equals === true) {
221
+ return evaluateCapabilityPredicate(predicate, void 0);
222
+ }
212
223
  const tools = agentTools ?? profile?.tools;
213
224
  if ("equals" in predicate && tools !== void 0 && !tools.includes("get_adcp_capabilities")) {
214
225
  return evaluateCapabilityPredicate(predicate, void 0);
@@ -233,6 +244,24 @@ function allExecutablePhasesCapabilitySkipped(storyboard, phaseCapabilitySkipDet
233
244
  function buildSkip(reason, detail) {
234
245
  return { reason, detail: detail ?? SKIP_DETAILS[reason] };
235
246
  }
247
+ function buildCreativeAssetFixtureUnavailableStep(step, phaseId, context, allSteps, runState, failure, task = step.task) {
248
+ const detail = `creative_asset_fixture_unavailable: slot "${failure.slotId}", asset type "${failure.assetType}", constraint: ${failure.constraint} (${failure.path})`;
249
+ return {
250
+ step_id: step.id,
251
+ phase_id: phaseId,
252
+ title: step.title,
253
+ task,
254
+ passed: true,
255
+ skipped: true,
256
+ skip_reason: "fixture_unavailable",
257
+ skip: buildSkip("fixture_unavailable", detail),
258
+ duration_ms: 0,
259
+ validations: [],
260
+ context,
261
+ next: getNextStepPreview(step.id, allSteps, context, runState.runnerVars),
262
+ extraction: { path: "none" }
263
+ };
264
+ }
236
265
  function detectResponseDerivedNotApplicable(step, request, response, runState, allSteps) {
237
266
  if (response === void 0 || response === null) return null;
238
267
  const gates = [
@@ -516,7 +545,10 @@ function filterResponseHeaders(headers) {
516
545
  if (!headers) return void 0;
517
546
  const out = {};
518
547
  for (const [k, v] of Object.entries(headers)) {
519
- if (RESPONSE_HEADER_ALLOWLIST.has(k.toLowerCase())) out[k] = v;
548
+ const lower = k.toLowerCase();
549
+ if (RESPONSE_HEADER_ALLOWLIST.has(lower)) {
550
+ out[lower] = lower === "location" ? redactOAuthUrlForOutput(v) : v;
551
+ }
520
552
  }
521
553
  return Object.keys(out).length > 0 ? out : void 0;
522
554
  }
@@ -548,15 +580,6 @@ async function runStoryboardBody(agentUrlOrUrls, storyboard, options) {
548
580
  "runStoryboard: _client override is incompatible with multi-instance mode. Remove _client (or pass a single agent URL) to use round-robin dispatch."
549
581
  );
550
582
  }
551
- const requestedStrategy = options.multi_instance_strategy ?? "round-robin";
552
- if (requestedStrategy === "multi-pass" && isMultiInstance) {
553
- if (options.webhook_receiver) {
554
- throw new Error(
555
- 'runStoryboard: webhook_receiver is incompatible with multi_instance_strategy: "multi-pass". Each pass would bind a fresh receiver URL, so agents caching the pass-1 URL would deliver to a dead port in pass 2. Use round-robin when the storyboard needs a webhook receiver, or run multi-pass on a storyboard without webhook observation.'
556
- );
557
- }
558
- return runMultiPass(agentUrls, storyboard, options);
559
- }
560
583
  const allRequires = resolveStoryboardRequires(storyboard, options);
561
584
  if (allRequires.length) {
562
585
  const unmet = checkRequires(allRequires, storyboard, options, options._profile);
@@ -568,6 +591,15 @@ async function runStoryboardBody(agentUrlOrUrls, storyboard, options) {
568
591
  };
569
592
  }
570
593
  }
594
+ const requestedStrategy = options.multi_instance_strategy ?? "round-robin";
595
+ if (requestedStrategy === "multi-pass" && isMultiInstance) {
596
+ if (options.webhook_receiver) {
597
+ throw new Error(
598
+ 'runStoryboard: webhook_receiver is incompatible with multi_instance_strategy: "multi-pass". Each pass would bind a fresh receiver URL, so agents caching the pass-1 URL would deliver to a dead port in pass 2. Use round-robin when the storyboard needs a webhook receiver, or run multi-pass on a storyboard without webhook observation.'
599
+ );
600
+ }
601
+ return runMultiPass(agentUrls, storyboard, options);
602
+ }
571
603
  if (options.agents) {
572
604
  const projected = Object.values(options.agents).map((e) => e.url);
573
605
  return executeStoryboardPass(projected, storyboard, options, 0);
@@ -646,7 +678,7 @@ function buildCapabilityUnsupportedResult(agentUrls, storyboard, detail) {
646
678
  return {
647
679
  storyboard_id: storyboard.id,
648
680
  storyboard_title: storyboard.title,
649
- agent_url: agentUrls[0],
681
+ agent_url: redactOAuthUrlForOutput(agentUrls[0]),
650
682
  overall_passed: true,
651
683
  phases: [
652
684
  {
@@ -701,6 +733,8 @@ const REQUIREMENT_TO_SKIP_REASON = {
701
733
  seeded_state: "requirement_unmet",
702
734
  real_wire: "requirement_unmet",
703
735
  webhook_receiver: "requirement_unmet",
736
+ webhook_replay_receiver: "not_applicable",
737
+ trusted_match_context_router_runner: "not_applicable",
704
738
  request_signer: "not_applicable",
705
739
  multi_agent: "requirement_unmet"
706
740
  };
@@ -727,7 +761,7 @@ function buildRequirementUnmetResult(agentUrls, storyboard, requirement, detail)
727
761
  return {
728
762
  storyboard_id: storyboard.id,
729
763
  storyboard_title: storyboard.title,
730
- agent_url: agentUrls[0],
764
+ agent_url: redactOAuthUrlForOutput(agentUrls[0]),
731
765
  overall_passed: true,
732
766
  phases: [
733
767
  {
@@ -777,7 +811,7 @@ function buildRequiredAnyOfToolsMissingResult(agentUrls, storyboard, detail) {
777
811
  return {
778
812
  storyboard_id: storyboard.id,
779
813
  storyboard_title: storyboard.title,
780
- agent_url: agentUrls[0],
814
+ agent_url: redactOAuthUrlForOutput(agentUrls[0]),
781
815
  overall_passed: true,
782
816
  phases: [
783
817
  {
@@ -857,6 +891,25 @@ function checkRequires(requires, storyboard, options, profile) {
857
891
  }
858
892
  break;
859
893
  }
894
+ case "webhook_replay_receiver": {
895
+ if (!options.webhook_replay_receiver?.url) {
896
+ return {
897
+ requirement,
898
+ detail: "Storyboard contains a `replay_webhook_vector` step but no webhook replay receiver URL is configured. Pass `webhook_replay_receiver.url` in StoryboardRunOptions to run inbound buyer/orchestrator receiver conformance. Seller agents without an inbound receiver grade not_applicable for this storyboard."
899
+ };
900
+ }
901
+ break;
902
+ }
903
+ case "trusted_match_context_router_runner": {
904
+ const runner = options.trusted_match_context_router_runner;
905
+ if (!runner?.router_url || typeof runner.registerProviders !== "function") {
906
+ return {
907
+ requirement,
908
+ detail: "Storyboard contains a `replay_trusted_match_context_vector` step but no Trusted Match Context router harness is configured. Pass `trusted_match_context_router_runner` with `router_url` and `registerProviders` to run raw POST /context conformance."
909
+ };
910
+ }
911
+ break;
912
+ }
860
913
  case "request_signer": {
861
914
  if (!profile?.raw_capabilities) break;
862
915
  const supported = resolveCapabilityPath(profile.raw_capabilities, "request_signing.supported");
@@ -918,22 +971,32 @@ function valueContainsWebhookToken(value) {
918
971
  function detectImplicitRequires(storyboard, options = {}) {
919
972
  const requires = [];
920
973
  let needsWebhook = false;
974
+ let needsWebhookReplayReceiver = false;
975
+ let needsTrustedMatchContextRouterRunner = false;
921
976
  let needsSigner = storyboard.id === "signed_requests";
922
977
  const contractsInScope = new Set(options.contracts ?? []);
923
978
  for (const phase of storyboard.phases) {
924
979
  for (const step of phase.steps) {
925
- const rateLimitTripInScope = !step.requires_contract || contractsInScope.has(step.requires_contract);
926
- if (!needsWebhook && (step.sample_request && valueContainsWebhookToken(step.sample_request) || step.rate_limit_trip?.trip_target_sample_request && rateLimitTripInScope && valueContainsWebhookToken(step.rate_limit_trip.trip_target_sample_request))) {
980
+ const stepContractInScope = !step.requires_contract || contractsInScope.has(step.requires_contract);
981
+ if (!needsWebhook && (step.sample_request && valueContainsWebhookToken(step.sample_request) || step.rate_limit_trip?.trip_target_sample_request && stepContractInScope && valueContainsWebhookToken(step.rate_limit_trip.trip_target_sample_request))) {
927
982
  needsWebhook = true;
928
983
  }
929
984
  if (!needsSigner && step.task === "request_signing_probe") {
930
985
  needsSigner = true;
931
986
  }
932
- if (needsWebhook && needsSigner) break;
987
+ if (!needsWebhookReplayReceiver && stepContractInScope && step.task === REPLAY_WEBHOOK_VECTOR_TASK) {
988
+ needsWebhookReplayReceiver = true;
989
+ }
990
+ if (!needsTrustedMatchContextRouterRunner && stepContractInScope && step.task === REPLAY_TRUSTED_MATCH_CONTEXT_VECTOR_TASK) {
991
+ needsTrustedMatchContextRouterRunner = true;
992
+ }
993
+ if (needsWebhook && needsWebhookReplayReceiver && needsTrustedMatchContextRouterRunner && needsSigner) break;
933
994
  }
934
- if (needsWebhook && needsSigner) break;
995
+ if (needsWebhook && needsWebhookReplayReceiver && needsTrustedMatchContextRouterRunner && needsSigner) break;
935
996
  }
936
997
  if (needsWebhook) requires.push("webhook_receiver");
998
+ if (needsWebhookReplayReceiver) requires.push("webhook_replay_receiver");
999
+ if (needsTrustedMatchContextRouterRunner) requires.push("trusted_match_context_router_runner");
937
1000
  if (needsSigner) requires.push("request_signer");
938
1001
  return requires;
939
1002
  }
@@ -956,7 +1019,7 @@ function buildDiscoveryFailedResult(agentUrls, storyboard, discoveryStep) {
956
1019
  return {
957
1020
  storyboard_id: storyboard.id,
958
1021
  storyboard_title: storyboard.title,
959
- agent_url: agentUrls[0],
1022
+ agent_url: redactOAuthUrlForOutput(agentUrls[0]),
960
1023
  overall_passed: false,
961
1024
  phases: [
962
1025
  {
@@ -988,7 +1051,7 @@ function buildRequiredToolsMissingResult(agentUrls, storyboard, detail) {
988
1051
  return {
989
1052
  storyboard_id: storyboard.id,
990
1053
  storyboard_title: storyboard.title,
991
- agent_url: agentUrls[0],
1054
+ agent_url: redactOAuthUrlForOutput(agentUrls[0]),
992
1055
  overall_passed: true,
993
1056
  phases: [
994
1057
  {
@@ -1330,6 +1393,33 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1330
1393
  storyboard,
1331
1394
  phaseCapabilitySkipDetails
1332
1395
  );
1396
+ const capabilitySkippedPhaseIds = /* @__PURE__ */ new Set([
1397
+ ...phaseCapabilitySkipDetails.keys(),
1398
+ ...storyboard.phases.filter((phase) => shouldSkipPhase(phase, options)).map((phase) => phase.id)
1399
+ ]);
1400
+ let creativeAssetFixtureGap = preflightRemainingCreativeAssetDirectives(
1401
+ allSteps,
1402
+ -1,
1403
+ context,
1404
+ options,
1405
+ {
1406
+ contributions,
1407
+ priorStepResults,
1408
+ priorProbes,
1409
+ agentUrl: agentUrls[0],
1410
+ webhookReceiver,
1411
+ runnerVars,
1412
+ contextProvenance,
1413
+ priorA2aEnvelopes,
1414
+ stepRequestStarts,
1415
+ responseDerivedNotApplicableContextKeys,
1416
+ agentProfile: profile,
1417
+ agentLibraryVersion: profile?.library_version,
1418
+ storyboardRequiresRequestSigner: allRequires.includes("request_signer")
1419
+ },
1420
+ capabilitySkippedPhaseIds
1421
+ );
1422
+ let creativeAssetFixtureGapRecorded = false;
1333
1423
  if (!hasExecutableSteps) {
1334
1424
  const isScenarioComposed = (storyboard.requires_scenarios?.length ?? 0) > 0;
1335
1425
  const detail = isScenarioComposed ? `Storyboard "${storyboard.id}" has no local phases \u2014 its test surface is fully composed from the scenarios listed in \`requires_scenarios\`.` : `Storyboard "${storyboard.id}" has no executable phases \u2014 populate \`phases[].steps\` or remove the storyboard.`;
@@ -1387,7 +1477,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1387
1477
  fixtureDiscoveryClient = dispatch.nextFor(fixtureStep("get_products")).client;
1388
1478
  }
1389
1479
  }
1390
- const seeding = skipControllerSeedingForPhaseGates ? null : preSeeded !== void 0 ? preSeeded.result : await runControllerSeeding(fixtureSeedClient, storyboard, options, context, fixtureDiscoveryClient);
1480
+ const seeding = creativeAssetFixtureGap ? null : skipControllerSeedingForPhaseGates ? null : preSeeded !== void 0 ? preSeeded.result : await runControllerSeeding(fixtureSeedClient, storyboard, options, context, fixtureDiscoveryClient);
1391
1481
  if (seeding) {
1392
1482
  const attach = preSeeded === void 0 || preSeeded.attach;
1393
1483
  if (attach) {
@@ -1435,6 +1525,32 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1435
1525
  }
1436
1526
  }
1437
1527
  }
1528
+ const buildExecutionState = (agentUrl = agentUrls[0], agentProfile = profile) => ({
1529
+ contributions,
1530
+ priorStepResults,
1531
+ priorProbes,
1532
+ agentUrl,
1533
+ webhookReceiver,
1534
+ runnerVars,
1535
+ contextProvenance,
1536
+ priorA2aEnvelopes,
1537
+ stepRequestStarts,
1538
+ responseDerivedNotApplicableContextKeys,
1539
+ agentProfile,
1540
+ agentLibraryVersion: agentProfile?.library_version,
1541
+ storyboardRequiresRequestSigner: allRequires.includes("request_signer"),
1542
+ fixtureBindings
1543
+ });
1544
+ if (!creativeAssetFixtureGap && !seedingMissingController && !seedingUnsupported && !seedingFailed && !fixtureUnsatisfied) {
1545
+ creativeAssetFixtureGap = preflightRemainingCreativeAssetDirectives(
1546
+ allSteps,
1547
+ -1,
1548
+ context,
1549
+ options,
1550
+ buildExecutionState(),
1551
+ capabilitySkippedPhaseIds
1552
+ );
1553
+ }
1438
1554
  for (const phase of storyboard.phases) {
1439
1555
  options.signal?.throwIfAborted();
1440
1556
  const phaseStart = Date.now();
@@ -1448,6 +1564,33 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1448
1564
  });
1449
1565
  continue;
1450
1566
  }
1567
+ if (creativeAssetFixtureGap) {
1568
+ const gapSteps = [];
1569
+ if (!creativeAssetFixtureGapRecorded && creativeAssetFixtureGap.target.phaseId === phase.id) {
1570
+ const gapStep = buildCreativeAssetFixtureUnavailableStep(
1571
+ creativeAssetFixtureGap.target.step,
1572
+ phase.id,
1573
+ context,
1574
+ allSteps,
1575
+ buildExecutionState(),
1576
+ creativeAssetFixtureGap.failure
1577
+ );
1578
+ gapStep.storyboard_id = storyboard.id;
1579
+ gapSteps.push(gapStep);
1580
+ priorStepResults.set(gapStep.step_id, gapStep);
1581
+ skippedCount++;
1582
+ creativeAssetFixtureGapRecorded = true;
1583
+ }
1584
+ phaseResults.push({
1585
+ phase_id: phase.id,
1586
+ phase_title: phase.title,
1587
+ passed: true,
1588
+ steps: gapSteps,
1589
+ duration_ms: 0
1590
+ });
1591
+ priorPhaseIds.push(phase.id);
1592
+ continue;
1593
+ }
1451
1594
  const phaseCapabilitySkipDetail = phaseCapabilitySkipDetails.get(phase.id);
1452
1595
  if (phaseCapabilitySkipDetail !== void 0) {
1453
1596
  const skippedSteps = buildPhaseCapabilitySkippedSteps(storyboard, phase, phaseCapabilitySkipDetail, context);
@@ -1631,6 +1774,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1631
1774
  phasePassed = false;
1632
1775
  continue;
1633
1776
  }
1777
+ const stepExecutionState = buildExecutionState(assignment.agentUrl, assignment.profile);
1634
1778
  const rawResult = await executeStep(
1635
1779
  assignment.client,
1636
1780
  step,
@@ -1639,26 +1783,11 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1639
1783
  context,
1640
1784
  allSteps,
1641
1785
  options,
1642
- {
1643
- contributions,
1644
- priorStepResults,
1645
- priorProbes,
1646
- agentUrl: assignment.agentUrl,
1647
- webhookReceiver,
1648
- runnerVars,
1649
- contextProvenance,
1650
- priorA2aEnvelopes,
1651
- stepRequestStarts,
1652
- responseDerivedNotApplicableContextKeys,
1653
- agentProfile: assignment.profile,
1654
- agentLibraryVersion: assignment.profile?.library_version,
1655
- storyboardRequiresRequestSigner: allRequires.includes("request_signer"),
1656
- fixtureBindings
1657
- }
1786
+ stepExecutionState
1658
1787
  );
1659
1788
  const result2 = { ...rawResult, storyboard_id: storyboard.id };
1660
1789
  if (isMultiInstance || useRouting) {
1661
- result2.agent_url = assignment.agentUrl;
1790
+ result2.agent_url = redactOAuthUrlForOutput(assignment.agentUrl);
1662
1791
  result2.agent_index = assignment.instanceIndex + 1;
1663
1792
  }
1664
1793
  stepResults.push(result2);
@@ -1777,6 +1906,39 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1777
1906
  annotateMultiInstanceFailure(result2, storyboard, stepResults);
1778
1907
  }
1779
1908
  }
1909
+ if (!result2.skipped && result2.passed) {
1910
+ const currentGlobalIndex = allSteps.find(
1911
+ (flat) => flat.phaseId === phase.id && flat.step.id === step.id
1912
+ )?.globalIndex;
1913
+ if (currentGlobalIndex !== void 0) {
1914
+ creativeAssetFixtureGap = preflightRemainingCreativeAssetDirectives(
1915
+ allSteps,
1916
+ currentGlobalIndex,
1917
+ context,
1918
+ options,
1919
+ stepExecutionState,
1920
+ capabilitySkippedPhaseIds
1921
+ );
1922
+ }
1923
+ if (creativeAssetFixtureGap) {
1924
+ if (creativeAssetFixtureGap.target.phaseId === phase.id) {
1925
+ const gapStep = buildCreativeAssetFixtureUnavailableStep(
1926
+ creativeAssetFixtureGap.target.step,
1927
+ phase.id,
1928
+ context,
1929
+ allSteps,
1930
+ stepExecutionState,
1931
+ creativeAssetFixtureGap.failure
1932
+ );
1933
+ gapStep.storyboard_id = storyboard.id;
1934
+ stepResults.push(gapStep);
1935
+ priorStepResults.set(gapStep.step_id, gapStep);
1936
+ skippedCount++;
1937
+ creativeAssetFixtureGapRecorded = true;
1938
+ }
1939
+ break;
1940
+ }
1941
+ }
1780
1942
  }
1781
1943
  if (phasePendingNotApplicable && !phaseEstablishedStatefulState && !phaseStatefulCascades.has(phase.id)) {
1782
1944
  const hadStatefulPeers = phaseStatefulStepIds.some((id) => id !== phasePendingNotApplicable.stepId);
@@ -1820,7 +1982,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1820
1982
  });
1821
1983
  priorPhaseIds.push(phase.id);
1822
1984
  }
1823
- const branchSetDelta = applyBranchSetGrading(
1985
+ const branchSetDelta = creativeAssetFixtureGap ? { skippedDelta: 0 } : applyBranchSetGrading(
1824
1986
  storyboard.phases,
1825
1987
  phaseResults,
1826
1988
  branchSetsByPhaseId,
@@ -1829,12 +1991,14 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1829
1991
  countedAsFailed
1830
1992
  );
1831
1993
  skippedCount += branchSetDelta.skippedDelta;
1832
- for (const spec of assertions) {
1833
- if (!spec.onEnd) continue;
1834
- const raw = await spec.onEnd(assertionContexts.get(spec.id));
1835
- for (const r of raw) {
1836
- assertionResults.push({ ...r, assertion_id: spec.id, scope: "storyboard" });
1837
- if (!r.passed) assertionsFailed = true;
1994
+ if (!creativeAssetFixtureGap) {
1995
+ for (const spec of assertions) {
1996
+ if (!spec.onEnd) continue;
1997
+ const raw = await spec.onEnd(assertionContexts.get(spec.id));
1998
+ for (const r of raw) {
1999
+ assertionResults.push({ ...r, assertion_id: spec.id, scope: "storyboard" });
2000
+ if (!r.passed) assertionsFailed = true;
2001
+ }
1838
2002
  }
1839
2003
  }
1840
2004
  const requiredPhaseHasExecutedPass = phaseResults.some((p, idx) => {
@@ -1843,13 +2007,17 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1843
2007
  return p.steps.some((s) => !s.skipped && s.passed);
1844
2008
  });
1845
2009
  const requiredPhaseDefs = storyboard.phases.filter((phaseDef) => !phaseDef.optional);
2010
+ const requiredPhasesNotApplicable = requiredPhaseDefs.length > 0 && requiredPhaseDefs.every((phaseDef) => {
2011
+ const phaseResult = phaseResults.find((p) => p.phase_id === phaseDef.id);
2012
+ return !!phaseResult && phaseResult.passed && phaseResult.steps.length > 0 && phaseResult.steps.every((step) => step.skipped && step.skip?.reason === "not_applicable");
2013
+ });
1846
2014
  const requiredPhasesCoveredByCapabilityGates = phaseCapabilitySkippedIds.size > 0 && requiredPhaseDefs.length > 0 && requiredPhaseDefs.every((phaseDef) => {
1847
2015
  if (phaseCapabilitySkippedIds.has(phaseDef.id)) return true;
1848
2016
  const phaseResult = phaseResults.find((p) => p.phase_id === phaseDef.id);
1849
2017
  return !!phaseResult && phaseResult.passed && phaseResult.steps.some((s) => !s.skipped && s.passed);
1850
2018
  });
1851
- const requiredPhasesPassed = !hasExecutableSteps || requiredPhaseHasExecutedPass || failedCount === 0 && requiredPhasesCoveredByCapabilityGates;
1852
- const storyboardWideFixtureUnavailable = (seedingUnsupported || fixtureUnsatisfied) && failedCount === 0;
2019
+ const requiredPhasesPassed = !hasExecutableSteps || requiredPhaseHasExecutedPass || requiredPhasesNotApplicable || failedCount === 0 && requiredPhasesCoveredByCapabilityGates;
2020
+ const storyboardWideFixtureUnavailable = (seedingUnsupported || fixtureUnsatisfied || creativeAssetFixtureGap !== void 0) && failedCount === 0;
1853
2021
  if (seedingPhaseResult) phaseResults.unshift(seedingPhaseResult);
1854
2022
  const schemasUsed = collectSchemasUsed(phaseResults);
1855
2023
  const strictSummary = summarizeStrictValidation(phaseResults);
@@ -1861,13 +2029,17 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1861
2029
  const result = {
1862
2030
  storyboard_id: storyboard.id,
1863
2031
  storyboard_title: storyboard.title,
1864
- agent_url: agentUrls[0],
1865
- ...isMultiInstance && { agent_urls: [...agentUrls] },
2032
+ agent_url: redactOAuthUrlForOutput(agentUrls[0]),
2033
+ ...isMultiInstance && { agent_urls: agentUrls.map(redactOAuthUrlForOutput) },
1866
2034
  // Inner multi-pass passes surface as `round-robin` (that's what they are
1867
2035
  // individually); the aggregating wrapper relabels the top-level result
1868
2036
  // `multi-pass`.
1869
2037
  ...isMultiInstance && { multi_instance_strategy: "round-robin" },
1870
- ...routingContext && { agent_map: { ...routingContext.agentMap } },
2038
+ ...routingContext && {
2039
+ agent_map: Object.fromEntries(
2040
+ Object.entries(routingContext.agentMap).map(([specialism, url]) => [specialism, redactOAuthUrlForOutput(url)])
2041
+ )
2042
+ },
1871
2043
  overall_passed: failedCount === 0 && (requiredPhasesPassed || storyboardWideFixtureUnavailable) && !assertionsFailed,
1872
2044
  phases: phaseResults,
1873
2045
  context,
@@ -1886,12 +2058,8 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1886
2058
  ...routingContext && routingContext.discoveryFailures.length > 0 ? {
1887
2059
  discovery_failures: routingContext.discoveryFailures.map((f) => ({
1888
2060
  agent_key: f.agentKey,
1889
- // Scrub URL userinfo (`https://user:pass@host/`) before
1890
- // echoing onto the result — an operator may legitimately
1891
- // encode credentials in the URL, and the leaderboard /
1892
- // dashboard surface mustn't carry them.
1893
- url: f.url.replace(/\/\/[^/@\s]+@/, "//[REDACTED]@"),
1894
- error: f.underlying
2061
+ url: redactOAuthUrlForOutput(f.url),
2062
+ error: redactOAuthUrlsInText(f.underlying)
1895
2063
  }))
1896
2064
  } : {}
1897
2065
  };
@@ -1929,7 +2097,24 @@ async function runMultiPass(agentUrls, storyboard, options) {
1929
2097
  options.agentTools,
1930
2098
  options.adcpVersion
1931
2099
  );
1932
- const preSeededResult = allExecutablePhasesCapabilitySkipped(storyboard, phaseCapabilitySkipDetails) ? null : await runControllerSeeding(preSeedClients[0], storyboard, options, preSeedContext);
2100
+ const preSeedExcludedPhaseIds = /* @__PURE__ */ new Set([
2101
+ ...phaseCapabilitySkipDetails.keys(),
2102
+ ...storyboard.phases.filter((phase) => shouldSkipPhase(phase, options)).map((phase) => phase.id)
2103
+ ]);
2104
+ const preSeedFixtureGap = preflightRemainingCreativeAssetDirectives(
2105
+ flattenSteps(storyboard),
2106
+ -1,
2107
+ preSeedContext,
2108
+ options,
2109
+ {
2110
+ contributions: /* @__PURE__ */ new Set(),
2111
+ priorStepResults: /* @__PURE__ */ new Map(),
2112
+ priorProbes: /* @__PURE__ */ new Map(),
2113
+ agentUrl: agentUrls[0]
2114
+ },
2115
+ preSeedExcludedPhaseIds
2116
+ );
2117
+ const preSeededResult = preSeedFixtureGap || allExecutablePhasesCapabilitySkipped(storyboard, phaseCapabilitySkipDetails) ? null : await runControllerSeeding(preSeedClients[0], storyboard, options, preSeedContext);
1933
2118
  const passes = [];
1934
2119
  const passResults = [];
1935
2120
  for (let passIdx = 0; passIdx < agentUrls.length; passIdx++) {
@@ -1961,8 +2146,8 @@ async function runMultiPass(agentUrls, storyboard, options) {
1961
2146
  return {
1962
2147
  storyboard_id: storyboard.id,
1963
2148
  storyboard_title: storyboard.title,
1964
- agent_url: agentUrls[0],
1965
- agent_urls: [...agentUrls],
2149
+ agent_url: redactOAuthUrlForOutput(agentUrls[0]),
2150
+ agent_urls: agentUrls.map(redactOAuthUrlForOutput),
1966
2151
  multi_instance_strategy: "multi-pass",
1967
2152
  overall_passed: overallPassed,
1968
2153
  phases: first.phases,
@@ -2287,7 +2472,19 @@ async function executeStep(client, step, storyboardId, phaseId, context, allStep
2287
2472
  };
2288
2473
  }
2289
2474
  request = fixtureBinding.request;
2290
- request = expandCreativeAssetDirectives(request, context, options.test_kit);
2475
+ const creativeAssetExpansion = expandCreativeAssetDirectivesWithDiagnostics(request, context, options.test_kit);
2476
+ request = creativeAssetExpansion.value;
2477
+ if (!creativeAssetExpansion.ok && creativeAssetExpansion.failure.reason === "fixture_unavailable") {
2478
+ return buildCreativeAssetFixtureUnavailableStep(
2479
+ step,
2480
+ phaseId,
2481
+ context,
2482
+ allSteps,
2483
+ runState,
2484
+ creativeAssetExpansion.failure,
2485
+ effectiveStep.task
2486
+ );
2487
+ }
2291
2488
  const unresolvedContextVars = findUnresolvedContextVars(request);
2292
2489
  const unresolvedAssetDirectives = findUnresolvedCreativeAssetDirectives(request).map((path) => ({
2293
2490
  key: path,
@@ -2359,7 +2556,7 @@ async function executeStep(client, step, storyboardId, phaseId, context, allStep
2359
2556
  transport: options.protocol === "a2a" ? "a2a" : "mcp",
2360
2557
  operation: effectiveStep.task,
2361
2558
  payload: redactSecrets(request),
2362
- ...runState.agentUrl ? { url: runState.agentUrl } : {}
2559
+ ...runState.agentUrl ? { url: redactOAuthUrlForOutput(runState.agentUrl) } : {}
2363
2560
  },
2364
2561
  extraction: { path: "none" }
2365
2562
  };
@@ -2554,7 +2751,7 @@ async function executeStep(client, step, storyboardId, phaseId, context, allStep
2554
2751
  transport: useRawProbe ? "mcp" : options.protocol === "a2a" ? "a2a" : "mcp",
2555
2752
  operation: effectiveStep.task,
2556
2753
  payload: redactSecrets(request),
2557
- ...runState.agentUrl ? { url: runState.agentUrl } : {}
2754
+ ...runState.agentUrl ? { url: redactOAuthUrlForOutput(runState.agentUrl) } : {}
2558
2755
  };
2559
2756
  const inputSchemaStripNotices = collectInputSchemaFieldStripNotices(
2560
2757
  taskResult?.debug_logs,
@@ -2910,6 +3107,7 @@ async function executeStep(client, step, storyboardId, phaseId, context, allStep
2910
3107
  async function executeProbeStep(client, step, phaseId, context, allSteps, options, runState) {
2911
3108
  const start = Date.now();
2912
3109
  let httpResult;
3110
+ let oauthMetadataGraph;
2913
3111
  const probeOpts = {
2914
3112
  allowPrivateIp: options.allow_http === true,
2915
3113
  fetchFn: options.transport?.fetchFn
@@ -2931,8 +3129,17 @@ async function executeProbeStep(client, step, phaseId, context, allSteps, option
2931
3129
  }
2932
3130
  if (httpResult) {
2933
3131
  } else if (step.task === "protected_resource_metadata") {
2934
- httpResult = await probeProtectedResourceMetadata(runState.agentUrl, probeOpts);
2935
- if (!httpResult.error && httpResult.status === 404) {
3132
+ if (step.validations?.some((validation) => validation.check === "oauth_metadata_graph")) {
3133
+ oauthMetadataGraph = await gradeOAuthMetadataGraph(runState.agentUrl, {
3134
+ allowHttp: options.allow_http === true,
3135
+ signal: options.signal,
3136
+ trustedFetchFn: options.transport?.fetchFn
3137
+ });
3138
+ httpResult = oauthMetadataGraph.protected_resource_result;
3139
+ } else {
3140
+ httpResult = await probeProtectedResourceMetadata(runState.agentUrl, probeOpts);
3141
+ }
3142
+ if (!oauthMetadataGraph && !httpResult.error && httpResult.status === 404) {
2936
3143
  httpResult.skipped = true;
2937
3144
  httpResult.skip_reason = "oauth_not_advertised";
2938
3145
  }
@@ -2970,6 +3177,16 @@ async function executeProbeStep(client, step, phaseId, context, allSteps, option
2970
3177
  };
2971
3178
  const targetRequestResult = buildEffectiveStepRequest(targetStep, context, options, runState);
2972
3179
  if (!targetRequestResult.ok) {
3180
+ if ("creativeAssetFailure" in targetRequestResult) {
3181
+ return buildCreativeAssetFixtureUnavailableStep(
3182
+ step,
3183
+ phaseId,
3184
+ context,
3185
+ allSteps,
3186
+ runState,
3187
+ targetRequestResult.creativeAssetFailure
3188
+ );
3189
+ }
2973
3190
  const next = getNextStepPreview(step.id, allSteps, context, runState.runnerVars);
2974
3191
  return {
2975
3192
  step_id: step.id,
@@ -3083,7 +3300,34 @@ async function executeProbeStep(client, step, phaseId, context, allSteps, option
3083
3300
  transport: targetTransport,
3084
3301
  operation: rateLimitTrip.trip_target_task,
3085
3302
  payload: redactSecrets(observedRequest),
3086
- ...runState.agentUrl ? { url: runState.agentUrl } : {}
3303
+ ...runState.agentUrl ? { url: redactOAuthUrlForOutput(runState.agentUrl) } : {}
3304
+ };
3305
+ }
3306
+ } else if (step.task === REPLAY_TRUSTED_MATCH_CONTEXT_VECTOR_TASK) {
3307
+ const routerRunner = options.trusted_match_context_router_runner;
3308
+ if (!routerRunner?.router_url || typeof routerRunner.registerProviders !== "function") {
3309
+ httpResult = {
3310
+ url: routerRunner?.router_url ?? runState.agentUrl,
3311
+ status: 0,
3312
+ headers: {},
3313
+ body: null,
3314
+ skipped: true,
3315
+ skip_reason: "grader_skipped",
3316
+ error: "No Trusted Match Context router harness configured. Pass trusted_match_context_router_runner with router_url and registerProviders; the runner will not dispatch this pseudo-task over MCP/A2A."
3317
+ };
3318
+ } else {
3319
+ const requestOverride = options.request ? injectContext({ ...options.request }, context, runState.runnerVars) : step.sample_request ? injectContext({ ...step.sample_request }, context, runState.runnerVars) : void 0;
3320
+ const replay = await replayTrustedMatchContextVector(step, routerRunner, {
3321
+ ...requestOverride && { requestOverride },
3322
+ ...options.adcpVersion && { adcpVersion: options.adcpVersion },
3323
+ ...options.signal && { signal: options.signal }
3324
+ });
3325
+ httpResult = replay.httpResult;
3326
+ requestRecordOverride = {
3327
+ transport: "http",
3328
+ operation: step.task,
3329
+ payload: redactSecrets(replay.request),
3330
+ url: redactOAuthUrlForOutput(replay.httpResult.url)
3087
3331
  };
3088
3332
  }
3089
3333
  }
@@ -3093,7 +3337,7 @@ async function executeProbeStep(client, step, phaseId, context, allSteps, option
3093
3337
  transport: "http",
3094
3338
  operation: step.task,
3095
3339
  payload: null,
3096
- ...httpResult?.url ? { url: httpResult.url } : runState.agentUrl ? { url: runState.agentUrl } : {}
3340
+ ...httpResult?.url ? { url: redactOAuthUrlForOutput(httpResult.url) } : runState.agentUrl ? { url: redactOAuthUrlForOutput(runState.agentUrl) } : {}
3097
3341
  };
3098
3342
  const filteredProbeHeaders = filterResponseHeaders(httpResult?.headers);
3099
3343
  const responseTransport = requestRecordOverride?.transport ?? "http";
@@ -3106,6 +3350,7 @@ async function executeProbeStep(client, step, phaseId, context, allSteps, option
3106
3350
  } : void 0;
3107
3351
  const redactedHttpResult = httpResult ? {
3108
3352
  ...httpResult,
3353
+ headers: filteredProbeHeaders ?? {},
3109
3354
  body: redactSecrets(httpResult.body),
3110
3355
  ...responseTransport !== "http" && { status: void 0 }
3111
3356
  } : void 0;
@@ -3135,15 +3380,24 @@ async function executeProbeStep(client, step, phaseId, context, allSteps, option
3135
3380
  };
3136
3381
  }
3137
3382
  const vctx = {
3138
- taskName: step.task,
3383
+ taskName: step.task === REPLAY_TRUSTED_MATCH_CONTEXT_VECTOR_TASK ? "context_match" : step.task,
3139
3384
  ...options.adcpVersion && { adcpVersion: options.adcpVersion },
3140
3385
  ...options._serverAdcpVersion && { responseAdcpVersion: options._serverAdcpVersion },
3141
3386
  httpResult: redactedHttpResult,
3387
+ ...step.task === REPLAY_TRUSTED_MATCH_CONTEXT_VECTOR_TASK && redactedHttpResult && {
3388
+ taskResult: {
3389
+ success: !redactedHttpResult.error && redactedHttpResult.status >= 200 && redactedHttpResult.status < 300,
3390
+ data: redactedHttpResult.body,
3391
+ ...redactedHttpResult.error && { error: redactedHttpResult.error }
3392
+ }
3393
+ },
3142
3394
  agentUrl: runState.agentUrl,
3143
3395
  contributions: runState.contributions,
3396
+ ...step.response_schema_ref && { responseSchemaRef: step.response_schema_ref },
3144
3397
  request: requestRecord,
3145
3398
  ...responseRecord && { response: responseRecord },
3146
- storyboardContext: context
3399
+ storyboardContext: context,
3400
+ ...oauthMetadataGraph && { oauthMetadataGraph }
3147
3401
  };
3148
3402
  const validations = step.validations?.length ? runValidations(step.validations, vctx) : [];
3149
3403
  const allValidationsPassed = validations.every((v) => v.passed);
@@ -3205,13 +3459,73 @@ function buildEffectiveStepRequest(step, context, options, runState) {
3205
3459
  request = applyIdempotencyInvariant(request, step.task, step);
3206
3460
  const fixtureBinding = applyFixtureBindingsSafely(request, step.task, options, runState);
3207
3461
  if (!fixtureBinding.ok) return fixtureBinding;
3462
+ const creativeAssetExpansion = expandCreativeAssetDirectivesWithDiagnostics(
3463
+ fixtureBinding.request,
3464
+ context,
3465
+ options.test_kit
3466
+ );
3467
+ if (!creativeAssetExpansion.ok && creativeAssetExpansion.failure.reason === "fixture_unavailable") {
3468
+ return { ok: false, creativeAssetFailure: creativeAssetExpansion.failure };
3469
+ }
3208
3470
  return {
3209
3471
  ok: true,
3210
- request: expandCreativeAssetDirectives(fixtureBinding.request, context, options.test_kit)
3472
+ request: creativeAssetExpansion.value
3211
3473
  };
3212
3474
  }
3475
+ function preflightRemainingCreativeAssetDirectives(allSteps, afterGlobalIndex, context, options, runState, excludedPhaseIds = /* @__PURE__ */ new Set()) {
3476
+ for (const target of allSteps) {
3477
+ if (target.globalIndex <= afterGlobalIndex) continue;
3478
+ if (excludedPhaseIds.has(target.phaseId)) continue;
3479
+ const resolvedTask = resolveTaskName(target.step, options);
3480
+ if (!resolvedTask) continue;
3481
+ if (target.step.requires_tool && options.agentTools && !options.agentTools.includes(target.step.requires_tool)) {
3482
+ continue;
3483
+ }
3484
+ if (target.step.requires_contract && !new Set(options.contracts ?? []).has(target.step.requires_contract)) {
3485
+ continue;
3486
+ }
3487
+ if (!PROBE_TASKS.has(target.step.task) && options.agentTools && !options.agentTools.includes(resolvedTask)) {
3488
+ continue;
3489
+ }
3490
+ let requestStep = resolvedTask === target.step.task ? target.step : { ...target.step, task: resolvedTask };
3491
+ if (target.step.task === "expect_rate_limit_not_replayed") {
3492
+ const rateLimitTrip = target.step.rate_limit_trip;
3493
+ if (validateRateLimitTripSpec(rateLimitTrip) || !rateLimitTrip) continue;
3494
+ requestStep = {
3495
+ ...target.step,
3496
+ task: rateLimitTrip.trip_target_task,
3497
+ sample_request: rateLimitTrip.trip_target_sample_request,
3498
+ omit_idempotency_key: true
3499
+ };
3500
+ }
3501
+ const request = buildEffectiveStepRequest(requestStep, context, options, runState);
3502
+ if (!request.ok && "creativeAssetFailure" in request) {
3503
+ return { target, failure: request.creativeAssetFailure };
3504
+ }
3505
+ }
3506
+ return void 0;
3507
+ }
3213
3508
  async function executeReplayWebhookVectorStep(step, phaseId, context, allSteps, options, runState) {
3214
3509
  const start = Date.now();
3510
+ if (step.requires_contract && !new Set(options.contracts ?? []).has(step.requires_contract)) {
3511
+ const detail = `Test-kit contract "${step.requires_contract}" is not configured on this runner.`;
3512
+ const reason = "missing_test_kit_contract";
3513
+ return {
3514
+ step_id: step.id,
3515
+ phase_id: phaseId,
3516
+ title: step.title,
3517
+ task: step.task,
3518
+ passed: true,
3519
+ skipped: true,
3520
+ skip_reason: reason,
3521
+ skip: buildSkip(DETAILED_SKIP_TO_CANONICAL[reason], detail),
3522
+ duration_ms: Date.now() - start,
3523
+ validations: [],
3524
+ context,
3525
+ next: getNextStepPreview(step.id, allSteps, context, runState.runnerVars),
3526
+ extraction: { path: "none", note: "test-kit contract not configured" }
3527
+ };
3528
+ }
3215
3529
  const receiver = options.webhook_replay_receiver;
3216
3530
  if (!receiver?.url) {
3217
3531
  const detail = "No webhook replay receiver URL configured. Pass `webhook_replay_receiver.url` in StoryboardRunOptions to run inbound receiver conformance.";
@@ -3789,6 +4103,9 @@ function phaseContainsOauthMetadataProbe(phase) {
3789
4103
  function agentAdvertisesOauth(profile) {
3790
4104
  const rawCaps = profile?.raw_capabilities;
3791
4105
  if (rawCaps === void 0) return true;
4106
+ const oauthSupported = resolveCapabilityPath(rawCaps, "oauth.supported");
4107
+ if (oauthSupported === true) return true;
4108
+ if (oauthSupported === false) return false;
3792
4109
  const endpoint = resolveCapabilityPath(rawCaps, "account.authorization_endpoint");
3793
4110
  return typeof endpoint === "string" && endpoint.length > 0;
3794
4111
  }
@@ -4044,7 +4361,7 @@ async function prefetchUpstreamTraffic(stepId, resolvedValidations, client, opti
4044
4361
  transport: options.protocol === "a2a" ? "a2a" : "mcp",
4045
4362
  operation: "comply_test_controller",
4046
4363
  payload: redactSecrets({ scenario: "query_upstream_traffic", params }),
4047
- ...runState.agentUrl ? { url: runState.agentUrl } : {}
4364
+ ...runState.agentUrl ? { url: redactOAuthUrlForOutput(runState.agentUrl) } : {}
4048
4365
  };
4049
4366
  const startMs = Date.now();
4050
4367
  let payload;