@adcp/sdk 8.1.0-beta.1 → 8.1.0-beta.11

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 (286) hide show
  1. package/README.md +16 -1
  2. package/bin/adcp.js +75 -21
  3. package/dist/lib/conformance/oracle.d.ts.map +1 -1
  4. package/dist/lib/conformance/oracle.js +8 -1
  5. package/dist/lib/conformance/oracle.js.map +1 -1
  6. package/dist/lib/conformance/schemaArbitrary.js +135 -9
  7. package/dist/lib/conformance/schemaArbitrary.js.map +1 -1
  8. package/dist/lib/core/AgentClient.d.ts +1 -1
  9. package/dist/lib/core/AgentClient.d.ts.map +1 -1
  10. package/dist/lib/core/AgentClient.js.map +1 -1
  11. package/dist/lib/core/GovernanceMiddleware.d.ts +2 -1
  12. package/dist/lib/core/GovernanceMiddleware.d.ts.map +1 -1
  13. package/dist/lib/core/GovernanceMiddleware.js +9 -2
  14. package/dist/lib/core/GovernanceMiddleware.js.map +1 -1
  15. package/dist/lib/core/ResponseValidator.js +1 -1
  16. package/dist/lib/core/ResponseValidator.js.map +1 -1
  17. package/dist/lib/core/SingleAgentClient.d.ts +6 -0
  18. package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
  19. package/dist/lib/core/SingleAgentClient.js +2 -3
  20. package/dist/lib/core/SingleAgentClient.js.map +1 -1
  21. package/dist/lib/core/TaskExecutor.d.ts +1 -0
  22. package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
  23. package/dist/lib/core/TaskExecutor.js +14 -3
  24. package/dist/lib/core/TaskExecutor.js.map +1 -1
  25. package/dist/lib/index.d.ts +3 -2
  26. package/dist/lib/index.d.ts.map +1 -1
  27. package/dist/lib/index.js +26 -11
  28. package/dist/lib/index.js.map +1 -1
  29. package/dist/lib/media-buy/index.d.ts +1 -1
  30. package/dist/lib/media-buy/index.d.ts.map +1 -1
  31. package/dist/lib/media-buy/index.js.map +1 -1
  32. package/dist/lib/media-buy/types.d.ts +12 -52
  33. package/dist/lib/media-buy/types.d.ts.map +1 -1
  34. package/dist/lib/media-buy/types.js +12 -15
  35. package/dist/lib/media-buy/types.js.map +1 -1
  36. package/dist/lib/net/index.d.ts +1 -1
  37. package/dist/lib/net/index.d.ts.map +1 -1
  38. package/dist/lib/net/index.js +2 -1
  39. package/dist/lib/net/index.js.map +1 -1
  40. package/dist/lib/protocols/index.d.ts +10 -2
  41. package/dist/lib/protocols/index.d.ts.map +1 -1
  42. package/dist/lib/protocols/index.js +7 -6
  43. package/dist/lib/protocols/index.js.map +1 -1
  44. package/dist/lib/schemas/index.d.ts +68 -10
  45. package/dist/lib/schemas/index.d.ts.map +1 -1
  46. package/dist/lib/schemas/index.js +57 -16
  47. package/dist/lib/schemas/index.js.map +1 -1
  48. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  49. package/dist/lib/server/create-adcp-server.d.ts +59 -53
  50. package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
  51. package/dist/lib/server/create-adcp-server.js +62 -10
  52. package/dist/lib/server/create-adcp-server.js.map +1 -1
  53. package/dist/lib/server/decisioning/account.d.ts +22 -6
  54. package/dist/lib/server/decisioning/account.d.ts.map +1 -1
  55. package/dist/lib/server/decisioning/account.js.map +1 -1
  56. package/dist/lib/server/decisioning/capabilities.d.ts +8 -0
  57. package/dist/lib/server/decisioning/capabilities.d.ts.map +1 -1
  58. package/dist/lib/server/decisioning/index.d.ts +12 -11
  59. package/dist/lib/server/decisioning/index.d.ts.map +1 -1
  60. package/dist/lib/server/decisioning/index.js.map +1 -1
  61. package/dist/lib/server/decisioning/list-helpers.d.ts +3 -2
  62. package/dist/lib/server/decisioning/list-helpers.d.ts.map +1 -1
  63. package/dist/lib/server/decisioning/list-helpers.js +0 -1
  64. package/dist/lib/server/decisioning/list-helpers.js.map +1 -1
  65. package/dist/lib/server/decisioning/manifest-helpers.d.ts +8 -2
  66. package/dist/lib/server/decisioning/manifest-helpers.d.ts.map +1 -1
  67. package/dist/lib/server/decisioning/manifest-helpers.js +8 -2
  68. package/dist/lib/server/decisioning/manifest-helpers.js.map +1 -1
  69. package/dist/lib/server/decisioning/proposal/dispatch.d.ts +3 -2
  70. package/dist/lib/server/decisioning/proposal/dispatch.d.ts.map +1 -1
  71. package/dist/lib/server/decisioning/proposal/dispatch.js +1 -0
  72. package/dist/lib/server/decisioning/proposal/dispatch.js.map +1 -1
  73. package/dist/lib/server/decisioning/proposal/mock-manager.d.ts +3 -2
  74. package/dist/lib/server/decisioning/proposal/mock-manager.d.ts.map +1 -1
  75. package/dist/lib/server/decisioning/proposal/mock-manager.js.map +1 -1
  76. package/dist/lib/server/decisioning/proposal/types.d.ts +3 -2
  77. package/dist/lib/server/decisioning/proposal/types.d.ts.map +1 -1
  78. package/dist/lib/server/decisioning/proposal/types.js.map +1 -1
  79. package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
  80. package/dist/lib/server/decisioning/runtime/from-platform.js +14 -1
  81. package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
  82. package/dist/lib/server/decisioning/specialisms/audiences.d.ts +4 -1
  83. package/dist/lib/server/decisioning/specialisms/audiences.d.ts.map +1 -1
  84. package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts +19 -7
  85. package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts.map +1 -1
  86. package/dist/lib/server/decisioning/specialisms/campaign-governance.d.ts +9 -4
  87. package/dist/lib/server/decisioning/specialisms/campaign-governance.d.ts.map +1 -1
  88. package/dist/lib/server/decisioning/specialisms/content-standards.d.ts +17 -8
  89. package/dist/lib/server/decisioning/specialisms/content-standards.d.ts.map +1 -1
  90. package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts +13 -5
  91. package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts.map +1 -1
  92. package/dist/lib/server/decisioning/specialisms/creative.d.ts +14 -9
  93. package/dist/lib/server/decisioning/specialisms/creative.d.ts.map +1 -1
  94. package/dist/lib/server/decisioning/specialisms/lists.d.ts +21 -10
  95. package/dist/lib/server/decisioning/specialisms/lists.d.ts.map +1 -1
  96. package/dist/lib/server/decisioning/specialisms/sales.d.ts +27 -12
  97. package/dist/lib/server/decisioning/specialisms/sales.d.ts.map +1 -1
  98. package/dist/lib/server/decisioning/specialisms/signals.d.ts +5 -2
  99. package/dist/lib/server/decisioning/specialisms/signals.d.ts.map +1 -1
  100. package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.d.ts +9 -4
  101. package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.d.ts.map +1 -1
  102. package/dist/lib/server/index.d.ts +2 -0
  103. package/dist/lib/server/index.d.ts.map +1 -1
  104. package/dist/lib/server/index.js.map +1 -1
  105. package/dist/lib/server/operational-platform.d.ts +4 -3
  106. package/dist/lib/server/operational-platform.d.ts.map +1 -1
  107. package/dist/lib/server/operational-platform.js.map +1 -1
  108. package/dist/lib/server/responses.d.ts +28 -27
  109. package/dist/lib/server/responses.d.ts.map +1 -1
  110. package/dist/lib/server/responses.js +96 -35
  111. package/dist/lib/server/responses.js.map +1 -1
  112. package/dist/lib/signing/types.d.ts +6 -0
  113. package/dist/lib/signing/types.d.ts.map +1 -1
  114. package/dist/lib/signing/types.js.map +1 -1
  115. package/dist/lib/signing/verifier.d.ts.map +1 -1
  116. package/dist/lib/signing/verifier.js +33 -4
  117. package/dist/lib/signing/verifier.js.map +1 -1
  118. package/dist/lib/testing/client.d.ts +5 -0
  119. package/dist/lib/testing/client.d.ts.map +1 -1
  120. package/dist/lib/testing/client.js +34 -3
  121. package/dist/lib/testing/client.js.map +1 -1
  122. package/dist/lib/testing/compliance/comply.d.ts +8 -1
  123. package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
  124. package/dist/lib/testing/compliance/comply.js +57 -25
  125. package/dist/lib/testing/compliance/comply.js.map +1 -1
  126. package/dist/lib/testing/compliance/types.d.ts +2 -0
  127. package/dist/lib/testing/compliance/types.d.ts.map +1 -1
  128. package/dist/lib/testing/index.d.ts +1 -1
  129. package/dist/lib/testing/index.d.ts.map +1 -1
  130. package/dist/lib/testing/index.js +4 -1
  131. package/dist/lib/testing/index.js.map +1 -1
  132. package/dist/lib/testing/scenarios/media-buy.d.ts.map +1 -1
  133. package/dist/lib/testing/scenarios/media-buy.js +12 -9
  134. package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
  135. package/dist/lib/testing/storyboard/compliance.d.ts +11 -1
  136. package/dist/lib/testing/storyboard/compliance.d.ts.map +1 -1
  137. package/dist/lib/testing/storyboard/compliance.js +37 -3
  138. package/dist/lib/testing/storyboard/compliance.js.map +1 -1
  139. package/dist/lib/testing/storyboard/context.d.ts.map +1 -1
  140. package/dist/lib/testing/storyboard/context.js +26 -11
  141. package/dist/lib/testing/storyboard/context.js.map +1 -1
  142. package/dist/lib/testing/storyboard/index.d.ts +2 -2
  143. package/dist/lib/testing/storyboard/index.d.ts.map +1 -1
  144. package/dist/lib/testing/storyboard/index.js +6 -2
  145. package/dist/lib/testing/storyboard/index.js.map +1 -1
  146. package/dist/lib/testing/storyboard/probes.d.ts.map +1 -1
  147. package/dist/lib/testing/storyboard/probes.js +3 -0
  148. package/dist/lib/testing/storyboard/probes.js.map +1 -1
  149. package/dist/lib/testing/storyboard/request-builder.d.ts.map +1 -1
  150. package/dist/lib/testing/storyboard/request-builder.js +4 -1
  151. package/dist/lib/testing/storyboard/request-builder.js.map +1 -1
  152. package/dist/lib/testing/storyboard/runner.d.ts +2 -0
  153. package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
  154. package/dist/lib/testing/storyboard/runner.js +357 -47
  155. package/dist/lib/testing/storyboard/runner.js.map +1 -1
  156. package/dist/lib/testing/storyboard/types.d.ts +65 -0
  157. package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
  158. package/dist/lib/testing/storyboard/types.js.map +1 -1
  159. package/dist/lib/testing/storyboard/validations.d.ts +4 -3
  160. package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
  161. package/dist/lib/testing/storyboard/validations.js +27 -3
  162. package/dist/lib/testing/storyboard/validations.js.map +1 -1
  163. package/dist/lib/testing/types.d.ts +19 -0
  164. package/dist/lib/testing/types.d.ts.map +1 -1
  165. package/dist/lib/types/activate-signal.d.ts +647 -0
  166. package/dist/lib/types/build-creative.d.ts +2105 -0
  167. package/dist/lib/types/calibrate-content.d.ts +675 -0
  168. package/dist/lib/types/check-governance.d.ts +619 -0
  169. package/dist/lib/types/comply-test-controller.d.ts +8428 -0
  170. package/dist/lib/types/core.generated.d.ts +547 -537
  171. package/dist/lib/types/core.generated.d.ts.map +1 -1
  172. package/dist/lib/types/core.generated.js +1 -1
  173. package/dist/lib/types/create-collection-list.d.ts +693 -0
  174. package/dist/lib/types/create-content-standards.d.ts +830 -0
  175. package/dist/lib/types/create-media-buy.d.ts +3374 -0
  176. package/dist/lib/types/create-property-list.d.ts +836 -0
  177. package/dist/lib/types/delete-collection-list.d.ts +497 -0
  178. package/dist/lib/types/delete-property-list.d.ts +497 -0
  179. package/dist/lib/types/get-account-financials.d.ts +624 -0
  180. package/dist/lib/types/get-adcp-capabilities.d.ts +2863 -0
  181. package/dist/lib/types/get-collection-list.d.ts +763 -0
  182. package/dist/lib/types/get-content-standards.d.ts +919 -0
  183. package/dist/lib/types/get-creative-delivery.d.ts +2219 -0
  184. package/dist/lib/types/get-creative-features.d.ts +1736 -0
  185. package/dist/lib/types/get-media-buy-artifacts.d.ts +864 -0
  186. package/dist/lib/types/get-media-buys.d.ts +1670 -0
  187. package/dist/lib/types/get-plan-audit-logs.d.ts +455 -0
  188. package/dist/lib/types/get-products.d.ts +4935 -0
  189. package/dist/lib/types/get-property-list.d.ts +874 -0
  190. package/dist/lib/types/get-signals.d.ts +986 -0
  191. package/dist/lib/types/index.d.ts +2 -0
  192. package/dist/lib/types/index.d.ts.map +1 -1
  193. package/dist/lib/types/index.js +1 -0
  194. package/dist/lib/types/index.js.map +1 -1
  195. package/dist/lib/types/inline-enums.generated.d.ts +163 -7
  196. package/dist/lib/types/inline-enums.generated.d.ts.map +1 -1
  197. package/dist/lib/types/inline-enums.generated.js +222 -9
  198. package/dist/lib/types/inline-enums.generated.js.map +1 -1
  199. package/dist/lib/types/list-accounts.d.ts +851 -0
  200. package/dist/lib/types/list-content-standards.d.ts +975 -0
  201. package/dist/lib/types/list-creative-formats.d.ts +3132 -0
  202. package/dist/lib/types/list-creatives.d.ts +2390 -0
  203. package/dist/lib/types/list-property-lists.d.ts +855 -0
  204. package/dist/lib/types/log-event.d.ts +373 -0
  205. package/dist/lib/types/per-tool-index.json +391 -0
  206. package/dist/lib/types/preview-creative.d.ts +1981 -0
  207. package/dist/lib/types/provide-performance-feedback.d.ts +218 -0
  208. package/dist/lib/types/report-plan-outcome.d.ts +433 -0
  209. package/dist/lib/types/report-usage.d.ts +579 -0
  210. package/dist/lib/types/schemas.generated.d.ts +146765 -128986
  211. package/dist/lib/types/schemas.generated.d.ts.map +1 -1
  212. package/dist/lib/types/schemas.generated.js +405 -437
  213. package/dist/lib/types/schemas.generated.js.map +1 -1
  214. package/dist/lib/types/server-payload-aliases.d.ts +78 -0
  215. package/dist/lib/types/server-payload-aliases.d.ts.map +1 -0
  216. package/dist/lib/types/server-payload-aliases.js +11 -0
  217. package/dist/lib/types/server-payload-aliases.js.map +1 -0
  218. package/dist/lib/types/server-payload.d.ts +39 -0
  219. package/dist/lib/types/server-payload.d.ts.map +1 -0
  220. package/dist/lib/types/server-payload.js +11 -0
  221. package/dist/lib/types/server-payload.js.map +1 -0
  222. package/dist/lib/types/si-get-offering.d.ts +259 -0
  223. package/dist/lib/types/si-initiate-session.d.ts +372 -0
  224. package/dist/lib/types/si-send-message.d.ts +300 -0
  225. package/dist/lib/types/si-terminate-session.d.ts +213 -0
  226. package/dist/lib/types/sync-accounts.d.ts +856 -0
  227. package/dist/lib/types/sync-audiences.d.ts +707 -0
  228. package/dist/lib/types/sync-catalogs.d.ts +766 -0
  229. package/dist/lib/types/sync-creatives.d.ts +2134 -0
  230. package/dist/lib/types/sync-event-sources.d.ts +665 -0
  231. package/dist/lib/types/sync-governance.d.ts +558 -0
  232. package/dist/lib/types/sync-plans.d.ts +979 -0
  233. package/dist/lib/types/tools.generated.d.ts +236 -188
  234. package/dist/lib/types/tools.generated.d.ts.map +1 -1
  235. package/dist/lib/types/update-collection-list.d.ts +697 -0
  236. package/dist/lib/types/update-content-standards.d.ts +847 -0
  237. package/dist/lib/types/update-media-buy.d.ts +3047 -0
  238. package/dist/lib/types/update-property-list.d.ts +840 -0
  239. package/dist/lib/types/validate-content-delivery.d.ts +722 -0
  240. package/dist/lib/types/validate-input.d.ts +1683 -0
  241. package/dist/lib/utils/adcp-version-config.d.ts +2 -0
  242. package/dist/lib/utils/adcp-version-config.d.ts.map +1 -1
  243. package/dist/lib/utils/adcp-version-config.js +42 -0
  244. package/dist/lib/utils/adcp-version-config.js.map +1 -1
  245. package/dist/lib/utils/envelope-status-compat.d.ts +24 -5
  246. package/dist/lib/utils/envelope-status-compat.d.ts.map +1 -1
  247. package/dist/lib/utils/envelope-status-compat.js +71 -5
  248. package/dist/lib/utils/envelope-status-compat.js.map +1 -1
  249. package/dist/lib/utils/index.d.ts +1 -0
  250. package/dist/lib/utils/index.d.ts.map +1 -1
  251. package/dist/lib/utils/index.js +5 -2
  252. package/dist/lib/utils/index.js.map +1 -1
  253. package/dist/lib/utils/media-buy-status.d.ts +22 -0
  254. package/dist/lib/utils/media-buy-status.d.ts.map +1 -0
  255. package/dist/lib/utils/media-buy-status.js +55 -0
  256. package/dist/lib/utils/media-buy-status.js.map +1 -0
  257. package/dist/lib/utils/response-schemas.d.ts.map +1 -1
  258. package/dist/lib/utils/response-schemas.js +3 -0
  259. package/dist/lib/utils/response-schemas.js.map +1 -1
  260. package/dist/lib/utils/response-unwrapper.d.ts.map +1 -1
  261. package/dist/lib/utils/response-unwrapper.js +48 -6
  262. package/dist/lib/utils/response-unwrapper.js.map +1 -1
  263. package/dist/lib/utils/tool-request-schemas.d.ts +8574 -1
  264. package/dist/lib/utils/tool-request-schemas.d.ts.map +1 -1
  265. package/dist/lib/utils/tool-request-schemas.js +4 -5
  266. package/dist/lib/utils/tool-request-schemas.js.map +1 -1
  267. package/dist/lib/utils/union-errors.d.ts +13 -6
  268. package/dist/lib/utils/union-errors.d.ts.map +1 -1
  269. package/dist/lib/utils/union-errors.js +34 -7
  270. package/dist/lib/utils/union-errors.js.map +1 -1
  271. package/dist/lib/validation/schema-loader.d.ts.map +1 -1
  272. package/dist/lib/validation/schema-loader.js +68 -15
  273. package/dist/lib/validation/schema-loader.js.map +1 -1
  274. package/dist/lib/validation/schema-validator.js +1 -1
  275. package/dist/lib/validation/schema-validator.js.map +1 -1
  276. package/dist/lib/version.d.ts +27 -3
  277. package/dist/lib/version.d.ts.map +1 -1
  278. package/dist/lib/version.js +42 -3
  279. package/dist/lib/version.js.map +1 -1
  280. package/examples/error-compliant-server.ts +1 -1
  281. package/examples/hello_seller_adapter_guaranteed.ts +14 -10
  282. package/examples/hello_seller_adapter_multi_tenant.ts +27 -23
  283. package/examples/hello_seller_adapter_non_guaranteed.ts +16 -14
  284. package/examples/hello_seller_adapter_proposal_mode.ts +22 -6
  285. package/examples/hello_signals_adapter_marketplace.ts +34 -3
  286. package/package.json +17 -2
