@adcp/sdk 8.1.0-beta.1 → 8.1.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -1
- package/bin/adcp.js +75 -21
- package/dist/lib/conformance/oracle.d.ts.map +1 -1
- package/dist/lib/conformance/oracle.js +8 -1
- package/dist/lib/conformance/oracle.js.map +1 -1
- package/dist/lib/conformance/schemaArbitrary.js +135 -9
- package/dist/lib/conformance/schemaArbitrary.js.map +1 -1
- package/dist/lib/core/AgentClient.d.ts +1 -1
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/AgentClient.js.map +1 -1
- package/dist/lib/core/GovernanceMiddleware.d.ts +2 -1
- package/dist/lib/core/GovernanceMiddleware.d.ts.map +1 -1
- package/dist/lib/core/GovernanceMiddleware.js +9 -2
- package/dist/lib/core/GovernanceMiddleware.js.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.ts +6 -0
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +2 -3
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/core/TaskExecutor.d.ts +1 -0
- package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
- package/dist/lib/core/TaskExecutor.js +5 -1
- package/dist/lib/core/TaskExecutor.js.map +1 -1
- package/dist/lib/protocols/index.d.ts +10 -2
- package/dist/lib/protocols/index.d.ts.map +1 -1
- package/dist/lib/protocols/index.js +7 -6
- package/dist/lib/protocols/index.js.map +1 -1
- package/dist/lib/schemas/index.d.ts +68 -10
- package/dist/lib/schemas/index.d.ts.map +1 -1
- package/dist/lib/schemas/index.js +57 -16
- package/dist/lib/schemas/index.js.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/create-adcp-server.d.ts +59 -53
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +62 -10
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/decisioning/account.d.ts +22 -6
- package/dist/lib/server/decisioning/account.d.ts.map +1 -1
- package/dist/lib/server/decisioning/account.js.map +1 -1
- package/dist/lib/server/decisioning/capabilities.d.ts +8 -0
- package/dist/lib/server/decisioning/capabilities.d.ts.map +1 -1
- package/dist/lib/server/decisioning/index.d.ts +12 -11
- package/dist/lib/server/decisioning/index.d.ts.map +1 -1
- package/dist/lib/server/decisioning/index.js.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.d.ts +3 -2
- package/dist/lib/server/decisioning/list-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.js +0 -1
- package/dist/lib/server/decisioning/list-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.d.ts +8 -2
- package/dist/lib/server/decisioning/manifest-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.js +8 -2
- package/dist/lib/server/decisioning/manifest-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/dispatch.d.ts +3 -2
- package/dist/lib/server/decisioning/proposal/dispatch.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/dispatch.js +1 -0
- package/dist/lib/server/decisioning/proposal/dispatch.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.d.ts +3 -2
- package/dist/lib/server/decisioning/proposal/mock-manager.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.d.ts +3 -2
- package/dist/lib/server/decisioning/proposal/types.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.js +14 -1
- package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/audiences.d.ts +4 -1
- package/dist/lib/server/decisioning/specialisms/audiences.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts +19 -7
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/campaign-governance.d.ts +9 -4
- package/dist/lib/server/decisioning/specialisms/campaign-governance.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/content-standards.d.ts +17 -8
- package/dist/lib/server/decisioning/specialisms/content-standards.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts +13 -5
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/creative.d.ts +14 -9
- package/dist/lib/server/decisioning/specialisms/creative.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/lists.d.ts +21 -10
- package/dist/lib/server/decisioning/specialisms/lists.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/sales.d.ts +27 -12
- package/dist/lib/server/decisioning/specialisms/sales.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/signals.d.ts +5 -2
- package/dist/lib/server/decisioning/specialisms/signals.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.d.ts +9 -4
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.d.ts.map +1 -1
- package/dist/lib/server/index.d.ts +1 -0
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/operational-platform.d.ts +4 -3
- package/dist/lib/server/operational-platform.d.ts.map +1 -1
- package/dist/lib/server/operational-platform.js.map +1 -1
- package/dist/lib/server/responses.d.ts +28 -27
- package/dist/lib/server/responses.d.ts.map +1 -1
- package/dist/lib/server/responses.js +96 -35
- package/dist/lib/server/responses.js.map +1 -1
- package/dist/lib/signing/types.d.ts +6 -0
- package/dist/lib/signing/types.d.ts.map +1 -1
- package/dist/lib/signing/types.js.map +1 -1
- package/dist/lib/signing/verifier.d.ts.map +1 -1
- package/dist/lib/signing/verifier.js +33 -4
- package/dist/lib/signing/verifier.js.map +1 -1
- package/dist/lib/testing/client.d.ts +5 -0
- package/dist/lib/testing/client.d.ts.map +1 -1
- package/dist/lib/testing/client.js +33 -2
- package/dist/lib/testing/client.js.map +1 -1
- package/dist/lib/testing/compliance/comply.d.ts +8 -1
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
- package/dist/lib/testing/compliance/comply.js +57 -25
- package/dist/lib/testing/compliance/comply.js.map +1 -1
- package/dist/lib/testing/compliance/types.d.ts +2 -0
- package/dist/lib/testing/compliance/types.d.ts.map +1 -1
- package/dist/lib/testing/index.d.ts +1 -1
- package/dist/lib/testing/index.d.ts.map +1 -1
- package/dist/lib/testing/index.js +4 -1
- package/dist/lib/testing/index.js.map +1 -1
- package/dist/lib/testing/storyboard/compliance.d.ts +11 -1
- package/dist/lib/testing/storyboard/compliance.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/compliance.js +37 -3
- package/dist/lib/testing/storyboard/compliance.js.map +1 -1
- package/dist/lib/testing/storyboard/index.d.ts +2 -2
- package/dist/lib/testing/storyboard/index.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/index.js +6 -2
- package/dist/lib/testing/storyboard/index.js.map +1 -1
- package/dist/lib/testing/storyboard/probes.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/probes.js +3 -0
- package/dist/lib/testing/storyboard/probes.js.map +1 -1
- package/dist/lib/testing/storyboard/request-builder.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/request-builder.js +4 -1
- package/dist/lib/testing/storyboard/request-builder.js.map +1 -1
- package/dist/lib/testing/storyboard/runner.d.ts +2 -0
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +335 -43
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/testing/storyboard/types.d.ts +65 -0
- package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/types.js.map +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +4 -3
- package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/validations.js +26 -2
- package/dist/lib/testing/storyboard/validations.js.map +1 -1
- package/dist/lib/testing/types.d.ts +19 -0
- package/dist/lib/testing/types.d.ts.map +1 -1
- package/dist/lib/types/activate-signal.d.ts +647 -0
- package/dist/lib/types/build-creative.d.ts +2105 -0
- package/dist/lib/types/calibrate-content.d.ts +675 -0
- package/dist/lib/types/check-governance.d.ts +619 -0
- package/dist/lib/types/comply-test-controller.d.ts +8428 -0
- package/dist/lib/types/core.generated.d.ts +547 -537
- package/dist/lib/types/core.generated.d.ts.map +1 -1
- package/dist/lib/types/core.generated.js +1 -1
- package/dist/lib/types/create-collection-list.d.ts +693 -0
- package/dist/lib/types/create-content-standards.d.ts +830 -0
- package/dist/lib/types/create-media-buy.d.ts +3374 -0
- package/dist/lib/types/create-property-list.d.ts +836 -0
- package/dist/lib/types/delete-collection-list.d.ts +497 -0
- package/dist/lib/types/delete-property-list.d.ts +497 -0
- package/dist/lib/types/get-account-financials.d.ts +624 -0
- package/dist/lib/types/get-adcp-capabilities.d.ts +2863 -0
- package/dist/lib/types/get-collection-list.d.ts +763 -0
- package/dist/lib/types/get-content-standards.d.ts +919 -0
- package/dist/lib/types/get-creative-delivery.d.ts +2219 -0
- package/dist/lib/types/get-creative-features.d.ts +1736 -0
- package/dist/lib/types/get-media-buy-artifacts.d.ts +864 -0
- package/dist/lib/types/get-media-buys.d.ts +1670 -0
- package/dist/lib/types/get-plan-audit-logs.d.ts +455 -0
- package/dist/lib/types/get-products.d.ts +4935 -0
- package/dist/lib/types/get-property-list.d.ts +874 -0
- package/dist/lib/types/get-signals.d.ts +986 -0
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/lib/types/index.d.ts.map +1 -1
- package/dist/lib/types/index.js.map +1 -1
- package/dist/lib/types/inline-enums.generated.d.ts +163 -7
- package/dist/lib/types/inline-enums.generated.d.ts.map +1 -1
- package/dist/lib/types/inline-enums.generated.js +222 -9
- package/dist/lib/types/inline-enums.generated.js.map +1 -1
- package/dist/lib/types/list-accounts.d.ts +851 -0
- package/dist/lib/types/list-content-standards.d.ts +975 -0
- package/dist/lib/types/list-creative-formats.d.ts +3132 -0
- package/dist/lib/types/list-creatives.d.ts +2390 -0
- package/dist/lib/types/list-property-lists.d.ts +855 -0
- package/dist/lib/types/log-event.d.ts +373 -0
- package/dist/lib/types/per-tool-index.json +391 -0
- package/dist/lib/types/preview-creative.d.ts +1981 -0
- package/dist/lib/types/provide-performance-feedback.d.ts +218 -0
- package/dist/lib/types/report-plan-outcome.d.ts +433 -0
- package/dist/lib/types/report-usage.d.ts +579 -0
- package/dist/lib/types/schemas.generated.d.ts +146765 -128986
- package/dist/lib/types/schemas.generated.d.ts.map +1 -1
- package/dist/lib/types/schemas.generated.js +405 -437
- package/dist/lib/types/schemas.generated.js.map +1 -1
- package/dist/lib/types/server-payload.d.ts +39 -0
- package/dist/lib/types/server-payload.d.ts.map +1 -0
- package/dist/lib/types/server-payload.js +11 -0
- package/dist/lib/types/server-payload.js.map +1 -0
- package/dist/lib/types/si-get-offering.d.ts +259 -0
- package/dist/lib/types/si-initiate-session.d.ts +372 -0
- package/dist/lib/types/si-send-message.d.ts +300 -0
- package/dist/lib/types/si-terminate-session.d.ts +213 -0
- package/dist/lib/types/sync-accounts.d.ts +856 -0
- package/dist/lib/types/sync-audiences.d.ts +707 -0
- package/dist/lib/types/sync-catalogs.d.ts +766 -0
- package/dist/lib/types/sync-creatives.d.ts +2134 -0
- package/dist/lib/types/sync-event-sources.d.ts +665 -0
- package/dist/lib/types/sync-governance.d.ts +558 -0
- package/dist/lib/types/sync-plans.d.ts +979 -0
- package/dist/lib/types/tools.generated.d.ts +236 -188
- package/dist/lib/types/tools.generated.d.ts.map +1 -1
- package/dist/lib/types/update-collection-list.d.ts +697 -0
- package/dist/lib/types/update-content-standards.d.ts +847 -0
- package/dist/lib/types/update-media-buy.d.ts +3047 -0
- package/dist/lib/types/update-property-list.d.ts +840 -0
- package/dist/lib/types/validate-content-delivery.d.ts +722 -0
- package/dist/lib/types/validate-input.d.ts +1683 -0
- package/dist/lib/utils/adcp-version-config.d.ts +2 -0
- package/dist/lib/utils/adcp-version-config.d.ts.map +1 -1
- package/dist/lib/utils/adcp-version-config.js +42 -0
- package/dist/lib/utils/adcp-version-config.js.map +1 -1
- package/dist/lib/utils/response-schemas.d.ts.map +1 -1
- package/dist/lib/utils/response-schemas.js +3 -0
- package/dist/lib/utils/response-schemas.js.map +1 -1
- package/dist/lib/utils/response-unwrapper.d.ts.map +1 -1
- package/dist/lib/utils/response-unwrapper.js +28 -4
- package/dist/lib/utils/response-unwrapper.js.map +1 -1
- package/dist/lib/utils/tool-request-schemas.d.ts +8574 -1
- package/dist/lib/utils/tool-request-schemas.d.ts.map +1 -1
- package/dist/lib/utils/tool-request-schemas.js +4 -5
- package/dist/lib/utils/tool-request-schemas.js.map +1 -1
- package/dist/lib/utils/union-errors.d.ts +13 -6
- package/dist/lib/utils/union-errors.d.ts.map +1 -1
- package/dist/lib/utils/union-errors.js +34 -7
- package/dist/lib/utils/union-errors.js.map +1 -1
- package/dist/lib/validation/schema-loader.d.ts.map +1 -1
- package/dist/lib/validation/schema-loader.js +68 -15
- package/dist/lib/validation/schema-loader.js.map +1 -1
- package/dist/lib/version.d.ts +27 -3
- package/dist/lib/version.d.ts.map +1 -1
- package/dist/lib/version.js +42 -3
- package/dist/lib/version.js.map +1 -1
- package/examples/error-compliant-server.ts +1 -1
- package/examples/hello_seller_adapter_guaranteed.ts +14 -10
- package/examples/hello_seller_adapter_multi_tenant.ts +27 -23
- package/examples/hello_seller_adapter_non_guaranteed.ts +16 -14
- package/examples/hello_seller_adapter_proposal_mode.ts +22 -6
- package/examples/hello_signals_adapter_marketplace.ts +34 -3
- package/package.json +9 -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;
|
|
@@ -39,7 +41,9 @@ const request_builder_1 = require("./request-builder");
|
|
|
39
41
|
const client_2 = require("../client");
|
|
40
42
|
const idempotency_1 = require("../../utils/idempotency");
|
|
41
43
|
const schema_loader_1 = require("../../validation/schema-loader");
|
|
44
|
+
const version_1 = require("../../version");
|
|
42
45
|
const probes_1 = require("./probes");
|
|
46
|
+
const capabilities_types_1 = require("../../signing/agent-resolver/capabilities-types");
|
|
43
47
|
const test_kit_1 = require("./test-kit");
|
|
44
48
|
const loader_1 = require("./loader");
|
|
45
49
|
const probe_dispatch_1 = require("./request-signing/probe-dispatch");
|
|
@@ -65,6 +69,26 @@ const SKIP_DETAILS = {
|
|
|
65
69
|
};
|
|
66
70
|
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
71
|
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.';
|
|
72
|
+
function applyStoryboardVersionOptions(storyboard, options) {
|
|
73
|
+
return applyAdcpVersionRunOptions(storyboard.adcp_version, options);
|
|
74
|
+
}
|
|
75
|
+
function applyAdcpVersionRunOptions(defaultAdcpVersion, options) {
|
|
76
|
+
const adcpVersion = options.adcpVersion ?? defaultAdcpVersion;
|
|
77
|
+
if (adcpVersion === undefined)
|
|
78
|
+
return options;
|
|
79
|
+
const versionEnvelope = options.versionEnvelope ?? storyboardVersionEnvelopeMode(adcpVersion);
|
|
80
|
+
if (options.adcpVersion === adcpVersion && options.versionEnvelope === versionEnvelope) {
|
|
81
|
+
return options;
|
|
82
|
+
}
|
|
83
|
+
return { ...options, adcpVersion, versionEnvelope };
|
|
84
|
+
}
|
|
85
|
+
function storyboardVersionEnvelopeMode(adcpVersion) {
|
|
86
|
+
const bundleKey = (0, schema_loader_1.resolveBundleKey)(adcpVersion);
|
|
87
|
+
const major = (0, version_1.parseAdcpMajorVersion)(bundleKey);
|
|
88
|
+
if (Number.isFinite(major) && major < 3)
|
|
89
|
+
return 'none';
|
|
90
|
+
return 'auto';
|
|
91
|
+
}
|
|
68
92
|
/**
|
|
69
93
|
* Suffix appended to the skip detail when the sole-stateful-step exemption
|
|
70
94
|
* fires (`not_applicable` / `missing_tool` / `missing_test_controller` on
|
|
@@ -179,6 +203,107 @@ function evaluateCapabilityPredicate(predicate, actual) {
|
|
|
179
203
|
function buildSkip(reason, detail) {
|
|
180
204
|
return { reason, detail: detail ?? SKIP_DETAILS[reason] };
|
|
181
205
|
}
|
|
206
|
+
function detectResponseDerivedNotApplicable(step, request, response, runState, allSteps) {
|
|
207
|
+
if (response === undefined || response === null)
|
|
208
|
+
return null;
|
|
209
|
+
const gates = [
|
|
210
|
+
...normalizeResponseNotApplicableGates(step.not_applicable_if),
|
|
211
|
+
...inferImplicitResponseNotApplicableGates(step, request, runState, allSteps),
|
|
212
|
+
];
|
|
213
|
+
for (const gate of gates) {
|
|
214
|
+
if (gate.kind !== 'terminal_page')
|
|
215
|
+
continue;
|
|
216
|
+
if (!terminalPageGateMatches(gate, request, response))
|
|
217
|
+
continue;
|
|
218
|
+
const detail = gate.detail ??
|
|
219
|
+
`${gate.reason ?? 'single_page_result'}: ${step.task} response is terminal; cursor-walk not applicable`;
|
|
220
|
+
return {
|
|
221
|
+
detail,
|
|
222
|
+
contextKeys: responseNotApplicableContextKeys(step, gate),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
function normalizeResponseNotApplicableGates(gates) {
|
|
228
|
+
if (!gates)
|
|
229
|
+
return [];
|
|
230
|
+
return Array.isArray(gates) ? gates : [gates];
|
|
231
|
+
}
|
|
232
|
+
function inferImplicitResponseNotApplicableGates(step, request, runState, allSteps) {
|
|
233
|
+
// Back-compat for the already-published pagination_integrity_list_accounts
|
|
234
|
+
// storyboard: it expresses the continuation requirement as validations
|
|
235
|
+
// rather than a dedicated response-derived gate. Keep this narrowly scoped
|
|
236
|
+
// to list_accounts so other seeded pagination storyboards do not silently
|
|
237
|
+
// waive fixture/setup mistakes.
|
|
238
|
+
if (step.task !== 'list_accounts')
|
|
239
|
+
return [];
|
|
240
|
+
const expectsContinuation = step.validations?.some(v => v.check === 'field_value' && v.path === 'pagination.has_more' && v.value === true);
|
|
241
|
+
const capturesCursor = step.context_outputs?.some(o => o.path === 'pagination.cursor');
|
|
242
|
+
const validatesCursor = step.validations?.some(v => v.check === 'field_present' && v.path === 'pagination.cursor');
|
|
243
|
+
if (!expectsContinuation || (!capturesCursor && !validatesCursor))
|
|
244
|
+
return [];
|
|
245
|
+
const maxResults = (0, path_1.resolvePath)(request, 'pagination.max_results');
|
|
246
|
+
if (typeof maxResults === 'number' &&
|
|
247
|
+
Number.isFinite(maxResults) &&
|
|
248
|
+
hasUnrunAccountSeedExceedingPageSize(allSteps, runState, maxResults)) {
|
|
249
|
+
return [];
|
|
250
|
+
}
|
|
251
|
+
const setupAccounts = (0, path_1.resolvePath)(runState?.priorStepResults.get('sync_three_accounts')?.response, 'accounts');
|
|
252
|
+
if (typeof maxResults === 'number' &&
|
|
253
|
+
Number.isFinite(maxResults) &&
|
|
254
|
+
Array.isArray(setupAccounts) &&
|
|
255
|
+
setupAccounts.length > maxResults) {
|
|
256
|
+
return [];
|
|
257
|
+
}
|
|
258
|
+
return [{ kind: 'terminal_page', items_path: 'accounts', reason: 'single_page_result' }];
|
|
259
|
+
}
|
|
260
|
+
function hasUnrunAccountSeedExceedingPageSize(allSteps, runState, maxResults) {
|
|
261
|
+
return (allSteps?.some(({ step }) => {
|
|
262
|
+
if (runState?.priorStepResults.has(step.id))
|
|
263
|
+
return false;
|
|
264
|
+
if (step.task !== 'sync_accounts')
|
|
265
|
+
return false;
|
|
266
|
+
const accounts = (0, path_1.resolvePath)(step.sample_request, 'accounts');
|
|
267
|
+
return Array.isArray(accounts) && accounts.length > maxResults;
|
|
268
|
+
}) ?? false);
|
|
269
|
+
}
|
|
270
|
+
function terminalPageGateMatches(gate, request, response) {
|
|
271
|
+
const maxResults = (0, path_1.resolvePath)(request, gate.request_max_results_path ?? 'pagination.max_results');
|
|
272
|
+
if (typeof maxResults !== 'number' || !Number.isFinite(maxResults) || maxResults <= 0)
|
|
273
|
+
return false;
|
|
274
|
+
const items = (0, path_1.resolvePath)(response, gate.items_path);
|
|
275
|
+
if (!Array.isArray(items))
|
|
276
|
+
return false;
|
|
277
|
+
const pagination = (0, path_1.resolvePath)(response, 'pagination');
|
|
278
|
+
if (pagination === undefined || pagination === null)
|
|
279
|
+
return items.length < maxResults;
|
|
280
|
+
if (typeof pagination !== 'object' || Array.isArray(pagination))
|
|
281
|
+
return false;
|
|
282
|
+
const p = pagination;
|
|
283
|
+
if (p.has_more === true)
|
|
284
|
+
return false;
|
|
285
|
+
if (p.has_more !== false)
|
|
286
|
+
return false;
|
|
287
|
+
if (typeof p.total_count === 'number' && p.total_count > items.length)
|
|
288
|
+
return false;
|
|
289
|
+
if (items.length < maxResults)
|
|
290
|
+
return true;
|
|
291
|
+
return typeof p.total_count === 'number' && p.total_count <= items.length;
|
|
292
|
+
}
|
|
293
|
+
function responseNotApplicableContextKeys(step, gate) {
|
|
294
|
+
if (gate.context_keys?.length)
|
|
295
|
+
return gate.context_keys;
|
|
296
|
+
return (step.context_outputs ?? [])
|
|
297
|
+
.filter(o => o.path === 'pagination.cursor')
|
|
298
|
+
.map(o => o.key)
|
|
299
|
+
.filter((key) => typeof key === 'string' && key.length > 0);
|
|
300
|
+
}
|
|
301
|
+
function responseDerivedContextResult(runState) {
|
|
302
|
+
const entries = runState.responseDerivedNotApplicableContextKeys;
|
|
303
|
+
return entries && entries.size > 0
|
|
304
|
+
? { response_derived_not_applicable_context_keys: Object.fromEntries(entries) }
|
|
305
|
+
: {};
|
|
306
|
+
}
|
|
182
307
|
/**
|
|
183
308
|
* True for skip reasons that imply state genuinely never materialized
|
|
184
309
|
* — no other code path could have established it. The runner trips
|
|
@@ -571,6 +696,7 @@ function filterResponseHeaders(headers) {
|
|
|
571
696
|
* empty on instance B.
|
|
572
697
|
*/
|
|
573
698
|
async function runStoryboard(agentUrlOrUrls, storyboard, options = {}) {
|
|
699
|
+
options = applyStoryboardVersionOptions(storyboard, options);
|
|
574
700
|
(0, test_kit_1.validateTestKit)(options.test_kit);
|
|
575
701
|
// Enforce authoring-time branch_set invariants regardless of how the
|
|
576
702
|
// storyboard reached us. YAML callers already ran these rules in
|
|
@@ -1211,6 +1337,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
|
|
|
1211
1337
|
let clients;
|
|
1212
1338
|
let routingContext;
|
|
1213
1339
|
let profile;
|
|
1340
|
+
let callerOwnsClients = false;
|
|
1214
1341
|
if (useRouting) {
|
|
1215
1342
|
try {
|
|
1216
1343
|
routingContext = await (0, agent_routing_1.buildRoutingContext)(storyboard, options);
|
|
@@ -1225,8 +1352,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
|
|
|
1225
1352
|
duration_ms: 0,
|
|
1226
1353
|
error: detail,
|
|
1227
1354
|
};
|
|
1228
|
-
|
|
1229
|
-
await (0, protocols_1.closeConnections)(options.protocol);
|
|
1355
|
+
await (0, protocols_1.closeConnections)(options.protocol);
|
|
1230
1356
|
return buildDiscoveryFailedResult(agentUrls, storyboard, failedStep);
|
|
1231
1357
|
}
|
|
1232
1358
|
clients = [...routingContext.clients.values()];
|
|
@@ -1258,7 +1384,9 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
|
|
|
1258
1384
|
else {
|
|
1259
1385
|
// Build one client per URL. In single-URL mode `_client` (from comply()) is
|
|
1260
1386
|
// honored so the shared MCP transport is reused across storyboards.
|
|
1261
|
-
|
|
1387
|
+
const clientResolutions = agentUrls.map(url => (0, client_1.getOrCreateClientResolution)(url, options));
|
|
1388
|
+
clients = clientResolutions.map(r => r.client);
|
|
1389
|
+
callerOwnsClients = clientResolutions.some(r => r.reusedShared);
|
|
1262
1390
|
// Drop any retained A2A session ids before this storyboard's first call.
|
|
1263
1391
|
// `comply()` shares one client across N storyboards for transport reuse;
|
|
1264
1392
|
// AgentClient.retainSession holds onto `pendingTaskId` from non-terminal
|
|
@@ -1273,7 +1401,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
|
|
|
1273
1401
|
// expected to run the same code behind a shared state store, so one probe
|
|
1274
1402
|
// is sufficient. For multi-instance runs, skipping N-1 redundant
|
|
1275
1403
|
// get_agent_info calls also keeps CI output clean.
|
|
1276
|
-
if (!
|
|
1404
|
+
if (!callerOwnsClients) {
|
|
1277
1405
|
const discovered = await (0, client_1.getOrDiscoverProfile)(clients[0], options);
|
|
1278
1406
|
// Discovery failure must surface as a HARD STORYBOARD FAILURE, not a
|
|
1279
1407
|
// silent empty `agentTools: []` that lets every step skip with
|
|
@@ -1282,8 +1410,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
|
|
|
1282
1410
|
// (auth misconfig, MCP transport-fallback bugs, network policy, etc.).
|
|
1283
1411
|
// See: https://github.com/adcontextprotocol/adcp-client/issues/...
|
|
1284
1412
|
if (discovered.step.passed === false) {
|
|
1285
|
-
|
|
1286
|
-
await (0, protocols_1.closeConnections)(options.protocol);
|
|
1413
|
+
await (0, protocols_1.closeConnections)(options.protocol);
|
|
1287
1414
|
return buildDiscoveryFailedResult(agentUrls, storyboard, discovered.step);
|
|
1288
1415
|
}
|
|
1289
1416
|
profile = discovered.profile;
|
|
@@ -1329,7 +1456,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
|
|
|
1329
1456
|
if (allRequires.length) {
|
|
1330
1457
|
const unmet = checkRequires(allRequires, options, profile);
|
|
1331
1458
|
if (unmet) {
|
|
1332
|
-
if (!
|
|
1459
|
+
if (!callerOwnsClients)
|
|
1333
1460
|
await (0, protocols_1.closeConnections)(options.protocol);
|
|
1334
1461
|
return {
|
|
1335
1462
|
...buildRequirementUnmetResult(agentUrls, storyboard, unmet.requirement, unmet.detail),
|
|
@@ -1348,7 +1475,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
|
|
|
1348
1475
|
const actual = resolveCapabilityPath(rawCaps, cap.path);
|
|
1349
1476
|
const unmetDetail = evaluateCapabilityPredicate(cap, actual);
|
|
1350
1477
|
if (unmetDetail !== null) {
|
|
1351
|
-
if (!
|
|
1478
|
+
if (!callerOwnsClients)
|
|
1352
1479
|
await (0, protocols_1.closeConnections)(options.protocol);
|
|
1353
1480
|
return {
|
|
1354
1481
|
...buildCapabilityUnsupportedResult(agentUrls, storyboard, unmetDetail),
|
|
@@ -1371,7 +1498,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
|
|
|
1371
1498
|
if (storyboard.required_tools?.length && options.agentTools) {
|
|
1372
1499
|
const hasAnyRequired = storyboard.required_tools.some(t => options.agentTools.includes(t));
|
|
1373
1500
|
if (!hasAnyRequired) {
|
|
1374
|
-
if (!
|
|
1501
|
+
if (!callerOwnsClients)
|
|
1375
1502
|
await (0, protocols_1.closeConnections)(options.protocol);
|
|
1376
1503
|
return {
|
|
1377
1504
|
...buildRequiredToolsMissingResult(agentUrls, storyboard, `agent does not advertise any of [${storyboard.required_tools.join(', ')}]`),
|
|
@@ -1394,6 +1521,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
|
|
|
1394
1521
|
const contextProvenance = new Map();
|
|
1395
1522
|
const priorA2aEnvelopes = new Map();
|
|
1396
1523
|
const stepRequestStarts = new Map();
|
|
1524
|
+
const responseDerivedNotApplicableContextKeys = new Map();
|
|
1397
1525
|
const phaseResults = [];
|
|
1398
1526
|
let passedCount = 0;
|
|
1399
1527
|
let failedCount = 0;
|
|
@@ -1863,6 +1991,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
|
|
|
1863
1991
|
contextProvenance,
|
|
1864
1992
|
priorA2aEnvelopes,
|
|
1865
1993
|
stepRequestStarts,
|
|
1994
|
+
responseDerivedNotApplicableContextKeys,
|
|
1866
1995
|
agentLibraryVersion: profile?.library_version,
|
|
1867
1996
|
});
|
|
1868
1997
|
const result = { ...rawResult, storyboard_id: storyboard.id };
|
|
@@ -2323,7 +2452,7 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
|
|
|
2323
2452
|
// Close protocol connections when the runner created its own client. The
|
|
2324
2453
|
// connection pool is keyed by URL+auth, so a single closeConnections() call
|
|
2325
2454
|
// evicts every instance's transport regardless of how many URLs we used.
|
|
2326
|
-
if (!
|
|
2455
|
+
if (!callerOwnsClients) {
|
|
2327
2456
|
await (0, protocols_1.closeConnections)(options.protocol);
|
|
2328
2457
|
}
|
|
2329
2458
|
if (webhookReceiver)
|
|
@@ -2361,7 +2490,7 @@ async function runMultiPass(agentUrls, storyboard, options) {
|
|
|
2361
2490
|
// only the first pass attaches the synthetic `__controller_seeding__`
|
|
2362
2491
|
// phase to its `phaseResults`, so the aggregated top-level counts reflect
|
|
2363
2492
|
// a single seeding pass across the whole run.
|
|
2364
|
-
const preSeedClients = agentUrls.map(url => (0, client_1.
|
|
2493
|
+
const preSeedClients = agentUrls.map(url => (0, client_1.getOrCreateClientResolution)(url, options).client);
|
|
2365
2494
|
const preSeedContext = { ...options.context };
|
|
2366
2495
|
const preSeededResult = await (0, seeding_1.runControllerSeeding)(preSeedClients[0], storyboard, options, preSeedContext);
|
|
2367
2496
|
const passes = [];
|
|
@@ -2558,15 +2687,17 @@ function summarizeStrictValidation(phases) {
|
|
|
2558
2687
|
* Context is passed in and returned, enabling step-by-step orchestration.
|
|
2559
2688
|
*/
|
|
2560
2689
|
async function runStoryboardStep(agentUrl, storyboard, stepId, options = {}) {
|
|
2690
|
+
options = applyStoryboardVersionOptions(storyboard, options);
|
|
2561
2691
|
(0, test_kit_1.validateTestKit)(options.test_kit);
|
|
2562
|
-
const
|
|
2692
|
+
const clientResolution = (0, client_1.getOrCreateClientResolution)(agentUrl, options);
|
|
2693
|
+
const client = clientResolution.client;
|
|
2563
2694
|
// Discover agent profile for standalone step execution. Captured so the
|
|
2564
2695
|
// executeStep call below can thread `library_version` through to
|
|
2565
2696
|
// shape-drift hint detection (issue #850). Also threads _profile into
|
|
2566
2697
|
// options so capability-based skip gates in executeStep (e.g. account-mode
|
|
2567
2698
|
// branching) can read raw_capabilities, mirroring executeStoryboardPass.
|
|
2568
2699
|
let profile;
|
|
2569
|
-
if (!
|
|
2700
|
+
if (!clientResolution.reusedShared) {
|
|
2570
2701
|
const discovered = await (0, client_1.getOrDiscoverProfile)(client, options);
|
|
2571
2702
|
profile = discovered.profile;
|
|
2572
2703
|
if (profile && !options._profile) {
|
|
@@ -2612,6 +2743,7 @@ async function runStoryboardStep(agentUrl, storyboard, stepId, options = {}) {
|
|
|
2612
2743
|
// previous step's result). Storyboard-level runs build this internally;
|
|
2613
2744
|
// here the caller owns accumulation across stateless invocations.
|
|
2614
2745
|
const contextProvenance = new Map(Object.entries(options.context_provenance ?? {}));
|
|
2746
|
+
const responseDerivedNotApplicableContextKeys = new Map(Object.entries(options.response_derived_not_applicable_context_keys ?? {}));
|
|
2615
2747
|
const result = await executeStep(client, found.step, found.phaseId, context, allSteps, options, {
|
|
2616
2748
|
contributions: new Set(),
|
|
2617
2749
|
priorStepResults: new Map(),
|
|
@@ -2622,9 +2754,10 @@ async function runStoryboardStep(agentUrl, storyboard, stepId, options = {}) {
|
|
|
2622
2754
|
contextProvenance,
|
|
2623
2755
|
priorA2aEnvelopes: new Map(),
|
|
2624
2756
|
stepRequestStarts: new Map(),
|
|
2757
|
+
responseDerivedNotApplicableContextKeys,
|
|
2625
2758
|
agentLibraryVersion: profile?.library_version,
|
|
2626
2759
|
});
|
|
2627
|
-
if (!
|
|
2760
|
+
if (!clientResolution.reusedShared) {
|
|
2628
2761
|
await (0, protocols_1.closeConnections)(options.protocol);
|
|
2629
2762
|
}
|
|
2630
2763
|
if (ownsWebhookReceiver && webhookReceiver)
|
|
@@ -2642,6 +2775,7 @@ client, step, phaseId, context, allSteps, options, state) {
|
|
|
2642
2775
|
agentUrl: '',
|
|
2643
2776
|
contextProvenance: new Map(),
|
|
2644
2777
|
stepRequestStarts: new Map(),
|
|
2778
|
+
responseDerivedNotApplicableContextKeys: new Map(),
|
|
2645
2779
|
};
|
|
2646
2780
|
// HTTP probe tasks bypass the MCP client entirely.
|
|
2647
2781
|
if (probes_1.PROBE_TASKS.has(step.task)) {
|
|
@@ -2810,45 +2944,50 @@ client, step, phaseId, context, allSteps, options, state) {
|
|
|
2810
2944
|
const unresolvedVars = findUnresolvedContextVars(request);
|
|
2811
2945
|
if (unresolvedVars.length > 0 && !step.expect_error) {
|
|
2812
2946
|
const next = getNextStepPreview(step.id, allSteps, context, runState.runnerVars);
|
|
2813
|
-
const
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
//
|
|
2821
|
-
//
|
|
2822
|
-
|
|
2947
|
+
const responseDerivedDetails = unresolvedVars
|
|
2948
|
+
.map(v => runState.responseDerivedNotApplicableContextKeys?.get(v.key))
|
|
2949
|
+
.filter((d) => typeof d === 'string');
|
|
2950
|
+
const allResponseDerived = responseDerivedDetails.length === unresolvedVars.length && responseDerivedDetails.length > 0;
|
|
2951
|
+
const detail = allResponseDerived
|
|
2952
|
+
? [...new Set(responseDerivedDetails)].join('; ')
|
|
2953
|
+
: `Skipped: unresolved context variables from prior steps: ${unresolvedVars.map(v => v.key).join(', ')}.`;
|
|
2954
|
+
// Normal unresolved substitutions carry one validation result per missing
|
|
2955
|
+
// token. Response-derived terminal-page skips are already successful
|
|
2956
|
+
// not_applicable rows, so their downstream cursor consumers stay validation
|
|
2957
|
+
// empty to avoid inventing a failing-looking check for an expected skip.
|
|
2823
2958
|
const synthesized = [];
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2959
|
+
if (!allResponseDerived) {
|
|
2960
|
+
const seenTokens = new Set();
|
|
2961
|
+
for (const v of unresolvedVars) {
|
|
2962
|
+
if (seenTokens.has(v.token))
|
|
2963
|
+
continue;
|
|
2964
|
+
seenTokens.add(v.token);
|
|
2965
|
+
synthesized.push({
|
|
2966
|
+
check: 'unresolved_substitution',
|
|
2967
|
+
passed: false,
|
|
2968
|
+
description: `request token "${v.token}" did not resolve — prior step did not populate context.${v.key}`,
|
|
2969
|
+
json_pointer: null,
|
|
2970
|
+
expected: v.token,
|
|
2971
|
+
actual: null,
|
|
2972
|
+
schema_id: null,
|
|
2973
|
+
schema_url: null,
|
|
2974
|
+
});
|
|
2975
|
+
}
|
|
2838
2976
|
}
|
|
2839
2977
|
return {
|
|
2840
2978
|
step_id: step.id,
|
|
2841
2979
|
phase_id: phaseId,
|
|
2842
2980
|
title: step.title,
|
|
2843
2981
|
task: step.task,
|
|
2844
|
-
passed:
|
|
2982
|
+
passed: allResponseDerived,
|
|
2845
2983
|
skipped: true,
|
|
2846
|
-
skip_reason: 'prerequisite_failed',
|
|
2847
|
-
skip: buildSkip('prerequisite_failed', detail),
|
|
2984
|
+
skip_reason: allResponseDerived ? 'not_applicable' : 'prerequisite_failed',
|
|
2985
|
+
skip: buildSkip(allResponseDerived ? 'not_applicable' : 'prerequisite_failed', detail),
|
|
2848
2986
|
duration_ms: 0,
|
|
2849
2987
|
validations: synthesized,
|
|
2850
2988
|
context,
|
|
2851
|
-
|
|
2989
|
+
...responseDerivedContextResult(runState),
|
|
2990
|
+
...(!allResponseDerived && { error: detail }),
|
|
2852
2991
|
next,
|
|
2853
2992
|
extraction: { path: 'none' },
|
|
2854
2993
|
};
|
|
@@ -3203,6 +3342,32 @@ client, step, phaseId, context, allSteps, options, state) {
|
|
|
3203
3342
|
if (step.expect_error && !taskResult?.data && taskResult?.error) {
|
|
3204
3343
|
taskResult = { ...taskResult, data: { error: taskResult.error } };
|
|
3205
3344
|
}
|
|
3345
|
+
const responseDerivedSkip = detectResponseDerivedNotApplicable(effectiveStep, request, taskResult?.data, runState, allSteps);
|
|
3346
|
+
if (responseDerivedSkip && !step.expect_error) {
|
|
3347
|
+
for (const key of responseDerivedSkip.contextKeys) {
|
|
3348
|
+
runState.responseDerivedNotApplicableContextKeys?.set(key, responseDerivedSkip.detail);
|
|
3349
|
+
}
|
|
3350
|
+
const next = getNextStepPreview(step.id, allSteps, context, runState.runnerVars);
|
|
3351
|
+
return {
|
|
3352
|
+
step_id: step.id,
|
|
3353
|
+
phase_id: phaseId,
|
|
3354
|
+
title: step.title,
|
|
3355
|
+
task: step.task,
|
|
3356
|
+
passed: true,
|
|
3357
|
+
skipped: true,
|
|
3358
|
+
skip_reason: 'not_applicable',
|
|
3359
|
+
skip: buildSkip('not_applicable', responseDerivedSkip.detail),
|
|
3360
|
+
duration_ms: stepResult.duration_ms,
|
|
3361
|
+
validations: [],
|
|
3362
|
+
context,
|
|
3363
|
+
...responseDerivedContextResult(runState),
|
|
3364
|
+
response: (0, redact_secrets_1.redactSecrets)(taskResult?.data),
|
|
3365
|
+
next,
|
|
3366
|
+
request: requestRecord,
|
|
3367
|
+
...(responseRecord && { response_record: responseRecord }),
|
|
3368
|
+
extraction: extractionFromTaskResult(taskResult),
|
|
3369
|
+
};
|
|
3370
|
+
}
|
|
3206
3371
|
// Determine pass/fail — inverted when expect_error is set
|
|
3207
3372
|
let passed;
|
|
3208
3373
|
if (step.expect_error) {
|
|
@@ -3370,6 +3535,9 @@ client, step, phaseId, context, allSteps, options, state) {
|
|
|
3370
3535
|
if (passed && hasData && taskResult) {
|
|
3371
3536
|
const extracted = (0, context_1.extractContextWithProvenance)(effectiveStep.task, taskResult.data, step.id);
|
|
3372
3537
|
Object.assign(updatedContext, extracted.values);
|
|
3538
|
+
for (const key of Object.keys(extracted.values)) {
|
|
3539
|
+
runState.responseDerivedNotApplicableContextKeys?.delete(key);
|
|
3540
|
+
}
|
|
3373
3541
|
if (runState.contextProvenance) {
|
|
3374
3542
|
for (const [key, entry] of Object.entries(extracted.provenance)) {
|
|
3375
3543
|
runState.contextProvenance.set(key, entry);
|
|
@@ -3387,6 +3555,10 @@ client, step, phaseId, context, allSteps, options, state) {
|
|
|
3387
3555
|
// ensures the minted value from any same-step $generate:…#<key> inline
|
|
3388
3556
|
// substitution is visible here.
|
|
3389
3557
|
if (step.context_outputs?.length) {
|
|
3558
|
+
for (const output of step.context_outputs) {
|
|
3559
|
+
if (output.key)
|
|
3560
|
+
runState.responseDerivedNotApplicableContextKeys?.delete(output.key);
|
|
3561
|
+
}
|
|
3390
3562
|
// Resolve `task_completion.<path>` outputs against the eventual task
|
|
3391
3563
|
// artifact rather than the immediate response. When the immediate
|
|
3392
3564
|
// response is a submitted-arm envelope (HITL / async-signed-IO flows),
|
|
@@ -3413,6 +3585,9 @@ client, step, phaseId, context, allSteps, options, state) {
|
|
|
3413
3585
|
const remappedOutputs = remapTaskCompletionOutputs(step.context_outputs);
|
|
3414
3586
|
const explicit = (0, context_1.applyContextOutputsWithProvenance)(extractionData, remappedOutputs, step.id, effectiveStep.task, updatedContext);
|
|
3415
3587
|
Object.assign(updatedContext, explicit.values);
|
|
3588
|
+
for (const key of Object.keys(explicit.values)) {
|
|
3589
|
+
runState.responseDerivedNotApplicableContextKeys?.delete(key);
|
|
3590
|
+
}
|
|
3416
3591
|
if (runState.contextProvenance) {
|
|
3417
3592
|
for (const [key, entry] of Object.entries(explicit.provenance)) {
|
|
3418
3593
|
runState.contextProvenance.set(key, entry);
|
|
@@ -3543,6 +3718,7 @@ client, step, phaseId, context, allSteps, options, state) {
|
|
|
3543
3718
|
runState.contextProvenance.size > 0 && {
|
|
3544
3719
|
context_provenance: Object.fromEntries(runState.contextProvenance),
|
|
3545
3720
|
}),
|
|
3721
|
+
...responseDerivedContextResult(runState),
|
|
3546
3722
|
error: step.expect_error ? undefined : truncateError(stepResult.error || taskResult?.error),
|
|
3547
3723
|
...(!step.expect_error && taskResult?.adcp_error && { adcp_error: taskResult.adcp_error }),
|
|
3548
3724
|
next,
|
|
@@ -3559,7 +3735,24 @@ async function executeProbeStep(step, phaseId, context, allSteps, options, runSt
|
|
|
3559
3735
|
const start = Date.now();
|
|
3560
3736
|
let httpResult;
|
|
3561
3737
|
const probeOpts = { allowPrivateIp: options.allow_http === true };
|
|
3562
|
-
if (step.
|
|
3738
|
+
if (step.requires_contract) {
|
|
3739
|
+
const contracts = new Set(options.contracts ?? []);
|
|
3740
|
+
if (!contracts.has(step.requires_contract)) {
|
|
3741
|
+
httpResult = {
|
|
3742
|
+
url: runState.agentUrl,
|
|
3743
|
+
status: 0,
|
|
3744
|
+
headers: {},
|
|
3745
|
+
body: null,
|
|
3746
|
+
skipped: true,
|
|
3747
|
+
skip_reason: 'missing_test_kit_contract',
|
|
3748
|
+
error: `Test-kit contract "${step.requires_contract}" is not configured on this runner.`,
|
|
3749
|
+
};
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
if (httpResult) {
|
|
3753
|
+
// Contract-gated synthetic probes self-skip before doing any network work.
|
|
3754
|
+
}
|
|
3755
|
+
else if (step.task === 'protected_resource_metadata') {
|
|
3563
3756
|
httpResult = await (0, probes_1.probeProtectedResourceMetadata)(runState.agentUrl, probeOpts);
|
|
3564
3757
|
// RFC 9728 presence semantics (adcp-client#677): a 404 means the agent is
|
|
3565
3758
|
// honestly not advertising OAuth. Convert to a clean step skip so the
|
|
@@ -3585,6 +3778,21 @@ async function executeProbeStep(step, phaseId, context, allSteps, options, runSt
|
|
|
3585
3778
|
else if (step.task === 'request_signing_probe') {
|
|
3586
3779
|
httpResult = await (0, probe_dispatch_1.probeRequestSigningVector)(step.id, runState.agentUrl, options);
|
|
3587
3780
|
}
|
|
3781
|
+
else if (step.task === 'fetch_brand_jwks') {
|
|
3782
|
+
httpResult = await probeBrandJwks(options._profile?.raw_capabilities, probeOpts);
|
|
3783
|
+
}
|
|
3784
|
+
else if (step.task === 'assert_jwks_purpose') {
|
|
3785
|
+
httpResult = assertJwksPurpose(runState.priorProbes.get('fetch_brand_jwks'), 'webhook-signing');
|
|
3786
|
+
}
|
|
3787
|
+
else if (step.task === 'expect_rate_limit_not_replayed') {
|
|
3788
|
+
httpResult = {
|
|
3789
|
+
url: runState.agentUrl,
|
|
3790
|
+
status: 0,
|
|
3791
|
+
headers: {},
|
|
3792
|
+
body: null,
|
|
3793
|
+
error: 'rate_limit_trip_runner contract is configured, but this SDK runner does not yet implement live rate-limit trip/replay probing.',
|
|
3794
|
+
};
|
|
3795
|
+
}
|
|
3588
3796
|
if (httpResult)
|
|
3589
3797
|
runState.priorProbes.set(step.task, httpResult);
|
|
3590
3798
|
const duration = Date.now() - start;
|
|
@@ -3670,6 +3878,90 @@ async function executeProbeStep(step, phaseId, context, allSteps, options, runSt
|
|
|
3670
3878
|
extraction,
|
|
3671
3879
|
};
|
|
3672
3880
|
}
|
|
3881
|
+
async function probeBrandJwks(rawCapabilities, options) {
|
|
3882
|
+
const brandJsonUrl = (0, capabilities_types_1.readBrandJsonUrl)(rawCapabilities);
|
|
3883
|
+
if (!brandJsonUrl) {
|
|
3884
|
+
return {
|
|
3885
|
+
url: '',
|
|
3886
|
+
status: 0,
|
|
3887
|
+
headers: {},
|
|
3888
|
+
body: null,
|
|
3889
|
+
error: 'identity.brand_json_url missing from get_adcp_capabilities; cannot fetch brand JWKS',
|
|
3890
|
+
};
|
|
3891
|
+
}
|
|
3892
|
+
const brand = await (0, probes_1.fetchProbe)(brandJsonUrl, options);
|
|
3893
|
+
if (brand.error || brand.status < 200 || brand.status >= 300) {
|
|
3894
|
+
return {
|
|
3895
|
+
...brand,
|
|
3896
|
+
error: brand.error ?? `brand.json fetch returned HTTP ${brand.status}`,
|
|
3897
|
+
};
|
|
3898
|
+
}
|
|
3899
|
+
const agents = brand.body && typeof brand.body === 'object' ? brand.body.agents : undefined;
|
|
3900
|
+
const jwksUri = Array.isArray(agents)
|
|
3901
|
+
? agents
|
|
3902
|
+
.map(agent => (agent && typeof agent === 'object' ? agent.jwks_uri : undefined))
|
|
3903
|
+
.find((uri) => typeof uri === 'string' && uri.length > 0)
|
|
3904
|
+
: undefined;
|
|
3905
|
+
if (!jwksUri) {
|
|
3906
|
+
return {
|
|
3907
|
+
url: brandJsonUrl,
|
|
3908
|
+
status: 0,
|
|
3909
|
+
headers: {},
|
|
3910
|
+
body: brand.body,
|
|
3911
|
+
error: 'brand.json agents[] did not contain a jwks_uri',
|
|
3912
|
+
};
|
|
3913
|
+
}
|
|
3914
|
+
const jwks = await (0, probes_1.fetchProbe)(jwksUri, options);
|
|
3915
|
+
if (jwks.error || jwks.status < 200 || jwks.status >= 300) {
|
|
3916
|
+
return {
|
|
3917
|
+
...jwks,
|
|
3918
|
+
error: jwks.error ?? `JWKS fetch returned HTTP ${jwks.status}`,
|
|
3919
|
+
};
|
|
3920
|
+
}
|
|
3921
|
+
return jwks;
|
|
3922
|
+
}
|
|
3923
|
+
function assertJwksPurpose(prior, purpose) {
|
|
3924
|
+
if (!prior || prior.error) {
|
|
3925
|
+
return {
|
|
3926
|
+
url: prior?.url ?? '',
|
|
3927
|
+
status: prior?.status ?? 0,
|
|
3928
|
+
headers: prior?.headers ?? {},
|
|
3929
|
+
body: prior?.body ?? null,
|
|
3930
|
+
error: prior?.error ?? 'fetch_brand_jwks step missing; cannot assert JWKS purpose',
|
|
3931
|
+
};
|
|
3932
|
+
}
|
|
3933
|
+
const keys = prior.body && typeof prior.body === 'object' ? prior.body.keys : undefined;
|
|
3934
|
+
if (!Array.isArray(keys)) {
|
|
3935
|
+
return {
|
|
3936
|
+
url: prior.url,
|
|
3937
|
+
status: 0,
|
|
3938
|
+
headers: {},
|
|
3939
|
+
body: prior.body,
|
|
3940
|
+
error: 'JWKS body does not contain keys[]',
|
|
3941
|
+
};
|
|
3942
|
+
}
|
|
3943
|
+
const matching = keys.filter(key => {
|
|
3944
|
+
if (!key || typeof key !== 'object')
|
|
3945
|
+
return false;
|
|
3946
|
+
const rec = key;
|
|
3947
|
+
return rec.adcp_use === purpose && rec.status !== 'revoked' && rec.revoked !== true;
|
|
3948
|
+
});
|
|
3949
|
+
if (matching.length === 0) {
|
|
3950
|
+
return {
|
|
3951
|
+
url: prior.url,
|
|
3952
|
+
status: 0,
|
|
3953
|
+
headers: {},
|
|
3954
|
+
body: prior.body,
|
|
3955
|
+
error: `JWKS contains no active key with adcp_use="${purpose}"`,
|
|
3956
|
+
};
|
|
3957
|
+
}
|
|
3958
|
+
return {
|
|
3959
|
+
url: prior.url,
|
|
3960
|
+
status: 200,
|
|
3961
|
+
headers: prior.headers,
|
|
3962
|
+
body: { purpose, matching_key_count: matching.length },
|
|
3963
|
+
};
|
|
3964
|
+
}
|
|
3673
3965
|
function findPriorProbe(priorStepResults) {
|
|
3674
3966
|
// Fallback for runStoryboardStep where priorProbes isn't populated — reach
|
|
3675
3967
|
// into the step result's response, which we set to the HttpProbeResult above.
|