@adcp/sdk 14.1.0 → 14.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/core/SingleAgentClient.d.mts +4 -1
- package/dist/lib/core/SingleAgentClient.d.ts +4 -1
- package/dist/lib/core/SingleAgentClient.js +25 -4
- package/dist/lib/core/SingleAgentClient.mjs +25 -4
- package/dist/lib/core/TaskExecutor.d.mts +3 -1
- package/dist/lib/core/TaskExecutor.d.ts +3 -1
- package/dist/lib/core/TaskExecutor.js +15 -11
- package/dist/lib/core/TaskExecutor.mjs +15 -11
- package/dist/lib/core/buyer-account-registry.d.mts +30 -2
- package/dist/lib/core/buyer-account-registry.d.ts +30 -2
- package/dist/lib/core/buyer-account-registry.js +126 -42
- package/dist/lib/core/buyer-account-registry.mjs +127 -43
- package/dist/lib/index.d.mts +3 -2
- package/dist/lib/index.d.ts +3 -2
- package/dist/lib/index.js +9 -0
- package/dist/lib/index.mjs +12 -1
- package/dist/lib/net/agent-transport-fetch.d.mts +4 -0
- package/dist/lib/net/agent-transport-fetch.d.ts +4 -0
- package/dist/lib/net/agent-transport-fetch.js +18 -5
- package/dist/lib/net/agent-transport-fetch.mjs +17 -5
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/signing/agent-resolver/errors.d.mts +2 -0
- package/dist/lib/signing/agent-resolver/errors.d.ts +2 -0
- package/dist/lib/signing/agent-resolver/errors.js +6 -0
- package/dist/lib/signing/agent-resolver/errors.mjs +6 -0
- package/dist/lib/signing/agent-resolver/fetch-helpers.d.mts +9 -0
- package/dist/lib/signing/agent-resolver/fetch-helpers.d.ts +9 -0
- package/dist/lib/signing/agent-resolver/fetch-helpers.js +35 -1
- package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +38 -2
- package/dist/lib/signing/agent-resolver/legacy-brand.js +1 -1
- package/dist/lib/signing/agent-resolver/legacy-brand.mjs +2 -2
- package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +1 -1
- package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +1 -1
- package/dist/lib/signing/agent-resolver/resolve-agent.js +47 -5
- package/dist/lib/signing/agent-resolver/resolve-agent.mjs +55 -6
- package/dist/lib/signing/brand-jwks.d.mts +2 -0
- package/dist/lib/signing/brand-jwks.d.ts +2 -0
- package/dist/lib/signing/brand-jwks.js +10 -2
- package/dist/lib/signing/brand-jwks.mjs +10 -2
- package/dist/lib/signing/errors.d.mts +4 -2
- package/dist/lib/signing/errors.d.ts +4 -2
- package/dist/lib/signing/errors.js +3 -1
- package/dist/lib/signing/errors.mjs +3 -1
- package/dist/lib/signing/verifier.js +1 -1
- package/dist/lib/signing/verifier.mjs +1 -1
- package/dist/lib/signing/webhook-verifier.js +2 -2
- package/dist/lib/signing/webhook-verifier.mjs +3 -3
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/types/accept-proposal.d.ts +19 -1
- package/dist/lib/types/buy-products.d.ts +19 -1
- package/dist/lib/types/check-governance.d.ts +19 -1
- package/dist/lib/types/comply-test-controller.d.ts +19 -1
- package/dist/lib/types/control-media-buy.d.ts +19 -1
- package/dist/lib/types/core.generated.d.mts +14 -1
- package/dist/lib/types/core.generated.d.ts +14 -1
- package/dist/lib/types/create-media-buy.d.ts +14 -1
- package/dist/lib/types/get-media-buys.d.ts +14 -1
- package/dist/lib/types/get-products.d.ts +19 -1
- package/dist/lib/types/list-products.d.ts +19 -1
- package/dist/lib/types/refine-proposals.d.ts +19 -1
- package/dist/lib/types/request-proposals.d.ts +19 -1
- package/dist/lib/types/schemas.generated.d.ts +12 -3
- package/dist/lib/types/schemas.generated.js +4 -1
- package/dist/lib/types/schemas.generated.mjs +4 -1
- package/dist/lib/types/tools.generated.d.mts +14 -1
- package/dist/lib/types/tools.generated.d.ts +14 -1
- package/dist/lib/types/update-media-buy.d.ts +14 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/dist/lib/webhooks/index.d.mts +24 -0
- package/dist/lib/webhooks/index.d.ts +24 -0
- package/dist/lib/webhooks/index.js +50 -24
- package/dist/lib/webhooks/index.mjs +49 -24
- package/dist/lib/wholesale-feed-sync/index.d.mts +2 -0
- package/dist/lib/wholesale-feed-sync/index.d.ts +2 -0
- package/dist/lib/wholesale-feed-sync/index.js +7 -0
- package/dist/lib/wholesale-feed-sync/index.mjs +4 -0
- package/dist/lib/wholesale-feed-sync/mirror.d.mts +97 -0
- package/dist/lib/wholesale-feed-sync/mirror.d.ts +97 -0
- package/dist/lib/wholesale-feed-sync/mirror.js +350 -0
- package/dist/lib/wholesale-feed-sync/mirror.mjs +322 -0
- package/dist/lib/wholesale-feed-sync/sync.d.mts +12 -29
- package/dist/lib/wholesale-feed-sync/sync.d.ts +12 -29
- package/dist/lib/wholesale-feed-sync/sync.js +153 -297
- package/dist/lib/wholesale-feed-sync/sync.mjs +158 -297
- package/docs/README.md +6 -0
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/guides/BUYER-STORAGE.md +3 -0
- package/docs/guides/FIRST-CALL-TO-A-SELLER.md +3 -1
- package/docs/guides/account-resolution.md +46 -10
- package/docs/llms.txt +2 -2
- package/docs/migration-14.0-to-14.1.md +85 -0
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/docs/migration-agent-resolution-3.3.md +5 -3
- package/docs/recipes/verifying-inbound-webhooks.md +4 -0
- package/package.json +2 -1
- package/skills/adcp-brand.previous/SKILL.md +0 -200
- package/skills/adcp-creative.previous/SKILL.md +0 -305
- package/skills/adcp-governance.previous/SKILL.md +0 -566
- package/skills/adcp-measurement.previous/SKILL.md +0 -136
- package/skills/adcp-media-buy.previous/SKILL.md +0 -556
- package/skills/adcp-si.previous/SKILL.md +0 -206
- package/skills/adcp-signals.previous/SKILL.md +0 -204
|
@@ -22,9 +22,12 @@ __export(resolve_agent_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(resolve_agent_exports);
|
|
24
24
|
var import_tldts = require("tldts");
|
|
25
|
+
var import_node_net = require("node:net");
|
|
25
26
|
var import_protocols = require('../../protocols/index.js');
|
|
26
27
|
var import_domain = require('../../brand/domain.js');
|
|
27
28
|
var import_capabilities_types = require('./capabilities-types.js');
|
|
29
|
+
var import_net = require('../../net/index.js');
|
|
30
|
+
var import_agent_transport_fetch = require('../../net/agent-transport-fetch.js');
|
|
28
31
|
var import_operator_authorization = require('./operator-authorization.js');
|
|
29
32
|
var import_legacy_brand = require('./legacy-brand.js');
|
|
30
33
|
var import_consistency = require('./consistency.js');
|
|
@@ -42,7 +45,7 @@ async function resolveAgent(agentUrl, options = {}) {
|
|
|
42
45
|
const caps = options.bodyCaps ?? {};
|
|
43
46
|
const brandJsonCap = caps.brandJsonBytes ?? import_fetch_helpers.MAX_BRAND_JSON_BYTES;
|
|
44
47
|
const jwksCap = caps.jwksBytes ?? import_fetch_helpers.MAX_JWKS_BYTES;
|
|
45
|
-
const fetchCapabilities = options.fetchCapabilities ?? defaultFetchCapabilities(agentUrl, options.protocol ?? "mcp");
|
|
48
|
+
const fetchCapabilities = options.fetchCapabilities ?? defaultFetchCapabilities(agentUrl, options.protocol ?? "mcp", allowPrivateIp, timeoutMs);
|
|
46
49
|
let capabilitiesPayload;
|
|
47
50
|
let capabilitiesFetchedAt;
|
|
48
51
|
try {
|
|
@@ -59,7 +62,7 @@ async function resolveAgent(agentUrl, options = {}) {
|
|
|
59
62
|
} catch (err) {
|
|
60
63
|
const detail = {
|
|
61
64
|
agent_url: agentUrl,
|
|
62
|
-
|
|
65
|
+
...(0, import_fetch_helpers.classifyDiscoveryFailure)(err),
|
|
63
66
|
last_attempt_at: now()
|
|
64
67
|
};
|
|
65
68
|
pushTrace(trace, { step: 1, name: "fetch_capabilities", ok: false, url: agentUrl, detail });
|
|
@@ -368,10 +371,49 @@ function checkAllowPrivateIp(requested) {
|
|
|
368
371
|
"resolveAgent: allowPrivateIp=true refused outside {NODE_ENV=test, NODE_ENV=development}. Set NODE_ENV appropriately for dev/test, or set ADCP_RESOLVER_ALLOW_PRIVATE_IP=1 as an explicit ops acknowledgment if you genuinely need private-IP discovery in this process."
|
|
369
372
|
);
|
|
370
373
|
}
|
|
371
|
-
function defaultFetchCapabilities(agentUrl, protocol) {
|
|
374
|
+
function defaultFetchCapabilities(agentUrl, protocol, allowPrivateIp, timeoutMs) {
|
|
372
375
|
return async () => {
|
|
373
|
-
const
|
|
374
|
-
|
|
376
|
+
const url = new URL(agentUrl);
|
|
377
|
+
const hostname = url.hostname.replace(/^\[|\]$/g, "");
|
|
378
|
+
if (!allowPrivateIp && url.protocol !== "https:" || (0, import_node_net.isIP)(hostname) && ((0, import_net.isAlwaysBlocked)(hostname) || !allowPrivateIp && (0, import_net.isPrivateIp)(hostname)))
|
|
379
|
+
throw new import_agent_transport_fetch.AgentTransportPolicyError("Signing discovery URL refused by address or HTTPS policy.");
|
|
380
|
+
let failure;
|
|
381
|
+
const upstream = (0, import_agent_transport_fetch.createAgentTransportFetch)(agentUrl, {
|
|
382
|
+
allowPrivateIp,
|
|
383
|
+
allowPrivateInitialOrigin: false,
|
|
384
|
+
requireHttps: true
|
|
385
|
+
});
|
|
386
|
+
let postSucceeded = false;
|
|
387
|
+
const trustedFetchFn = async (input, init) => {
|
|
388
|
+
const method = init?.method ?? (input instanceof Request ? input.method : "GET");
|
|
389
|
+
try {
|
|
390
|
+
const response = await upstream(input, init);
|
|
391
|
+
const isGet = method.toUpperCase() === "GET";
|
|
392
|
+
const observed = response.status >= 400 ? { dns_error: "fetch_failed", http_status: response.status } : void 0;
|
|
393
|
+
if (protocol === "mcp" && method.toUpperCase() === "POST" && response.ok) postSucceeded = true;
|
|
394
|
+
if (!(protocol === "mcp" && isGet && (postSucceeded || response.status === 405)) && !(isGet && observed && failure && !(0, import_fetch_helpers.isPermanentDiscoveryFailure)(failure) && (0, import_fetch_helpers.isPermanentDiscoveryFailure)(observed)))
|
|
395
|
+
failure = observed;
|
|
396
|
+
return response;
|
|
397
|
+
} catch (error) {
|
|
398
|
+
if (!(protocol === "mcp" && method.toUpperCase() === "GET" && postSucceeded))
|
|
399
|
+
failure = (0, import_fetch_helpers.classifyDiscoveryFailure)(error);
|
|
400
|
+
throw error;
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
const transport = { trustedFetchFn, requestTimeoutMs: timeoutMs };
|
|
404
|
+
const client = protocol === "a2a" ? (0, import_protocols.createA2AClient)(agentUrl, void 0, void 0, void 0, void 0, transport) : (0, import_protocols.createMCPClient)(agentUrl, void 0, void 0, void 0, void 0, transport);
|
|
405
|
+
try {
|
|
406
|
+
return await client.callTool("get_adcp_capabilities", {});
|
|
407
|
+
} catch (error) {
|
|
408
|
+
if (failure)
|
|
409
|
+
throw new import_fetch_helpers.SafeFetchError(
|
|
410
|
+
"capabilities",
|
|
411
|
+
failure.dns_error,
|
|
412
|
+
"Capabilities fetch failed",
|
|
413
|
+
failure.http_status
|
|
414
|
+
);
|
|
415
|
+
throw error;
|
|
416
|
+
}
|
|
375
417
|
};
|
|
376
418
|
}
|
|
377
419
|
function findAuthorizedOperator(brandJson, agentEtld1, _now, _options) {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { parse as parseTld } from "tldts";
|
|
2
|
+
import { isIP } from "node:net";
|
|
2
3
|
import { createA2AClient, createMCPClient } from "../../protocols/index.mjs";
|
|
3
4
|
import { isDevelopmentBrandDomain } from "../../brand/domain.mjs";
|
|
4
5
|
import { readBrandJsonUrl, readIdentityPosture } from "./capabilities-types.mjs";
|
|
6
|
+
import { isAlwaysBlocked, isPrivateIp } from "../../net/index.mjs";
|
|
7
|
+
import { AgentTransportPolicyError, createAgentTransportFetch } from "../../net/agent-transport-fetch.mjs";
|
|
5
8
|
import { checkDelegatedOperatorAuthorization } from "./operator-authorization.mjs";
|
|
6
9
|
import { fetchLegacyBrandJson } from "./legacy-brand.mjs";
|
|
7
10
|
import {
|
|
@@ -11,7 +14,14 @@ import {
|
|
|
11
14
|
} from "./consistency.mjs";
|
|
12
15
|
import { AgentResolverError } from "./errors.mjs";
|
|
13
16
|
import { eTldPlusOne, sameEtldPlusOne } from "./etld.mjs";
|
|
14
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
MAX_BRAND_JSON_BYTES,
|
|
19
|
+
MAX_JWKS_BYTES,
|
|
20
|
+
safeFetchJson,
|
|
21
|
+
SafeFetchError,
|
|
22
|
+
classifyDiscoveryFailure,
|
|
23
|
+
isPermanentDiscoveryFailure
|
|
24
|
+
} from "./fetch-helpers.mjs";
|
|
15
25
|
import { unwrapProtocolResponse } from "../protocol-response.mjs";
|
|
16
26
|
import { selectAgentByUrl, agentJwksUri, AgentSelectorError, canonicalAgentUrl } from "./select-agent.mjs";
|
|
17
27
|
const DEFAULT_TIMEOUT_MS = 1e4;
|
|
@@ -23,7 +33,7 @@ async function resolveAgent(agentUrl, options = {}) {
|
|
|
23
33
|
const caps = options.bodyCaps ?? {};
|
|
24
34
|
const brandJsonCap = caps.brandJsonBytes ?? MAX_BRAND_JSON_BYTES;
|
|
25
35
|
const jwksCap = caps.jwksBytes ?? MAX_JWKS_BYTES;
|
|
26
|
-
const fetchCapabilities = options.fetchCapabilities ?? defaultFetchCapabilities(agentUrl, options.protocol ?? "mcp");
|
|
36
|
+
const fetchCapabilities = options.fetchCapabilities ?? defaultFetchCapabilities(agentUrl, options.protocol ?? "mcp", allowPrivateIp, timeoutMs);
|
|
27
37
|
let capabilitiesPayload;
|
|
28
38
|
let capabilitiesFetchedAt;
|
|
29
39
|
try {
|
|
@@ -40,7 +50,7 @@ async function resolveAgent(agentUrl, options = {}) {
|
|
|
40
50
|
} catch (err) {
|
|
41
51
|
const detail = {
|
|
42
52
|
agent_url: agentUrl,
|
|
43
|
-
|
|
53
|
+
...classifyDiscoveryFailure(err),
|
|
44
54
|
last_attempt_at: now()
|
|
45
55
|
};
|
|
46
56
|
pushTrace(trace, { step: 1, name: "fetch_capabilities", ok: false, url: agentUrl, detail });
|
|
@@ -349,10 +359,49 @@ function checkAllowPrivateIp(requested) {
|
|
|
349
359
|
"resolveAgent: allowPrivateIp=true refused outside {NODE_ENV=test, NODE_ENV=development}. Set NODE_ENV appropriately for dev/test, or set ADCP_RESOLVER_ALLOW_PRIVATE_IP=1 as an explicit ops acknowledgment if you genuinely need private-IP discovery in this process."
|
|
350
360
|
);
|
|
351
361
|
}
|
|
352
|
-
function defaultFetchCapabilities(agentUrl, protocol) {
|
|
362
|
+
function defaultFetchCapabilities(agentUrl, protocol, allowPrivateIp, timeoutMs) {
|
|
353
363
|
return async () => {
|
|
354
|
-
const
|
|
355
|
-
|
|
364
|
+
const url = new URL(agentUrl);
|
|
365
|
+
const hostname = url.hostname.replace(/^\[|\]$/g, "");
|
|
366
|
+
if (!allowPrivateIp && url.protocol !== "https:" || isIP(hostname) && (isAlwaysBlocked(hostname) || !allowPrivateIp && isPrivateIp(hostname)))
|
|
367
|
+
throw new AgentTransportPolicyError("Signing discovery URL refused by address or HTTPS policy.");
|
|
368
|
+
let failure;
|
|
369
|
+
const upstream = createAgentTransportFetch(agentUrl, {
|
|
370
|
+
allowPrivateIp,
|
|
371
|
+
allowPrivateInitialOrigin: false,
|
|
372
|
+
requireHttps: true
|
|
373
|
+
});
|
|
374
|
+
let postSucceeded = false;
|
|
375
|
+
const trustedFetchFn = async (input, init) => {
|
|
376
|
+
const method = init?.method ?? (input instanceof Request ? input.method : "GET");
|
|
377
|
+
try {
|
|
378
|
+
const response = await upstream(input, init);
|
|
379
|
+
const isGet = method.toUpperCase() === "GET";
|
|
380
|
+
const observed = response.status >= 400 ? { dns_error: "fetch_failed", http_status: response.status } : void 0;
|
|
381
|
+
if (protocol === "mcp" && method.toUpperCase() === "POST" && response.ok) postSucceeded = true;
|
|
382
|
+
if (!(protocol === "mcp" && isGet && (postSucceeded || response.status === 405)) && !(isGet && observed && failure && !isPermanentDiscoveryFailure(failure) && isPermanentDiscoveryFailure(observed)))
|
|
383
|
+
failure = observed;
|
|
384
|
+
return response;
|
|
385
|
+
} catch (error) {
|
|
386
|
+
if (!(protocol === "mcp" && method.toUpperCase() === "GET" && postSucceeded))
|
|
387
|
+
failure = classifyDiscoveryFailure(error);
|
|
388
|
+
throw error;
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
const transport = { trustedFetchFn, requestTimeoutMs: timeoutMs };
|
|
392
|
+
const client = protocol === "a2a" ? createA2AClient(agentUrl, void 0, void 0, void 0, void 0, transport) : createMCPClient(agentUrl, void 0, void 0, void 0, void 0, transport);
|
|
393
|
+
try {
|
|
394
|
+
return await client.callTool("get_adcp_capabilities", {});
|
|
395
|
+
} catch (error) {
|
|
396
|
+
if (failure)
|
|
397
|
+
throw new SafeFetchError(
|
|
398
|
+
"capabilities",
|
|
399
|
+
failure.dns_error,
|
|
400
|
+
"Capabilities fetch failed",
|
|
401
|
+
failure.http_status
|
|
402
|
+
);
|
|
403
|
+
throw error;
|
|
404
|
+
}
|
|
356
405
|
};
|
|
357
406
|
}
|
|
358
407
|
function findAuthorizedOperator(brandJson, agentEtld1, _now, _options) {
|
|
@@ -14,6 +14,7 @@ export type BrandJsonResolverErrorCode = 'invalid_url' | 'invalid_house' | 'redi
|
|
|
14
14
|
*/
|
|
15
15
|
export declare class BrandJsonResolverError extends Error {
|
|
16
16
|
readonly code: BrandJsonResolverErrorCode;
|
|
17
|
+
readonly recovery: 'terminal' | 'transient';
|
|
17
18
|
readonly cause?: unknown;
|
|
18
19
|
/** HTTP status for `fetch_failed` responses, when a response was received. */
|
|
19
20
|
readonly httpStatus?: number;
|
|
@@ -79,6 +80,7 @@ export declare class BrandJsonJwksResolver implements JwksResolver {
|
|
|
79
80
|
private resolver?;
|
|
80
81
|
private onboarding?;
|
|
81
82
|
private lastOnboardingAttempt;
|
|
83
|
+
private lastOnboardingError?;
|
|
82
84
|
constructor(brandJsonUrl: string, options: BrandJsonJwksResolverOptions);
|
|
83
85
|
private createResolver;
|
|
84
86
|
private confirmedResolver;
|
|
@@ -14,6 +14,7 @@ export type BrandJsonResolverErrorCode = 'invalid_url' | 'invalid_house' | 'redi
|
|
|
14
14
|
*/
|
|
15
15
|
export declare class BrandJsonResolverError extends Error {
|
|
16
16
|
readonly code: BrandJsonResolverErrorCode;
|
|
17
|
+
readonly recovery: 'terminal' | 'transient';
|
|
17
18
|
readonly cause?: unknown;
|
|
18
19
|
/** HTTP status for `fetch_failed` responses, when a response was received. */
|
|
19
20
|
readonly httpStatus?: number;
|
|
@@ -79,6 +80,7 @@ export declare class BrandJsonJwksResolver implements JwksResolver {
|
|
|
79
80
|
private resolver?;
|
|
80
81
|
private onboarding?;
|
|
81
82
|
private lastOnboardingAttempt;
|
|
83
|
+
private lastOnboardingError?;
|
|
82
84
|
constructor(brandJsonUrl: string, options: BrandJsonJwksResolverOptions);
|
|
83
85
|
private createResolver;
|
|
84
86
|
private confirmedResolver;
|
|
@@ -23,12 +23,14 @@ __export(brand_jwks_exports, {
|
|
|
23
23
|
fetchBrandJson: () => fetchBrandJson
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(brand_jwks_exports);
|
|
26
|
+
var import_fetch_helpers = require('./agent-resolver/fetch-helpers.js');
|
|
26
27
|
var import_net = require('../net/index.js');
|
|
27
28
|
var import_resolved_agent_jwks = require('./agent-resolver/resolved-agent-jwks.js');
|
|
28
29
|
var import_strict_json = require('./agent-resolver/strict-json.js');
|
|
29
30
|
var import_select_agent = require('./agent-resolver/select-agent.js');
|
|
30
31
|
class BrandJsonResolverError extends Error {
|
|
31
32
|
code;
|
|
33
|
+
recovery;
|
|
32
34
|
cause;
|
|
33
35
|
/** HTTP status for `fetch_failed` responses, when a response was received. */
|
|
34
36
|
httpStatus;
|
|
@@ -38,6 +40,7 @@ class BrandJsonResolverError extends Error {
|
|
|
38
40
|
this.code = code;
|
|
39
41
|
this.httpStatus = details.httpStatus;
|
|
40
42
|
this.cause = details.cause;
|
|
43
|
+
this.recovery = code !== "fetch_failed" || (0, import_fetch_helpers.isPermanentDiscoveryFailure)((0, import_fetch_helpers.classifyDiscoveryFailure)(this)) ? "terminal" : "transient";
|
|
41
44
|
}
|
|
42
45
|
}
|
|
43
46
|
const DEFAULT_MIN_COOLDOWN_SECONDS = 30;
|
|
@@ -63,6 +66,7 @@ class BrandJsonJwksResolver {
|
|
|
63
66
|
resolver;
|
|
64
67
|
onboarding;
|
|
65
68
|
lastOnboardingAttempt = Number.NEGATIVE_INFINITY;
|
|
69
|
+
lastOnboardingError;
|
|
66
70
|
createResolver(agentUrl, operatorUrl) {
|
|
67
71
|
const options = this.options;
|
|
68
72
|
return new import_resolved_agent_jwks.ResolvedAgentJwksResolver(agentUrl, options.protocol ?? "mcp", {
|
|
@@ -86,7 +90,7 @@ class BrandJsonJwksResolver {
|
|
|
86
90
|
if (this.onboarding) return this.onboarding;
|
|
87
91
|
const now = this.options.now?.() ?? Date.now() / 1e3;
|
|
88
92
|
if (now - this.lastOnboardingAttempt < 30)
|
|
89
|
-
throw new BrandJsonResolverError("fetch_failed", "Operator onboarding discovery is in its retry cooldown.");
|
|
93
|
+
throw this.lastOnboardingError ?? new BrandJsonResolverError("fetch_failed", "Operator onboarding discovery is in its retry cooldown.");
|
|
90
94
|
this.lastOnboardingAttempt = now;
|
|
91
95
|
this.onboarding = (async () => {
|
|
92
96
|
const record = await fetchBrandJson({
|
|
@@ -99,8 +103,12 @@ class BrandJsonJwksResolver {
|
|
|
99
103
|
const resolver = this.createResolver(agentUrl, record.finalUrl);
|
|
100
104
|
await resolver.forceRefresh();
|
|
101
105
|
this.resolver = resolver;
|
|
106
|
+
this.lastOnboardingError = void 0;
|
|
102
107
|
return resolver;
|
|
103
|
-
})().
|
|
108
|
+
})().catch((error) => {
|
|
109
|
+
this.lastOnboardingError = error;
|
|
110
|
+
throw error;
|
|
111
|
+
}).finally(() => {
|
|
104
112
|
this.onboarding = void 0;
|
|
105
113
|
});
|
|
106
114
|
return this.onboarding;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { classifyDiscoveryFailure, isPermanentDiscoveryFailure } from "./agent-resolver/fetch-helpers.mjs";
|
|
1
2
|
import { ssrfSafeFetch } from "../net/index.mjs";
|
|
2
3
|
import { ResolvedAgentJwksResolver } from "./agent-resolver/resolved-agent-jwks.mjs";
|
|
3
4
|
import { parseStrictJson } from "./agent-resolver/strict-json.mjs";
|
|
4
5
|
import { canonicalAgentUrl } from "./agent-resolver/select-agent.mjs";
|
|
5
6
|
class BrandJsonResolverError extends Error {
|
|
6
7
|
code;
|
|
8
|
+
recovery;
|
|
7
9
|
cause;
|
|
8
10
|
/** HTTP status for `fetch_failed` responses, when a response was received. */
|
|
9
11
|
httpStatus;
|
|
@@ -13,6 +15,7 @@ class BrandJsonResolverError extends Error {
|
|
|
13
15
|
this.code = code;
|
|
14
16
|
this.httpStatus = details.httpStatus;
|
|
15
17
|
this.cause = details.cause;
|
|
18
|
+
this.recovery = code !== "fetch_failed" || isPermanentDiscoveryFailure(classifyDiscoveryFailure(this)) ? "terminal" : "transient";
|
|
16
19
|
}
|
|
17
20
|
}
|
|
18
21
|
const DEFAULT_MIN_COOLDOWN_SECONDS = 30;
|
|
@@ -38,6 +41,7 @@ class BrandJsonJwksResolver {
|
|
|
38
41
|
resolver;
|
|
39
42
|
onboarding;
|
|
40
43
|
lastOnboardingAttempt = Number.NEGATIVE_INFINITY;
|
|
44
|
+
lastOnboardingError;
|
|
41
45
|
createResolver(agentUrl, operatorUrl) {
|
|
42
46
|
const options = this.options;
|
|
43
47
|
return new ResolvedAgentJwksResolver(agentUrl, options.protocol ?? "mcp", {
|
|
@@ -61,7 +65,7 @@ class BrandJsonJwksResolver {
|
|
|
61
65
|
if (this.onboarding) return this.onboarding;
|
|
62
66
|
const now = this.options.now?.() ?? Date.now() / 1e3;
|
|
63
67
|
if (now - this.lastOnboardingAttempt < 30)
|
|
64
|
-
throw new BrandJsonResolverError("fetch_failed", "Operator onboarding discovery is in its retry cooldown.");
|
|
68
|
+
throw this.lastOnboardingError ?? new BrandJsonResolverError("fetch_failed", "Operator onboarding discovery is in its retry cooldown.");
|
|
65
69
|
this.lastOnboardingAttempt = now;
|
|
66
70
|
this.onboarding = (async () => {
|
|
67
71
|
const record = await fetchBrandJson({
|
|
@@ -74,8 +78,12 @@ class BrandJsonJwksResolver {
|
|
|
74
78
|
const resolver = this.createResolver(agentUrl, record.finalUrl);
|
|
75
79
|
await resolver.forceRefresh();
|
|
76
80
|
this.resolver = resolver;
|
|
81
|
+
this.lastOnboardingError = void 0;
|
|
77
82
|
return resolver;
|
|
78
|
-
})().
|
|
83
|
+
})().catch((error) => {
|
|
84
|
+
this.lastOnboardingError = error;
|
|
85
|
+
throw error;
|
|
86
|
+
}).finally(() => {
|
|
79
87
|
this.onboarding = void 0;
|
|
80
88
|
});
|
|
81
89
|
return this.onboarding;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { ADCPError } from '../errors/index.mjs';
|
|
2
2
|
import type { RequestSigningErrorCode } from '../types/core.generated.mjs';
|
|
3
|
-
|
|
3
|
+
import { RequestSigningErrorCodeMetadata } from '../types/enums.generated.mjs';
|
|
4
|
+
export { RequestSigningErrorCodeMetadata };
|
|
4
5
|
/** Canonical rc.6 request-signing transport vocabulary. */
|
|
5
6
|
export type RequestSignatureErrorCode = RequestSigningErrorCode;
|
|
6
7
|
export declare class RequestSignatureError extends ADCPError {
|
|
7
8
|
readonly code: RequestSignatureErrorCode;
|
|
8
9
|
readonly failedStep: number;
|
|
9
|
-
|
|
10
|
+
readonly recovery: 'transient' | 'correctable' | 'terminal';
|
|
11
|
+
constructor(code: RequestSignatureErrorCode, failedStep: number, message: string, details?: unknown, recovery?: 'transient' | 'correctable' | 'terminal');
|
|
10
12
|
}
|
|
11
13
|
/**
|
|
12
14
|
* Error codes surfaced by the RFC 9421 webhook-signing verifier. Maps onto
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { ADCPError } from '../errors';
|
|
2
2
|
import type { RequestSigningErrorCode } from '../types/core.generated';
|
|
3
|
-
|
|
3
|
+
import { RequestSigningErrorCodeMetadata } from '../types/enums.generated';
|
|
4
|
+
export { RequestSigningErrorCodeMetadata };
|
|
4
5
|
/** Canonical rc.6 request-signing transport vocabulary. */
|
|
5
6
|
export type RequestSignatureErrorCode = RequestSigningErrorCode;
|
|
6
7
|
export declare class RequestSignatureError extends ADCPError {
|
|
7
8
|
readonly code: RequestSignatureErrorCode;
|
|
8
9
|
readonly failedStep: number;
|
|
9
|
-
|
|
10
|
+
readonly recovery: 'transient' | 'correctable' | 'terminal';
|
|
11
|
+
constructor(code: RequestSignatureErrorCode, failedStep: number, message: string, details?: unknown, recovery?: 'transient' | 'correctable' | 'terminal');
|
|
10
12
|
}
|
|
11
13
|
/**
|
|
12
14
|
* Error codes surfaced by the RFC 9421 webhook-signing verifier. Maps onto
|
|
@@ -30,10 +30,12 @@ var import_enums = require('../types/enums.generated.js');
|
|
|
30
30
|
class RequestSignatureError extends import_errors.ADCPError {
|
|
31
31
|
code;
|
|
32
32
|
failedStep;
|
|
33
|
-
|
|
33
|
+
recovery;
|
|
34
|
+
constructor(code, failedStep, message, details, recovery) {
|
|
34
35
|
super(message, details);
|
|
35
36
|
this.code = code;
|
|
36
37
|
this.failedStep = failedStep;
|
|
38
|
+
this.recovery = recovery ?? import_enums.RequestSigningErrorCodeMetadata[code].recovery;
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
class WebhookSignatureError extends import_errors.ADCPError {
|
|
@@ -3,10 +3,12 @@ import { RequestSigningErrorCodeMetadata } from "../types/enums.generated.mjs";
|
|
|
3
3
|
class RequestSignatureError extends ADCPError {
|
|
4
4
|
code;
|
|
5
5
|
failedStep;
|
|
6
|
-
|
|
6
|
+
recovery;
|
|
7
|
+
constructor(code, failedStep, message, details, recovery) {
|
|
7
8
|
super(message, details);
|
|
8
9
|
this.code = code;
|
|
9
10
|
this.failedStep = failedStep;
|
|
11
|
+
this.recovery = recovery ?? RequestSigningErrorCodeMetadata[code].recovery;
|
|
10
12
|
}
|
|
11
13
|
}
|
|
12
14
|
class WebhookSignatureError extends ADCPError {
|
|
@@ -141,7 +141,7 @@ async function verifyRequestSignature(request, options) {
|
|
|
141
141
|
}
|
|
142
142
|
} catch (err) {
|
|
143
143
|
if (err instanceof import_errors2.AgentResolverError) {
|
|
144
|
-
throw new import_errors.RequestSignatureError(err.code, 7, err.message, err.detail);
|
|
144
|
+
throw new import_errors.RequestSignatureError(err.code, 7, err.message, err.detail, err.recovery);
|
|
145
145
|
}
|
|
146
146
|
throw err;
|
|
147
147
|
}
|
|
@@ -134,7 +134,7 @@ async function verifyRequestSignature(request, options) {
|
|
|
134
134
|
}
|
|
135
135
|
} catch (err) {
|
|
136
136
|
if (err instanceof AgentResolverError) {
|
|
137
|
-
throw new RequestSignatureError(err.code, 7, err.message, err.detail);
|
|
137
|
+
throw new RequestSignatureError(err.code, 7, err.message, err.detail, err.recovery);
|
|
138
138
|
}
|
|
139
139
|
throw err;
|
|
140
140
|
}
|
|
@@ -37,8 +37,8 @@ var import_replay = require('./replay.js');
|
|
|
37
37
|
var import_revocation = require('./revocation.js');
|
|
38
38
|
var import_types = require('./types.js');
|
|
39
39
|
function keyDiscoveryRetryable(error) {
|
|
40
|
-
if (error instanceof import_errors2.AgentResolverError) return
|
|
41
|
-
if (error instanceof import_brand_jwks.BrandJsonResolverError) return error.
|
|
40
|
+
if (error instanceof import_errors2.AgentResolverError) return error.recovery === "transient";
|
|
41
|
+
if (error instanceof import_brand_jwks.BrandJsonResolverError) return error.recovery === "transient";
|
|
42
42
|
return true;
|
|
43
43
|
}
|
|
44
44
|
const WEBHOOK_SIGNING_TAG = "adcp/webhook-signing/v1";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { buildSignatureBase, canonicalTargetUri, getHeaderValue } from "./canonicalize.mjs";
|
|
2
2
|
import { contentDigestMatches } from "./content-digest.mjs";
|
|
3
|
-
import { RequestSignatureError, WebhookSignatureError
|
|
3
|
+
import { RequestSignatureError, WebhookSignatureError } from "./errors.mjs";
|
|
4
4
|
import { AgentResolverError } from "./agent-resolver/errors.mjs";
|
|
5
5
|
import { BrandJsonResolverError } from "./brand-jwks.mjs";
|
|
6
6
|
import { parseSignature, parseSignatureInput } from "./parser.mjs";
|
|
@@ -10,8 +10,8 @@ import { InMemoryReplayStore } from "./replay.mjs";
|
|
|
10
10
|
import { InMemoryRevocationStore } from "./revocation.mjs";
|
|
11
11
|
import { ALLOWED_ALGS, CLOCK_SKEW_TOLERANCE_SECONDS, MAX_SIGNATURE_WINDOW_SECONDS } from "./types.mjs";
|
|
12
12
|
function keyDiscoveryRetryable(error) {
|
|
13
|
-
if (error instanceof AgentResolverError) return
|
|
14
|
-
if (error instanceof BrandJsonResolverError) return error.
|
|
13
|
+
if (error instanceof AgentResolverError) return error.recovery === "transient";
|
|
14
|
+
if (error instanceof BrandJsonResolverError) return error.recovery === "transient";
|
|
15
15
|
return true;
|
|
16
16
|
}
|
|
17
17
|
const WEBHOOK_SIGNING_TAG = "adcp/webhook-signing/v1";
|
|
@@ -197,7 +197,7 @@ export interface UpstreamTrafficQueryResult {
|
|
|
197
197
|
*/
|
|
198
198
|
export declare function runValidations(validations: StoryboardValidation[], context: ValidationContext): ValidationResult[];
|
|
199
199
|
/** Capability semver emitted in run summaries and used for advisory expiry. */
|
|
200
|
-
export declare const RUNNER_CAPABILITY_VERSION = "14.
|
|
200
|
+
export declare const RUNNER_CAPABILITY_VERSION = "14.2.0";
|
|
201
201
|
/** True when a failed validation contributes to the owning step's grade. */
|
|
202
202
|
export declare function validationFailsStep(result: ValidationResult): boolean;
|
|
203
203
|
/**
|
|
@@ -197,7 +197,7 @@ export interface UpstreamTrafficQueryResult {
|
|
|
197
197
|
*/
|
|
198
198
|
export declare function runValidations(validations: StoryboardValidation[], context: ValidationContext): ValidationResult[];
|
|
199
199
|
/** Capability semver emitted in run summaries and used for advisory expiry. */
|
|
200
|
-
export declare const RUNNER_CAPABILITY_VERSION = "14.
|
|
200
|
+
export declare const RUNNER_CAPABILITY_VERSION = "14.2.0";
|
|
201
201
|
/** True when a failed validation contributes to the owning step's grade. */
|
|
202
202
|
export declare function validationFailsStep(result: ValidationResult): boolean;
|
|
203
203
|
/**
|
|
@@ -145,6 +145,11 @@ export type ActivationKey = {
|
|
|
145
145
|
*/
|
|
146
146
|
export type AdvertiserIndustry = 'automotive' | 'automotive.electric_vehicles' | 'automotive.parts_accessories' | 'automotive.luxury' | 'beauty_cosmetics' | 'beauty_cosmetics.skincare' | 'beauty_cosmetics.fragrance' | 'beauty_cosmetics.haircare' | 'cannabis' | 'cpg' | 'cpg.personal_care' | 'cpg.household' | 'dating' | 'education' | 'education.higher_education' | 'education.online_learning' | 'education.k12' | 'energy_utilities' | 'energy_utilities.renewable' | 'fashion_apparel' | 'fashion_apparel.luxury' | 'fashion_apparel.sportswear' | 'finance' | 'finance.banking' | 'finance.insurance' | 'finance.investment' | 'finance.cryptocurrency' | 'food_beverage' | 'food_beverage.alcohol' | 'food_beverage.restaurants' | 'food_beverage.packaged_goods' | 'gambling_betting' | 'gambling_betting.sports_betting' | 'gambling_betting.casino' | 'gaming' | 'gaming.mobile' | 'gaming.console_pc' | 'gaming.esports' | 'government_nonprofit' | 'government_nonprofit.political' | 'government_nonprofit.charity' | 'healthcare' | 'healthcare.pharmaceutical' | 'healthcare.medical_devices' | 'healthcare.wellness' | 'home_garden' | 'home_garden.furniture' | 'home_garden.home_improvement' | 'media_entertainment' | 'media_entertainment.podcasts' | 'media_entertainment.music' | 'media_entertainment.film_tv' | 'media_entertainment.publishing' | 'media_entertainment.live_events' | 'pets' | 'professional_services' | 'professional_services.legal' | 'professional_services.consulting' | 'real_estate' | 'real_estate.residential' | 'real_estate.commercial' | 'recruitment_hr' | 'retail' | 'retail.ecommerce' | 'retail.department_stores' | 'sports_fitness' | 'sports_fitness.equipment' | 'sports_fitness.teams_leagues' | 'technology' | 'technology.software' | 'technology.hardware' | 'technology.ai_ml' | 'telecom' | 'telecom.mobile_carriers' | 'telecom.internet_providers' | 'transportation_logistics' | 'travel_hospitality' | 'travel_hospitality.airlines' | 'travel_hospitality.hotels' | 'travel_hospitality.cruise' | 'travel_hospitality.tourism';
|
|
147
147
|
|
|
148
|
+
/**
|
|
149
|
+
* User-level bases that may resolve age for demographic targeting execution. Population estimates are deliberately absent because aggregate composition cannot establish an individual impression's age.
|
|
150
|
+
*/
|
|
151
|
+
export type AgeDeterminationBasis = 'verified' | 'declared' | 'inferred';
|
|
152
|
+
|
|
148
153
|
/**
|
|
149
154
|
* Methods for verifying user age for compliance. Does not include 'inferred' as it is not accepted for regulatory compliance.
|
|
150
155
|
*/
|
|
@@ -1083,7 +1088,20 @@ export type DemographicSystem = 'nielsen' | 'nielsen_audio' | 'barb' | 'agf' | '
|
|
|
1083
1088
|
* Canonical demographic audience targeting intent with optional constraints on how age may be determined. This is distinct from age_restriction: demographics selects an audience, while age_restriction expresses a legal eligibility or verification floor. Fresh create/update targeting MUST compile exactly or be rejected. During get_products, a seller may offer a different configured predicate only through sparse targeting_resolution modifications on a distinguishable product_id; selecting that product accepts the alternative. Sellers never silently broaden, narrow, default, drop, or substitute the basis.
|
|
1084
1089
|
*/
|
|
1085
1090
|
export interface DemographicTargetingIntent {
|
|
1086
|
-
age: DemographicAgeRange & {
|
|
1091
|
+
age: DemographicAgeRange & {
|
|
1092
|
+
/**
|
|
1093
|
+
* User-level age determination bases the buyer permits for this predicate. Sellers MUST use only these bases or reject the request; they MUST NOT silently fall back to another basis. When omitted, the buyer imposes no determination-basis constraint beyond product capability and any age_restriction. population_estimate is structurally unavailable because aggregate evidence cannot identify an eligible user.
|
|
1094
|
+
*
|
|
1095
|
+
* @minItems 1
|
|
1096
|
+
*/
|
|
1097
|
+
accepted_bases?: [AgeDeterminationBasis, ...AgeDeterminationBasis[]];
|
|
1098
|
+
/**
|
|
1099
|
+
* Verification methods acceptable when verified is an accepted basis. This field narrows verified demographic execution; age_restriction.accepted_methods independently governs legal eligibility and the two sets are intersected when both are present.
|
|
1100
|
+
*
|
|
1101
|
+
* @minItems 1
|
|
1102
|
+
*/
|
|
1103
|
+
accepted_verification_methods?: [AgeVerificationMethod, ...AgeVerificationMethod[]];
|
|
1104
|
+
};
|
|
1087
1105
|
}
|
|
1088
1106
|
|
|
1089
1107
|
/**
|
|
@@ -145,6 +145,11 @@ export type ActivationKey = {
|
|
|
145
145
|
*/
|
|
146
146
|
export type AdvertiserIndustry = 'automotive' | 'automotive.electric_vehicles' | 'automotive.parts_accessories' | 'automotive.luxury' | 'beauty_cosmetics' | 'beauty_cosmetics.skincare' | 'beauty_cosmetics.fragrance' | 'beauty_cosmetics.haircare' | 'cannabis' | 'cpg' | 'cpg.personal_care' | 'cpg.household' | 'dating' | 'education' | 'education.higher_education' | 'education.online_learning' | 'education.k12' | 'energy_utilities' | 'energy_utilities.renewable' | 'fashion_apparel' | 'fashion_apparel.luxury' | 'fashion_apparel.sportswear' | 'finance' | 'finance.banking' | 'finance.insurance' | 'finance.investment' | 'finance.cryptocurrency' | 'food_beverage' | 'food_beverage.alcohol' | 'food_beverage.restaurants' | 'food_beverage.packaged_goods' | 'gambling_betting' | 'gambling_betting.sports_betting' | 'gambling_betting.casino' | 'gaming' | 'gaming.mobile' | 'gaming.console_pc' | 'gaming.esports' | 'government_nonprofit' | 'government_nonprofit.political' | 'government_nonprofit.charity' | 'healthcare' | 'healthcare.pharmaceutical' | 'healthcare.medical_devices' | 'healthcare.wellness' | 'home_garden' | 'home_garden.furniture' | 'home_garden.home_improvement' | 'media_entertainment' | 'media_entertainment.podcasts' | 'media_entertainment.music' | 'media_entertainment.film_tv' | 'media_entertainment.publishing' | 'media_entertainment.live_events' | 'pets' | 'professional_services' | 'professional_services.legal' | 'professional_services.consulting' | 'real_estate' | 'real_estate.residential' | 'real_estate.commercial' | 'recruitment_hr' | 'retail' | 'retail.ecommerce' | 'retail.department_stores' | 'sports_fitness' | 'sports_fitness.equipment' | 'sports_fitness.teams_leagues' | 'technology' | 'technology.software' | 'technology.hardware' | 'technology.ai_ml' | 'telecom' | 'telecom.mobile_carriers' | 'telecom.internet_providers' | 'transportation_logistics' | 'travel_hospitality' | 'travel_hospitality.airlines' | 'travel_hospitality.hotels' | 'travel_hospitality.cruise' | 'travel_hospitality.tourism';
|
|
147
147
|
|
|
148
|
+
/**
|
|
149
|
+
* User-level bases that may resolve age for demographic targeting execution. Population estimates are deliberately absent because aggregate composition cannot establish an individual impression's age.
|
|
150
|
+
*/
|
|
151
|
+
export type AgeDeterminationBasis = 'verified' | 'declared' | 'inferred';
|
|
152
|
+
|
|
148
153
|
/**
|
|
149
154
|
* Age restriction for compliance. Use for legal requirements (alcohol, gambling), not audience targeting.
|
|
150
155
|
*/
|
|
@@ -1152,7 +1157,20 @@ export type DemographicSystem = 'nielsen' | 'nielsen_audio' | 'barb' | 'agf' | '
|
|
|
1152
1157
|
* Canonical demographic audience targeting intent with optional constraints on how age may be determined. This is distinct from age_restriction: demographics selects an audience, while age_restriction expresses a legal eligibility or verification floor. Fresh create/update targeting MUST compile exactly or be rejected. During get_products, a seller may offer a different configured predicate only through sparse targeting_resolution modifications on a distinguishable product_id; selecting that product accepts the alternative. Sellers never silently broaden, narrow, default, drop, or substitute the basis.
|
|
1153
1158
|
*/
|
|
1154
1159
|
export interface DemographicTargetingIntent {
|
|
1155
|
-
age: DemographicAgeRange & {
|
|
1160
|
+
age: DemographicAgeRange & {
|
|
1161
|
+
/**
|
|
1162
|
+
* User-level age determination bases the buyer permits for this predicate. Sellers MUST use only these bases or reject the request; they MUST NOT silently fall back to another basis. When omitted, the buyer imposes no determination-basis constraint beyond product capability and any age_restriction. population_estimate is structurally unavailable because aggregate evidence cannot identify an eligible user.
|
|
1163
|
+
*
|
|
1164
|
+
* @minItems 1
|
|
1165
|
+
*/
|
|
1166
|
+
accepted_bases?: [AgeDeterminationBasis, ...AgeDeterminationBasis[]];
|
|
1167
|
+
/**
|
|
1168
|
+
* Verification methods acceptable when verified is an accepted basis. This field narrows verified demographic execution; age_restriction.accepted_methods independently governs legal eligibility and the two sets are intersected when both are present.
|
|
1169
|
+
*
|
|
1170
|
+
* @minItems 1
|
|
1171
|
+
*/
|
|
1172
|
+
accepted_verification_methods?: [AgeVerificationMethod, ...AgeVerificationMethod[]];
|
|
1173
|
+
};
|
|
1156
1174
|
}
|
|
1157
1175
|
|
|
1158
1176
|
/**
|
|
@@ -445,6 +445,11 @@ export type ActivationKey = {
|
|
|
445
445
|
*/
|
|
446
446
|
export type AdvertiserIndustry = 'automotive' | 'automotive.electric_vehicles' | 'automotive.parts_accessories' | 'automotive.luxury' | 'beauty_cosmetics' | 'beauty_cosmetics.skincare' | 'beauty_cosmetics.fragrance' | 'beauty_cosmetics.haircare' | 'cannabis' | 'cpg' | 'cpg.personal_care' | 'cpg.household' | 'dating' | 'education' | 'education.higher_education' | 'education.online_learning' | 'education.k12' | 'energy_utilities' | 'energy_utilities.renewable' | 'fashion_apparel' | 'fashion_apparel.luxury' | 'fashion_apparel.sportswear' | 'finance' | 'finance.banking' | 'finance.insurance' | 'finance.investment' | 'finance.cryptocurrency' | 'food_beverage' | 'food_beverage.alcohol' | 'food_beverage.restaurants' | 'food_beverage.packaged_goods' | 'gambling_betting' | 'gambling_betting.sports_betting' | 'gambling_betting.casino' | 'gaming' | 'gaming.mobile' | 'gaming.console_pc' | 'gaming.esports' | 'government_nonprofit' | 'government_nonprofit.political' | 'government_nonprofit.charity' | 'healthcare' | 'healthcare.pharmaceutical' | 'healthcare.medical_devices' | 'healthcare.wellness' | 'home_garden' | 'home_garden.furniture' | 'home_garden.home_improvement' | 'media_entertainment' | 'media_entertainment.podcasts' | 'media_entertainment.music' | 'media_entertainment.film_tv' | 'media_entertainment.publishing' | 'media_entertainment.live_events' | 'pets' | 'professional_services' | 'professional_services.legal' | 'professional_services.consulting' | 'real_estate' | 'real_estate.residential' | 'real_estate.commercial' | 'recruitment_hr' | 'retail' | 'retail.ecommerce' | 'retail.department_stores' | 'sports_fitness' | 'sports_fitness.equipment' | 'sports_fitness.teams_leagues' | 'technology' | 'technology.software' | 'technology.hardware' | 'technology.ai_ml' | 'telecom' | 'telecom.mobile_carriers' | 'telecom.internet_providers' | 'transportation_logistics' | 'travel_hospitality' | 'travel_hospitality.airlines' | 'travel_hospitality.hotels' | 'travel_hospitality.cruise' | 'travel_hospitality.tourism';
|
|
447
447
|
|
|
448
|
+
/**
|
|
449
|
+
* User-level bases that may resolve age for demographic targeting execution. Population estimates are deliberately absent because aggregate composition cannot establish an individual impression's age.
|
|
450
|
+
*/
|
|
451
|
+
export type AgeDeterminationBasis = 'verified' | 'declared' | 'inferred';
|
|
452
|
+
|
|
448
453
|
/**
|
|
449
454
|
* Methods for verifying user age for compliance. Does not include 'inferred' as it is not accepted for regulatory compliance.
|
|
450
455
|
*/
|
|
@@ -1585,7 +1590,20 @@ export type DemographicSystem = 'nielsen' | 'nielsen_audio' | 'barb' | 'agf' | '
|
|
|
1585
1590
|
* Canonical demographic audience targeting intent with optional constraints on how age may be determined. This is distinct from age_restriction: demographics selects an audience, while age_restriction expresses a legal eligibility or verification floor. Fresh create/update targeting MUST compile exactly or be rejected. During get_products, a seller may offer a different configured predicate only through sparse targeting_resolution modifications on a distinguishable product_id; selecting that product accepts the alternative. Sellers never silently broaden, narrow, default, drop, or substitute the basis.
|
|
1586
1591
|
*/
|
|
1587
1592
|
export interface DemographicTargetingIntent {
|
|
1588
|
-
age: DemographicAgeRange & {
|
|
1593
|
+
age: DemographicAgeRange & {
|
|
1594
|
+
/**
|
|
1595
|
+
* User-level age determination bases the buyer permits for this predicate. Sellers MUST use only these bases or reject the request; they MUST NOT silently fall back to another basis. When omitted, the buyer imposes no determination-basis constraint beyond product capability and any age_restriction. population_estimate is structurally unavailable because aggregate evidence cannot identify an eligible user.
|
|
1596
|
+
*
|
|
1597
|
+
* @minItems 1
|
|
1598
|
+
*/
|
|
1599
|
+
accepted_bases?: [AgeDeterminationBasis, ...AgeDeterminationBasis[]];
|
|
1600
|
+
/**
|
|
1601
|
+
* Verification methods acceptable when verified is an accepted basis. This field narrows verified demographic execution; age_restriction.accepted_methods independently governs legal eligibility and the two sets are intersected when both are present.
|
|
1602
|
+
*
|
|
1603
|
+
* @minItems 1
|
|
1604
|
+
*/
|
|
1605
|
+
accepted_verification_methods?: [AgeVerificationMethod, ...AgeVerificationMethod[]];
|
|
1606
|
+
};
|
|
1589
1607
|
}
|
|
1590
1608
|
|
|
1591
1609
|
/**
|
|
@@ -486,6 +486,11 @@ export interface AdInventoryConfiguration {
|
|
|
486
486
|
supported_formats?: string[];
|
|
487
487
|
}
|
|
488
488
|
|
|
489
|
+
/**
|
|
490
|
+
* User-level bases that may resolve age for demographic targeting execution. Population estimates are deliberately absent because aggregate composition cannot establish an individual impression's age.
|
|
491
|
+
*/
|
|
492
|
+
export type AgeDeterminationBasis = 'verified' | 'declared' | 'inferred';
|
|
493
|
+
|
|
489
494
|
/**
|
|
490
495
|
* Methods for verifying user age for compliance. Does not include 'inferred' as it is not accepted for regulatory compliance.
|
|
491
496
|
*/
|
|
@@ -5526,7 +5531,20 @@ export interface DemographicTargetingCapability {
|
|
|
5526
5531
|
* Canonical demographic audience targeting intent with optional constraints on how age may be determined. This is distinct from age_restriction: demographics selects an audience, while age_restriction expresses a legal eligibility or verification floor. Fresh create/update targeting MUST compile exactly or be rejected. During get_products, a seller may offer a different configured predicate only through sparse targeting_resolution modifications on a distinguishable product_id; selecting that product accepts the alternative. Sellers never silently broaden, narrow, default, drop, or substitute the basis.
|
|
5527
5532
|
*/
|
|
5528
5533
|
export interface DemographicTargetingIntent {
|
|
5529
|
-
age: DemographicAgeRange & {
|
|
5534
|
+
age: DemographicAgeRange & {
|
|
5535
|
+
/**
|
|
5536
|
+
* User-level age determination bases the buyer permits for this predicate. Sellers MUST use only these bases or reject the request; they MUST NOT silently fall back to another basis. When omitted, the buyer imposes no determination-basis constraint beyond product capability and any age_restriction. population_estimate is structurally unavailable because aggregate evidence cannot identify an eligible user.
|
|
5537
|
+
*
|
|
5538
|
+
* @minItems 1
|
|
5539
|
+
*/
|
|
5540
|
+
accepted_bases?: [AgeDeterminationBasis, ...AgeDeterminationBasis[]];
|
|
5541
|
+
/**
|
|
5542
|
+
* Verification methods acceptable when verified is an accepted basis. This field narrows verified demographic execution; age_restriction.accepted_methods independently governs legal eligibility and the two sets are intersected when both are present.
|
|
5543
|
+
*
|
|
5544
|
+
* @minItems 1
|
|
5545
|
+
*/
|
|
5546
|
+
accepted_verification_methods?: [AgeVerificationMethod, ...AgeVerificationMethod[]];
|
|
5547
|
+
};
|
|
5530
5548
|
}
|
|
5531
5549
|
|
|
5532
5550
|
/**
|