@@ -7,6 +7,8 @@
7
7
  * - runStoryboardStep(): run a single step (stateless, LLM-friendly)
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.applyStoryboardVersionOptions = applyStoryboardVersionOptions;
11
+ exports.applyAdcpVersionRunOptions = applyAdcpVersionRunOptions;
10
12
  exports.resolveCapabilityPath = resolveCapabilityPath;
11
13
  exports.evaluateCapabilityPredicate = evaluateCapabilityPredicate;
12
14
  exports.__redactSecretsForTest = __redactSecretsForTest;
@@ -34,12 +36,15 @@ const parallel_dispatch_1 = require("./parallel-dispatch");
34
36
  const path_1 = require("./path");
35
37
  const redact_secrets_1 = require("../../utils/redact-secrets");
36
38
  const response_unwrapper_1 = require("../../utils/response-unwrapper");
39
+ const envelope_status_compat_1 = require("../../utils/envelope-status-compat");
37
40
  const test_controller_1 = require("../test-controller");
38
41
  const request_builder_1 = require("./request-builder");
39
42
  const client_2 = require("../client");
40
43
  const idempotency_1 = require("../../utils/idempotency");
41
44
  const schema_loader_1 = require("../../validation/schema-loader");
45
+ const version_1 = require("../../version");
42
46
  const probes_1 = require("./probes");
47
+ const capabilities_types_1 = require("../../signing/agent-resolver/capabilities-types");
43
48
  const test_kit_1 = require("./test-kit");
44
49
  const loader_1 = require("./loader");
45
50
  const probe_dispatch_1 = require("./request-signing/probe-dispatch");
@@ -65,6 +70,26 @@ const SKIP_DETAILS = {
65
70
  };
66
71
  const CONTROLLER_SEEDING_FAILED_DETAIL = 'Skipped: pre-flight comply_test_controller seeding failed; the agent was not populated with the storyboard fixtures the remaining phases depend on.';
67
72
  const OAUTH_NOT_ADVERTISED_DETAIL = 'Skipped: agent does not advertise OAuth — /.well-known/oauth-protected-resource returned 404 (RFC 9728 §3). API-key path must carry auth_mechanism_verified for this storyboard to pass.';
73
+ function applyStoryboardVersionOptions(storyboard, options) {
74
+ return applyAdcpVersionRunOptions(storyboard.adcp_version, options);
75
+ }
76
+ function applyAdcpVersionRunOptions(defaultAdcpVersion, options) {
77
+ const adcpVersion = options.adcpVersion ?? defaultAdcpVersion;
78
+ if (adcpVersion === undefined)
79
+ return options;
80
+ const versionEnvelope = options.versionEnvelope ?? storyboardVersionEnvelopeMode(adcpVersion);
81
+ if (options.adcpVersion === adcpVersion && options.versionEnvelope === versionEnvelope) {
82
+ return options;
83
+ }
84
+ return { ...options, adcpVersion, versionEnvelope };
85
+ }
86
+ function storyboardVersionEnvelopeMode(adcpVersion) {
87
+ const bundleKey = (0, schema_loader_1.resolveBundleKey)(adcpVersion);
88
+ const major = (0, version_1.parseAdcpMajorVersion)(bundleKey);
89
+ if (Number.isFinite(major) && major < 3)
90
+ return 'none';
91
+ return 'auto';
92
+ }
68
93
  /**
69
94
  * Suffix appended to the skip detail when the sole-stateful-step exemption
70
95
  * fires (`not_applicable` / `missing_tool` / `missing_test_controller` on
@@ -179,6 +204,107 @@ function evaluateCapabilityPredicate(predicate, actual) {
179
204
  function buildSkip(reason, detail) {
180
205
  return { reason, detail: detail ?? SKIP_DETAILS[reason] };
181
206
  }
207
+ function detectResponseDerivedNotApplicable(step, request, response, runState, allSteps) {
208
+ if (response === undefined || response === null)
209
+ return null;
210
+ const gates = [
211
+ ...normalizeResponseNotApplicableGates(step.not_applicable_if),
212
+ ...inferImplicitResponseNotApplicableGates(step, request, runState, allSteps),
213
+ ];
214
+ for (const gate of gates) {
215
+ if (gate.kind !== 'terminal_page')
216
+ continue;
217
+ if (!terminalPageGateMatches(gate, request, response))
218
+ continue;
219
+ const detail = gate.detail ??
220
+ `${gate.reason ?? 'single_page_result'}: ${step.task} response is terminal; cursor-walk not applicable`;
221
+ return {
222
+ detail,
223
+ contextKeys: responseNotApplicableContextKeys(step, gate),
224
+ };
225
+ }
226
+ return null;
227
+ }
228
+ function normalizeResponseNotApplicableGates(gates) {
229
+ if (!gates)
230
+ return [];
231
+ return Array.isArray(gates) ? gates : [gates];
232
+ }
233
+ function inferImplicitResponseNotApplicableGates(step, request, runState, allSteps) {
234
+ // Back-compat for the already-published pagination_integrity_list_accounts
235
+ // storyboard: it expresses the continuation requirement as validations
236
+ // rather than a dedicated response-derived gate. Keep this narrowly scoped
237
+ // to list_accounts so other seeded pagination storyboards do not silently
238
+ // waive fixture/setup mistakes.
239
+ if (step.task !== 'list_accounts')
240
+ return [];
241
+ const expectsContinuation = step.validations?.some(v => v.check === 'field_value' && v.path === 'pagination.has_more' && v.value === true);
242
+ const capturesCursor = step.context_outputs?.some(o => o.path === 'pagination.cursor');
243
+ const validatesCursor = step.validations?.some(v => v.check === 'field_present' && v.path === 'pagination.cursor');
244
+ if (!expectsContinuation || (!capturesCursor && !validatesCursor))
245
+ return [];
246
+ const maxResults = (0, path_1.resolvePath)(request, 'pagination.max_results');
247
+ if (typeof maxResults === 'number' &&
248
+ Number.isFinite(maxResults) &&
249
+ hasUnrunAccountSeedExceedingPageSize(allSteps, runState, maxResults)) {
250
+ return [];
251
+ }
252
+ const setupAccounts = (0, path_1.resolvePath)(runState?.priorStepResults.get('sync_three_accounts')?.response, 'accounts');
253
+ if (typeof maxResults === 'number' &&
254
+ Number.isFinite(maxResults) &&
255
+ Array.isArray(setupAccounts) &&
256
+ setupAccounts.length > maxResults) {
257
+ return [];
258
+ }
259
+ return [{ kind: 'terminal_page', items_path: 'accounts', reason: 'single_page_result' }];
260
+ }
261
+ function hasUnrunAccountSeedExceedingPageSize(allSteps, runState, maxResults) {
262
+ return (allSteps?.some(({ step }) => {
263
+ if (runState?.priorStepResults.has(step.id))
264
+ return false;
265
+ if (step.task !== 'sync_accounts')
266
+ return false;
267
+ const accounts = (0, path_1.resolvePath)(step.sample_request, 'accounts');
268
+ return Array.isArray(accounts) && accounts.length > maxResults;
269
+ }) ?? false);
270
+ }
271
+ function terminalPageGateMatches(gate, request, response) {
272
+ const maxResults = (0, path_1.resolvePath)(request, gate.request_max_results_path ?? 'pagination.max_results');
273
+ if (typeof maxResults !== 'number' || !Number.isFinite(maxResults) || maxResults <= 0)
274
+ return false;
275
+ const items = (0, path_1.resolvePath)(response, gate.items_path);
276
+ if (!Array.isArray(items))
277
+ return false;
278
+ const pagination = (0, path_1.resolvePath)(response, 'pagination');
279
+ if (pagination === undefined || pagination === null)
280
+ return items.length < maxResults;
281
+ if (typeof pagination !== 'object' || Array.isArray(pagination))
282
+ return false;
283
+ const p = pagination;
284
+ if (p.has_more === true)
285
+ return false;
286
+ if (p.has_more !== false)
287
+ return false;
288
+ if (typeof p.total_count === 'number' && p.total_count > items.length)
289
+ return false;
290
+ if (items.length < maxResults)
291
+ return true;
292
+ return typeof p.total_count === 'number' && p.total_count <= items.length;
293
+ }
294
+ function responseNotApplicableContextKeys(step, gate) {
295
+ if (gate.context_keys?.length)
296
+ return gate.context_keys;
297
+ return (step.context_outputs ?? [])
298
+ .filter(o => o.path === 'pagination.cursor')
299
+ .map(o => o.key)
300
+ .filter((key) => typeof key === 'string' && key.length > 0);
301
+ }
302
+ function responseDerivedContextResult(runState) {
303
+ const entries = runState.responseDerivedNotApplicableContextKeys;
304
+ return entries && entries.size > 0
305
+ ? { response_derived_not_applicable_context_keys: Object.fromEntries(entries) }
306
+ : {};
307
+ }
182
308
  /**
183
309
  * True for skip reasons that imply state genuinely never materialized
184
310
  * — no other code path could have established it. The runner trips
@@ -400,7 +526,7 @@ function remapTaskCompletionOutputs(outputs) {
400
526
  return o;
401
527
  });
402
528
  }
403
- async function resolveTaskCompletionOutputs(taskResult, outputs, client, webhookReceiver) {
529
+ async function resolveTaskCompletionOutputs(taskResult, outputs, client, webhookReceiver, originatingTaskName) {
404
530
  const hasTaskCompletionPath = outputs.some(o => typeof o.path === 'string' && o.path.startsWith(TASK_COMPLETION_PATH_PREFIX));
405
531
  if (!hasTaskCompletionPath)
406
532
  return { attempted: false };
@@ -473,7 +599,7 @@ async function resolveTaskCompletionOutputs(taskResult, outputs, client, webhook
473
599
  const polled = winner.result;
474
600
  if (polled.success === false)
475
601
  return { attempted: true, taskFailed: true };
476
- return { attempted: true, data: polled.data };
602
+ return { attempted: true, data: normalizeTaskCompletionData(polled.data, originatingTaskName) };
477
603
  }
478
604
  // Webhook win.
479
605
  const waitResult = winner.result;
@@ -488,7 +614,7 @@ async function resolveTaskCompletionOutputs(taskResult, outputs, client, webhook
488
614
  // attribute to `capture_task_failed`, not silently fall through to a
489
615
  // capture against an undefined `result`.
490
616
  if (webhookBody?.status === 'completed') {
491
- return { attempted: true, data: webhookBody.result };
617
+ return { attempted: true, data: normalizeTaskCompletionData(webhookBody.result, originatingTaskName) };
492
618
  }
493
619
  return { attempted: true, taskFailed: true };
494
620
  }
@@ -500,6 +626,23 @@ async function resolveTaskCompletionOutputs(taskResult, outputs, client, webhook
500
626
  clearTimeout(timer);
501
627
  }
502
628
  }
629
+ function normalizeTaskCompletionData(data, taskName) {
630
+ if (data == null || typeof data !== 'object' || Array.isArray(data))
631
+ return data;
632
+ const original = data;
633
+ const compat = (0, envelope_status_compat_1.injectLegacyEnvelopeStatus)(original, { toolName: taskName });
634
+ if (!('status' in original) && 'status' in compat) {
635
+ const { status: _status, ...rest } = compat;
636
+ return (0, envelope_status_compat_1.normalizeLegacyMediaBuyStatusForReturn)(rest, { toolName: taskName });
637
+ }
638
+ if ((taskName === 'create_media_buy' || taskName === 'update_media_buy') &&
639
+ typeof original.status === 'string' &&
640
+ compat.status === 'completed' &&
641
+ typeof compat.media_buy_status === 'string') {
642
+ return (0, envelope_status_compat_1.normalizeLegacyMediaBuyStatusForReturn)({ ...compat, status: original.status }, { toolName: taskName });
643
+ }
644
+ return (0, envelope_status_compat_1.normalizeLegacyMediaBuyStatusForReturn)(compat, { toolName: taskName });
645
+ }
503
646
  function readEnvIntOrDefault(value, fallback) {
504
647
  if (!value)
505
648
  return fallback;
@@ -571,6 +714,7 @@ function filterResponseHeaders(headers) {
571
714
  * empty on instance B.
572
715
  */
573
716
  async function runStoryboard(agentUrlOrUrls, storyboard, options = {}) {
717
+ options = applyStoryboardVersionOptions(storyboard, options);
574
718
  (0, test_kit_1.validateTestKit)(options.test_kit);
575
719
  // Enforce authoring-time branch_set invariants regardless of how the
576
720
  // storyboard reached us. YAML callers already ran these rules in
@@ -1211,6 +1355,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1211
1355
  let clients;
1212
1356
  let routingContext;
1213
1357
  let profile;
1358
+ let callerOwnsClients = false;
1214
1359
  if (useRouting) {
1215
1360
  try {
1216
1361
  routingContext = await (0, agent_routing_1.buildRoutingContext)(storyboard, options);
@@ -1225,8 +1370,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1225
1370
  duration_ms: 0,
1226
1371
  error: detail,
1227
1372
  };
1228
- if (!options._client)
1229
- await (0, protocols_1.closeConnections)(options.protocol);
1373
+ await (0, protocols_1.closeConnections)(options.protocol);
1230
1374
  return buildDiscoveryFailedResult(agentUrls, storyboard, failedStep);
1231
1375
  }
1232
1376
  clients = [...routingContext.clients.values()];
@@ -1258,7 +1402,9 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1258
1402
  else {
1259
1403
  // Build one client per URL. In single-URL mode `_client` (from comply()) is
1260
1404
  // honored so the shared MCP transport is reused across storyboards.
1261
- clients = agentUrls.map(url => (0, client_1.getOrCreateClient)(url, options));
1405
+ const clientResolutions = agentUrls.map(url => (0, client_1.getOrCreateClientResolution)(url, options));
1406
+ clients = clientResolutions.map(r => r.client);
1407
+ callerOwnsClients = clientResolutions.some(r => r.reusedShared);
1262
1408
  // Drop any retained A2A session ids before this storyboard's first call.
1263
1409
  // `comply()` shares one client across N storyboards for transport reuse;
1264
1410
  // AgentClient.retainSession holds onto `pendingTaskId` from non-terminal
@@ -1273,7 +1419,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1273
1419
  // expected to run the same code behind a shared state store, so one probe
1274
1420
  // is sufficient. For multi-instance runs, skipping N-1 redundant
1275
1421
  // get_agent_info calls also keeps CI output clean.
1276
- if (!options._client) {
1422
+ if (!callerOwnsClients) {
1277
1423
  const discovered = await (0, client_1.getOrDiscoverProfile)(clients[0], options);
1278
1424
  // Discovery failure must surface as a HARD STORYBOARD FAILURE, not a
1279
1425
  // silent empty `agentTools: []` that lets every step skip with
@@ -1282,8 +1428,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1282
1428
  // (auth misconfig, MCP transport-fallback bugs, network policy, etc.).
1283
1429
  // See: https://github.com/adcontextprotocol/adcp-client/issues/...
1284
1430
  if (discovered.step.passed === false) {
1285
- if (!options._client)
1286
- await (0, protocols_1.closeConnections)(options.protocol);
1431
+ await (0, protocols_1.closeConnections)(options.protocol);
1287
1432
  return buildDiscoveryFailedResult(agentUrls, storyboard, discovered.step);
1288
1433
  }
1289
1434
  profile = discovered.profile;
@@ -1329,7 +1474,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1329
1474
  if (allRequires.length) {
1330
1475
  const unmet = checkRequires(allRequires, options, profile);
1331
1476
  if (unmet) {
1332
- if (!options._client)
1477
+ if (!callerOwnsClients)
1333
1478
  await (0, protocols_1.closeConnections)(options.protocol);
1334
1479
  return {
1335
1480
  ...buildRequirementUnmetResult(agentUrls, storyboard, unmet.requirement, unmet.detail),
@@ -1348,7 +1493,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1348
1493
  const actual = resolveCapabilityPath(rawCaps, cap.path);
1349
1494
  const unmetDetail = evaluateCapabilityPredicate(cap, actual);
1350
1495
  if (unmetDetail !== null) {
1351
- if (!options._client)
1496
+ if (!callerOwnsClients)
1352
1497
  await (0, protocols_1.closeConnections)(options.protocol);
1353
1498
  return {
1354
1499
  ...buildCapabilityUnsupportedResult(agentUrls, storyboard, unmetDetail),
@@ -1371,7 +1516,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1371
1516
  if (storyboard.required_tools?.length && options.agentTools) {
1372
1517
  const hasAnyRequired = storyboard.required_tools.some(t => options.agentTools.includes(t));
1373
1518
  if (!hasAnyRequired) {
1374
- if (!options._client)
1519
+ if (!callerOwnsClients)
1375
1520
  await (0, protocols_1.closeConnections)(options.protocol);
1376
1521
  return {
1377
1522
  ...buildRequiredToolsMissingResult(agentUrls, storyboard, `agent does not advertise any of [${storyboard.required_tools.join(', ')}]`),
@@ -1394,6 +1539,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1394
1539
  const contextProvenance = new Map();
1395
1540
  const priorA2aEnvelopes = new Map();
1396
1541
  const stepRequestStarts = new Map();
1542
+ const responseDerivedNotApplicableContextKeys = new Map();
1397
1543
  const phaseResults = [];
1398
1544
  let passedCount = 0;
1399
1545
  let failedCount = 0;
@@ -1863,6 +2009,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
1863
2009
  contextProvenance,
1864
2010
  priorA2aEnvelopes,
1865
2011
  stepRequestStarts,
2012
+ responseDerivedNotApplicableContextKeys,
1866
2013
  agentLibraryVersion: profile?.library_version,
1867
2014
  });
1868
2015
  const result = { ...rawResult, storyboard_id: storyboard.id };
@@ -2323,7 +2470,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
2323
2470
  // Close protocol connections when the runner created its own client. The
2324
2471
  // connection pool is keyed by URL+auth, so a single closeConnections() call
2325
2472
  // evicts every instance's transport regardless of how many URLs we used.
2326
- if (!options._client) {
2473
+ if (!callerOwnsClients) {
2327
2474
  await (0, protocols_1.closeConnections)(options.protocol);
2328
2475
  }
2329
2476
  if (webhookReceiver)
@@ -2361,7 +2508,7 @@ async function runMultiPass(agentUrls, storyboard, options) {
2361
2508
  // only the first pass attaches the synthetic `__controller_seeding__`
2362
2509
  // phase to its `phaseResults`, so the aggregated top-level counts reflect
2363
2510
  // a single seeding pass across the whole run.
2364
- const preSeedClients = agentUrls.map(url => (0, client_1.getOrCreateClient)(url, options));
2511
+ const preSeedClients = agentUrls.map(url => (0, client_1.getOrCreateClientResolution)(url, options).client);
2365
2512
  const preSeedContext = { ...options.context };
2366
2513
  const preSeededResult = await (0, seeding_1.runControllerSeeding)(preSeedClients[0], storyboard, options, preSeedContext);
2367
2514
  const passes = [];
@@ -2558,15 +2705,17 @@ function summarizeStrictValidation(phases) {
2558
2705
  * Context is passed in and returned, enabling step-by-step orchestration.
2559
2706
  */
2560
2707
  async function runStoryboardStep(agentUrl, storyboard, stepId, options = {}) {
2708
+ options = applyStoryboardVersionOptions(storyboard, options);
2561
2709
  (0, test_kit_1.validateTestKit)(options.test_kit);
2562
- const client = (0, client_1.getOrCreateClient)(agentUrl, options);
2710
+ const clientResolution = (0, client_1.getOrCreateClientResolution)(agentUrl, options);
2711
+ const client = clientResolution.client;
2563
2712
  // Discover agent profile for standalone step execution. Captured so the
2564
2713
  // executeStep call below can thread `library_version` through to
2565
2714
  // shape-drift hint detection (issue #850). Also threads _profile into
2566
2715
  // options so capability-based skip gates in executeStep (e.g. account-mode
2567
2716
  // branching) can read raw_capabilities, mirroring executeStoryboardPass.
2568
2717
  let profile;
2569
- if (!options._client) {
2718
+ if (!clientResolution.reusedShared) {
2570
2719
  const discovered = await (0, client_1.getOrDiscoverProfile)(client, options);
2571
2720
  profile = discovered.profile;
2572
2721
  if (profile && !options._profile) {
@@ -2612,6 +2761,7 @@ async function runStoryboardStep(agentUrl, storyboard, stepId, options = {}) {
2612
2761
  // previous step's result). Storyboard-level runs build this internally;
2613
2762
  // here the caller owns accumulation across stateless invocations.
2614
2763
  const contextProvenance = new Map(Object.entries(options.context_provenance ?? {}));
2764
+ const responseDerivedNotApplicableContextKeys = new Map(Object.entries(options.response_derived_not_applicable_context_keys ?? {}));
2615
2765
  const result = await executeStep(client, found.step, found.phaseId, context, allSteps, options, {
2616
2766
  contributions: new Set(),
2617
2767
  priorStepResults: new Map(),
@@ -2622,9 +2772,10 @@ async function runStoryboardStep(agentUrl, storyboard, stepId, options = {}) {
2622
2772
  contextProvenance,
2623
2773
  priorA2aEnvelopes: new Map(),
2624
2774
  stepRequestStarts: new Map(),
2775
+ responseDerivedNotApplicableContextKeys,
2625
2776
  agentLibraryVersion: profile?.library_version,
2626
2777
  });
2627
- if (!options._client) {
2778
+ if (!clientResolution.reusedShared) {
2628
2779
  await (0, protocols_1.closeConnections)(options.protocol);
2629
2780
  }
2630
2781
  if (ownsWebhookReceiver && webhookReceiver)
@@ -2642,6 +2793,7 @@ client, step, phaseId, context, allSteps, options, state) {
2642
2793
  agentUrl: '',
2643
2794
  contextProvenance: new Map(),
2644
2795
  stepRequestStarts: new Map(),
2796
+ responseDerivedNotApplicableContextKeys: new Map(),
2645
2797
  };
2646
2798
  // HTTP probe tasks bypass the MCP client entirely.
2647
2799
  if (probes_1.PROBE_TASKS.has(step.task)) {
@@ -2810,45 +2962,50 @@ client, step, phaseId, context, allSteps, options, state) {
2810
2962
  const unresolvedVars = findUnresolvedContextVars(request);
2811
2963
  if (unresolvedVars.length > 0 && !step.expect_error) {
2812
2964
  const next = getNextStepPreview(step.id, allSteps, context, runState.runnerVars);
2813
- const detail = `Skipped: unresolved context variables from prior steps: ${unresolvedVars.map(v => v.key).join(', ')}.`;
2814
- // Per runner-output-contract.yaml v2.0.0, a skipped consumer step MUST
2815
- // carry an `unresolved_substitution` validation result for each missing
2816
- // token `expected` is the token string, `actual` / `json_pointer` /
2817
- // `request` / `response` are null (pre-wire failure; no response payload
2818
- // exists). Surfacing this as a validation rather than only a skip detail
2819
- // keeps the runner-output contract's "failed/skipped steps include at
2820
- // least one validation result" invariant intact and lets dashboards
2821
- // attribute the cascade origin without parsing the skip message.
2822
- const seenTokens = new Set();
2965
+ const responseDerivedDetails = unresolvedVars
2966
+ .map(v => runState.responseDerivedNotApplicableContextKeys?.get(v.key))
2967
+ .filter((d) => typeof d === 'string');
2968
+ const allResponseDerived = responseDerivedDetails.length === unresolvedVars.length && responseDerivedDetails.length > 0;
2969
+ const detail = allResponseDerived
2970
+ ? [...new Set(responseDerivedDetails)].join('; ')
2971
+ : `Skipped: unresolved context variables from prior steps: ${unresolvedVars.map(v => v.key).join(', ')}.`;
2972
+ // Normal unresolved substitutions carry one validation result per missing
2973
+ // token. Response-derived terminal-page skips are already successful
2974
+ // not_applicable rows, so their downstream cursor consumers stay validation
2975
+ // empty to avoid inventing a failing-looking check for an expected skip.
2823
2976
  const synthesized = [];
2824
- for (const v of unresolvedVars) {
2825
- if (seenTokens.has(v.token))
2826
- continue;
2827
- seenTokens.add(v.token);
2828
- synthesized.push({
2829
- check: 'unresolved_substitution',
2830
- passed: false,
2831
- description: `request token "${v.token}" did not resolve — prior step did not populate context.${v.key}`,
2832
- json_pointer: null,
2833
- expected: v.token,
2834
- actual: null,
2835
- schema_id: null,
2836
- schema_url: null,
2837
- });
2977
+ if (!allResponseDerived) {
2978
+ const seenTokens = new Set();
2979
+ for (const v of unresolvedVars) {
2980
+ if (seenTokens.has(v.token))
2981
+ continue;
2982
+ seenTokens.add(v.token);
2983
+ synthesized.push({
2984
+ check: 'unresolved_substitution',
2985
+ passed: false,
2986
+ description: `request token "${v.token}" did not resolve — prior step did not populate context.${v.key}`,
2987
+ json_pointer: null,
2988
+ expected: v.token,
2989
+ actual: null,
2990
+ schema_id: null,
2991
+ schema_url: null,
2992
+ });
2993
+ }
2838
2994
  }
2839
2995
  return {
2840
2996
  step_id: step.id,
2841
2997
  phase_id: phaseId,
2842
2998
  title: step.title,
2843
2999
  task: step.task,
2844
- passed: false,
3000
+ passed: allResponseDerived,
2845
3001
  skipped: true,
2846
- skip_reason: 'prerequisite_failed',
2847
- skip: buildSkip('prerequisite_failed', detail),
3002
+ skip_reason: allResponseDerived ? 'not_applicable' : 'prerequisite_failed',
3003
+ skip: buildSkip(allResponseDerived ? 'not_applicable' : 'prerequisite_failed', detail),
2848
3004
  duration_ms: 0,
2849
3005
  validations: synthesized,
2850
3006
  context,
2851
- error: detail,
3007
+ ...responseDerivedContextResult(runState),
3008
+ ...(!allResponseDerived && { error: detail }),
2852
3009
  next,
2853
3010
  extraction: { path: 'none' },
2854
3011
  };
@@ -3203,6 +3360,32 @@ client, step, phaseId, context, allSteps, options, state) {
3203
3360
  if (step.expect_error && !taskResult?.data && taskResult?.error) {
3204
3361
  taskResult = { ...taskResult, data: { error: taskResult.error } };
3205
3362
  }
3363
+ const responseDerivedSkip = detectResponseDerivedNotApplicable(effectiveStep, request, taskResult?.data, runState, allSteps);
3364
+ if (responseDerivedSkip && !step.expect_error) {
3365
+ for (const key of responseDerivedSkip.contextKeys) {
3366
+ runState.responseDerivedNotApplicableContextKeys?.set(key, responseDerivedSkip.detail);
3367
+ }
3368
+ const next = getNextStepPreview(step.id, allSteps, context, runState.runnerVars);
3369
+ return {
3370
+ step_id: step.id,
3371
+ phase_id: phaseId,
3372
+ title: step.title,
3373
+ task: step.task,
3374
+ passed: true,
3375
+ skipped: true,
3376
+ skip_reason: 'not_applicable',
3377
+ skip: buildSkip('not_applicable', responseDerivedSkip.detail),
3378
+ duration_ms: stepResult.duration_ms,
3379
+ validations: [],
3380
+ context,
3381
+ ...responseDerivedContextResult(runState),
3382
+ response: (0, redact_secrets_1.redactSecrets)(taskResult?.data),
3383
+ next,
3384
+ request: requestRecord,
3385
+ ...(responseRecord && { response_record: responseRecord }),
3386
+ extraction: extractionFromTaskResult(taskResult),
3387
+ };
3388
+ }
3206
3389
  // Determine pass/fail — inverted when expect_error is set
3207
3390
  let passed;
3208
3391
  if (step.expect_error) {
@@ -3370,6 +3553,9 @@ client, step, phaseId, context, allSteps, options, state) {
3370
3553
  if (passed && hasData && taskResult) {
3371
3554
  const extracted = (0, context_1.extractContextWithProvenance)(effectiveStep.task, taskResult.data, step.id);
3372
3555
  Object.assign(updatedContext, extracted.values);
3556
+ for (const key of Object.keys(extracted.values)) {
3557
+ runState.responseDerivedNotApplicableContextKeys?.delete(key);
3558
+ }
3373
3559
  if (runState.contextProvenance) {
3374
3560
  for (const [key, entry] of Object.entries(extracted.provenance)) {
3375
3561
  runState.contextProvenance.set(key, entry);
@@ -3387,6 +3573,10 @@ client, step, phaseId, context, allSteps, options, state) {
3387
3573
  // ensures the minted value from any same-step $generate:…#<key> inline
3388
3574
  // substitution is visible here.
3389
3575
  if (step.context_outputs?.length) {
3576
+ for (const output of step.context_outputs) {
3577
+ if (output.key)
3578
+ runState.responseDerivedNotApplicableContextKeys?.delete(output.key);
3579
+ }
3390
3580
  // Resolve `task_completion.<path>` outputs against the eventual task
3391
3581
  // artifact rather than the immediate response. When the immediate
3392
3582
  // response is a submitted-arm envelope (HITL / async-signed-IO flows),
@@ -3401,7 +3591,7 @@ client, step, phaseId, context, allSteps, options, state) {
3401
3591
  // `capture_poll_timeout` instead of recycling
3402
3592
  // `capture_path_not_resolvable` so the failure-class is distinct from
3403
3593
  // the original "field absent in immediate response" diagnostic.
3404
- const taskCompletionResolution = await resolveTaskCompletionOutputs(taskResult, step.context_outputs, client, runState.webhookReceiver);
3594
+ const taskCompletionResolution = await resolveTaskCompletionOutputs(taskResult, step.context_outputs, client, runState.webhookReceiver, effectiveStep.task);
3405
3595
  // `'data' in resolution` distinguishes "polled, artifact had no data"
3406
3596
  // (use undefined → outputs fail with capture_path_not_resolvable) from
3407
3597
  // "did not poll" (fall back to the immediate response data).
@@ -3413,6 +3603,9 @@ client, step, phaseId, context, allSteps, options, state) {
3413
3603
  const remappedOutputs = remapTaskCompletionOutputs(step.context_outputs);
3414
3604
  const explicit = (0, context_1.applyContextOutputsWithProvenance)(extractionData, remappedOutputs, step.id, effectiveStep.task, updatedContext);
3415
3605
  Object.assign(updatedContext, explicit.values);
3606
+ for (const key of Object.keys(explicit.values)) {
3607
+ runState.responseDerivedNotApplicableContextKeys?.delete(key);
3608
+ }
3416
3609
  if (runState.contextProvenance) {
3417
3610
  for (const [key, entry] of Object.entries(explicit.provenance)) {
3418
3611
  runState.contextProvenance.set(key, entry);
@@ -3543,6 +3736,7 @@ client, step, phaseId, context, allSteps, options, state) {
3543
3736
  runState.contextProvenance.size > 0 && {
3544
3737
  context_provenance: Object.fromEntries(runState.contextProvenance),
3545
3738
  }),
3739
+ ...responseDerivedContextResult(runState),
3546
3740
  error: step.expect_error ? undefined : truncateError(stepResult.error || taskResult?.error),
3547
3741
  ...(!step.expect_error && taskResult?.adcp_error && { adcp_error: taskResult.adcp_error }),
3548
3742
  next,
@@ -3559,7 +3753,24 @@ async function executeProbeStep(step, phaseId, context, allSteps, options, runSt
3559
3753
  const start = Date.now();
3560
3754
  let httpResult;
3561
3755
  const probeOpts = { allowPrivateIp: options.allow_http === true };
3562
- if (step.task === 'protected_resource_metadata') {
3756
+ if (step.requires_contract) {
3757
+ const contracts = new Set(options.contracts ?? []);
3758
+ if (!contracts.has(step.requires_contract)) {
3759
+ httpResult = {
3760
+ url: runState.agentUrl,
3761
+ status: 0,
3762
+ headers: {},
3763
+ body: null,
3764
+ skipped: true,
3765
+ skip_reason: 'missing_test_kit_contract',
3766
+ error: `Test-kit contract "${step.requires_contract}" is not configured on this runner.`,
3767
+ };
3768
+ }
3769
+ }
3770
+ if (httpResult) {
3771
+ // Contract-gated synthetic probes self-skip before doing any network work.
3772
+ }
3773
+ else if (step.task === 'protected_resource_metadata') {
3563
3774
  httpResult = await (0, probes_1.probeProtectedResourceMetadata)(runState.agentUrl, probeOpts);
3564
3775
  // RFC 9728 presence semantics (adcp-client#677): a 404 means the agent is
3565
3776
  // honestly not advertising OAuth. Convert to a clean step skip so the
@@ -3585,6 +3796,21 @@ async function executeProbeStep(step, phaseId, context, allSteps, options, runSt
3585
3796
  else if (step.task === 'request_signing_probe') {
3586
3797
  httpResult = await (0, probe_dispatch_1.probeRequestSigningVector)(step.id, runState.agentUrl, options);
3587
3798
  }
3799
+ else if (step.task === 'fetch_brand_jwks') {
3800
+ httpResult = await probeBrandJwks(options._profile?.raw_capabilities, probeOpts);
3801
+ }
3802
+ else if (step.task === 'assert_jwks_purpose') {
3803
+ httpResult = assertJwksPurpose(runState.priorProbes.get('fetch_brand_jwks'), 'webhook-signing');
3804
+ }
3805
+ else if (step.task === 'expect_rate_limit_not_replayed') {
3806
+ httpResult = {
3807
+ url: runState.agentUrl,
3808
+ status: 0,
3809
+ headers: {},
3810
+ body: null,
3811
+ error: 'rate_limit_trip_runner contract is configured, but this SDK runner does not yet implement live rate-limit trip/replay probing.',
3812
+ };
3813
+ }
3588
3814
  if (httpResult)
3589
3815
  runState.priorProbes.set(step.task, httpResult);
3590
3816
  const duration = Date.now() - start;
@@ -3670,6 +3896,90 @@ async function executeProbeStep(step, phaseId, context, allSteps, options, runSt
3670
3896
  extraction,
3671
3897
  };
3672
3898
  }
3899
+ async function probeBrandJwks(rawCapabilities, options) {
3900
+ const brandJsonUrl = (0, capabilities_types_1.readBrandJsonUrl)(rawCapabilities);
3901
+ if (!brandJsonUrl) {
3902
+ return {
3903
+ url: '',
3904
+ status: 0,
3905
+ headers: {},
3906
+ body: null,
3907
+ error: 'identity.brand_json_url missing from get_adcp_capabilities; cannot fetch brand JWKS',
3908
+ };
3909
+ }
3910
+ const brand = await (0, probes_1.fetchProbe)(brandJsonUrl, options);
3911
+ if (brand.error || brand.status < 200 || brand.status >= 300) {
3912
+ return {
3913
+ ...brand,
3914
+ error: brand.error ?? `brand.json fetch returned HTTP ${brand.status}`,
3915
+ };
3916
+ }
3917
+ const agents = brand.body && typeof brand.body === 'object' ? brand.body.agents : undefined;
3918
+ const jwksUri = Array.isArray(agents)
3919
+ ? agents
3920
+ .map(agent => (agent && typeof agent === 'object' ? agent.jwks_uri : undefined))
3921
+ .find((uri) => typeof uri === 'string' && uri.length > 0)
3922
+ : undefined;
3923
+ if (!jwksUri) {
3924
+ return {
3925
+ url: brandJsonUrl,
3926
+ status: 0,
3927
+ headers: {},
3928
+ body: brand.body,
3929
+ error: 'brand.json agents[] did not contain a jwks_uri',
3930
+ };
3931
+ }
3932
+ const jwks = await (0, probes_1.fetchProbe)(jwksUri, options);
3933
+ if (jwks.error || jwks.status < 200 || jwks.status >= 300) {
3934
+ return {
3935
+ ...jwks,
3936
+ error: jwks.error ?? `JWKS fetch returned HTTP ${jwks.status}`,
3937
+ };
3938
+ }
3939
+ return jwks;
3940
+ }
3941
+ function assertJwksPurpose(prior, purpose) {
3942
+ if (!prior || prior.error) {
3943
+ return {
3944
+ url: prior?.url ?? '',
3945
+ status: prior?.status ?? 0,
3946
+ headers: prior?.headers ?? {},
3947
+ body: prior?.body ?? null,
3948
+ error: prior?.error ?? 'fetch_brand_jwks step missing; cannot assert JWKS purpose',
3949
+ };
3950
+ }
3951
+ const keys = prior.body && typeof prior.body === 'object' ? prior.body.keys : undefined;
3952
+ if (!Array.isArray(keys)) {
3953
+ return {
3954
+ url: prior.url,
3955
+ status: 0,
3956
+ headers: {},
3957
+ body: prior.body,
3958
+ error: 'JWKS body does not contain keys[]',
3959
+ };
3960
+ }
3961
+ const matching = keys.filter(key => {
3962
+ if (!key || typeof key !== 'object')
3963
+ return false;
3964
+ const rec = key;
3965
+ return rec.adcp_use === purpose && rec.status !== 'revoked' && rec.revoked !== true;
3966
+ });
3967
+ if (matching.length === 0) {
3968
+ return {
3969
+ url: prior.url,
3970
+ status: 0,
3971
+ headers: {},
3972
+ body: prior.body,
3973
+ error: `JWKS contains no active key with adcp_use="${purpose}"`,
3974
+ };
3975
+ }
3976
+ return {
3977
+ url: prior.url,
3978
+ status: 200,
3979
+ headers: prior.headers,
3980
+ body: { purpose, matching_key_count: matching.length },
3981
+ };
3982
+ }
3673
3983
  function findPriorProbe(priorStepResults) {
3674
3984
  // Fallback for runStoryboardStep where priorProbes isn't populated — reach
3675
3985
  // into the step result's response, which we set to the HttpProbeResult above.