@adcp/sdk 14.0.0 → 14.1.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/adapters/implicit-account-store.d.mts +12 -7
- package/dist/lib/adapters/implicit-account-store.d.ts +12 -7
- package/dist/lib/adapters/implicit-account-store.js +69 -15
- package/dist/lib/adapters/implicit-account-store.mjs +69 -15
- package/dist/lib/core/AgentClient.d.mts +1 -0
- package/dist/lib/core/AgentClient.d.ts +1 -0
- package/dist/lib/core/AgentClient.js +3 -0
- package/dist/lib/core/AgentClient.mjs +3 -0
- package/dist/lib/core/SingleAgentClient.d.mts +31 -1
- package/dist/lib/core/SingleAgentClient.d.ts +31 -1
- package/dist/lib/core/SingleAgentClient.js +355 -35
- package/dist/lib/core/SingleAgentClient.mjs +365 -37
- package/dist/lib/core/TaskExecutor.js +2 -1
- package/dist/lib/core/TaskExecutor.mjs +2 -1
- package/dist/lib/core/account-key.d.mts +3 -0
- package/dist/lib/core/account-key.d.ts +3 -0
- package/dist/lib/core/account-key.js +41 -0
- package/dist/lib/core/account-key.mjs +17 -0
- package/dist/lib/core/account-resolution.d.mts +2 -0
- package/dist/lib/core/account-resolution.d.ts +2 -0
- package/dist/lib/core/buyer-account-registry.d.mts +65 -0
- package/dist/lib/core/buyer-account-registry.d.ts +65 -0
- package/dist/lib/core/buyer-account-registry.js +518 -0
- package/dist/lib/core/buyer-account-registry.mjs +494 -0
- package/dist/lib/core/product-cache.d.mts +18 -0
- package/dist/lib/core/product-cache.d.ts +18 -0
- package/dist/lib/core/product-cache.js +137 -0
- package/dist/lib/core/product-cache.mjs +112 -0
- package/dist/lib/errors/index.d.mts +40 -1
- package/dist/lib/errors/index.d.ts +40 -1
- package/dist/lib/errors/index.js +69 -3
- package/dist/lib/errors/index.mjs +64 -3
- package/dist/lib/governance/authorization.d.mts +17 -1
- package/dist/lib/governance/authorization.d.ts +17 -1
- package/dist/lib/governance/authorization.js +55 -7
- package/dist/lib/governance/authorization.mjs +59 -7
- package/dist/lib/governance/index.d.mts +2 -2
- package/dist/lib/governance/index.d.ts +2 -2
- package/dist/lib/governance/index.js +2 -0
- package/dist/lib/governance/index.mjs +3 -1
- package/dist/lib/index.d.mts +5 -3
- package/dist/lib/index.d.ts +5 -3
- package/dist/lib/index.js +20 -0
- package/dist/lib/index.mjs +19 -0
- package/dist/lib/protocols/a2a.js +9 -1
- package/dist/lib/protocols/a2a.mjs +9 -1
- package/dist/lib/protocols/index.js +9 -2
- package/dist/lib/protocols/index.mjs +9 -2
- package/dist/lib/protocols/mcp-modern.js +2 -1
- package/dist/lib/protocols/mcp-modern.mjs +2 -1
- package/dist/lib/protocols/mcp.js +5 -2
- package/dist/lib/protocols/mcp.mjs +5 -2
- package/dist/lib/protocols/rawResponseCapture.d.mts +6 -0
- package/dist/lib/protocols/rawResponseCapture.d.ts +6 -0
- package/dist/lib/protocols/rawResponseCapture.js +41 -29
- package/dist/lib/protocols/rawResponseCapture.mjs +40 -29
- package/dist/lib/protocols/signedRequestRejection.d.mts +9 -0
- package/dist/lib/protocols/signedRequestRejection.d.ts +9 -0
- package/dist/lib/protocols/signedRequestRejection.js +209 -0
- package/dist/lib/protocols/signedRequestRejection.mjs +189 -0
- package/dist/lib/protocols/transportDiagnostics.d.mts +1 -0
- package/dist/lib/protocols/transportDiagnostics.d.ts +1 -0
- package/dist/lib/protocols/transportDiagnostics.js +2 -0
- package/dist/lib/protocols/transportDiagnostics.mjs +1 -0
- package/dist/lib/registry/types.generated.d.mts +112 -45
- package/dist/lib/registry/types.generated.d.ts +112 -45
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/account-provisioning.d.mts +2 -0
- package/dist/lib/server/account-provisioning.d.ts +2 -0
- package/dist/lib/server/account-provisioning.js +30 -0
- package/dist/lib/server/account-provisioning.mjs +6 -0
- package/dist/lib/server/account-reference-warnings.d.mts +12 -0
- package/dist/lib/server/account-reference-warnings.d.ts +12 -0
- package/dist/lib/server/account-reference-warnings.js +48 -0
- package/dist/lib/server/account-reference-warnings.mjs +23 -0
- package/dist/lib/server/auth-signature.js +1 -0
- package/dist/lib/server/auth-signature.mjs +1 -0
- package/dist/lib/server/create-adcp-server.d.mts +34 -0
- package/dist/lib/server/create-adcp-server.d.ts +34 -0
- package/dist/lib/server/create-adcp-server.js +225 -14
- package/dist/lib/server/create-adcp-server.mjs +225 -14
- package/dist/lib/server/decisioning/account.d.mts +2 -0
- package/dist/lib/server/decisioning/account.d.ts +2 -0
- package/dist/lib/server/decisioning/runtime/from-platform.js +49 -10
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +49 -10
- package/dist/lib/server/index.d.mts +2 -2
- package/dist/lib/server/index.d.ts +2 -2
- package/dist/lib/server/index.js +2 -0
- package/dist/lib/server/index.mjs +3 -1
- package/dist/lib/signing/agent-resolver/consistency.d.mts +6 -13
- package/dist/lib/signing/agent-resolver/consistency.d.ts +6 -13
- package/dist/lib/signing/agent-resolver/consistency.js +0 -1
- package/dist/lib/signing/agent-resolver/consistency.mjs +0 -1
- package/dist/lib/signing/agent-resolver/errors.d.mts +1 -1
- package/dist/lib/signing/agent-resolver/errors.d.ts +1 -1
- package/dist/lib/signing/agent-resolver/fetch-helpers.d.mts +2 -0
- package/dist/lib/signing/agent-resolver/fetch-helpers.d.ts +2 -0
- package/dist/lib/signing/agent-resolver/fetch-helpers.js +2 -1
- package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +2 -1
- package/dist/lib/signing/agent-resolver/jwks-set.js +24 -3
- package/dist/lib/signing/agent-resolver/jwks-set.mjs +24 -3
- package/dist/lib/signing/agent-resolver/legacy-brand.d.mts +15 -0
- package/dist/lib/signing/agent-resolver/legacy-brand.d.ts +15 -0
- package/dist/lib/signing/agent-resolver/legacy-brand.js +60 -0
- package/dist/lib/signing/agent-resolver/legacy-brand.mjs +36 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.d.mts +13 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.d.ts +13 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.js +108 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.mjs +84 -0
- package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +14 -4
- package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +14 -4
- package/dist/lib/signing/agent-resolver/resolve-agent.js +101 -132
- package/dist/lib/signing/agent-resolver/resolve-agent.mjs +102 -133
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +7 -1
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +7 -1
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +50 -17
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +50 -17
- package/dist/lib/signing/agent-resolver/select-agent.d.mts +14 -15
- package/dist/lib/signing/agent-resolver/select-agent.d.ts +14 -15
- package/dist/lib/signing/agent-resolver/select-agent.js +98 -13
- package/dist/lib/signing/agent-resolver/select-agent.mjs +95 -13
- package/dist/lib/signing/brand-jwks.d.mts +27 -75
- package/dist/lib/signing/brand-jwks.d.ts +27 -75
- package/dist/lib/signing/brand-jwks.js +112 -182
- package/dist/lib/signing/brand-jwks.mjs +112 -182
- package/dist/lib/signing/errors.d.mts +3 -1
- package/dist/lib/signing/errors.d.ts +3 -1
- package/dist/lib/signing/errors.js +4 -1
- package/dist/lib/signing/errors.mjs +4 -1
- package/dist/lib/signing/jwks-https.d.mts +7 -0
- package/dist/lib/signing/jwks-https.d.ts +7 -0
- package/dist/lib/signing/jwks-https.js +31 -8
- package/dist/lib/signing/jwks-https.mjs +31 -8
- package/dist/lib/signing/jwks.d.mts +8 -0
- package/dist/lib/signing/jwks.d.ts +8 -0
- package/dist/lib/signing/middleware.js +2 -1
- package/dist/lib/signing/middleware.mjs +2 -1
- package/dist/lib/signing/publisher-pins.d.mts +11 -0
- package/dist/lib/signing/publisher-pins.d.ts +11 -0
- package/dist/lib/signing/publisher-pins.js +125 -0
- package/dist/lib/signing/publisher-pins.mjs +101 -0
- package/dist/lib/signing/server.d.mts +1 -0
- package/dist/lib/signing/server.d.ts +1 -0
- package/dist/lib/signing/types.d.mts +5 -0
- package/dist/lib/signing/types.d.ts +5 -0
- package/dist/lib/signing/verifier.js +49 -4
- package/dist/lib/signing/verifier.mjs +49 -4
- package/dist/lib/signing/webhook-verifier.d.mts +7 -2
- package/dist/lib/signing/webhook-verifier.d.ts +7 -2
- package/dist/lib/signing/webhook-verifier.js +42 -2
- package/dist/lib/signing/webhook-verifier.mjs +43 -3
- package/dist/lib/testing/storyboard/account-policy.d.mts +2 -0
- package/dist/lib/testing/storyboard/account-policy.d.ts +2 -0
- package/dist/lib/testing/storyboard/account-policy.js +35 -0
- package/dist/lib/testing/storyboard/account-policy.mjs +11 -0
- package/dist/lib/testing/storyboard/context.js +6 -0
- package/dist/lib/testing/storyboard/context.mjs +6 -0
- package/dist/lib/testing/storyboard/request-builder.js +12 -2
- package/dist/lib/testing/storyboard/request-builder.mjs +12 -2
- package/dist/lib/testing/storyboard/runner.js +3 -2
- package/dist/lib/testing/storyboard/runner.mjs +3 -2
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -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/wholesale-feed-sync/sync.d.mts +1 -0
- package/dist/lib/wholesale-feed-sync/sync.d.ts +1 -0
- package/dist/lib/wholesale-feed-sync/sync.js +92 -21
- package/dist/lib/wholesale-feed-sync/sync.mjs +92 -21
- package/dist/lib/wholesale-feed-sync/types.d.mts +4 -3
- package/dist/lib/wholesale-feed-sync/types.d.ts +4 -3
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/guides/BUILD-AN-AGENT.md +2 -2
- package/docs/guides/BUYER-QUICKSTART-3.2.md +2 -0
- package/docs/guides/FIRST-CALL-TO-A-SELLER.md +104 -0
- package/docs/guides/SIGNING-GUIDE.md +16 -7
- package/docs/guides/account-resolution.md +86 -0
- package/docs/llms.txt +3 -2
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/docs/migration-4.x-to-5.x.md +1 -0
- package/docs/migration-agent-resolution-3.3.md +123 -0
- package/docs/recipes/verifying-inbound-webhooks.md +56 -15
- package/package.json +2 -2
|
@@ -1,16 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
* `agents[]` entry whose `url` byte-equals the agent URL the verifier is
|
|
4
|
-
* resolving. The byte-equal rule is deliberate (security.mdx §"Discovering
|
|
5
|
-
* an agent's signing keys via `brand_json_url`" step 5) — no canonicalization,
|
|
6
|
-
* because the most common operator misconfiguration is a trailing-slash or
|
|
7
|
-
* scheme mismatch (`https://x.com/mcp` vs `https://x.com/mcp/`), and silently
|
|
8
|
-
* canonicalizing past that hides the bug. The match must be exact.
|
|
9
|
-
*
|
|
10
|
-
* Both flat (`agents[]` at top level) and house-portfolio
|
|
11
|
-
* (`house.agents[]` + `brands[].agents[]`) brand.json shapes are supported,
|
|
12
|
-
* because either may carry the entry that matches the agent URL.
|
|
13
|
-
*/
|
|
1
|
+
/** Shared identifier rules, including query escapes (unlike the signing profile's raw query). */
|
|
2
|
+
export declare function canonicalAgentUrl(raw: string): string;
|
|
14
3
|
export type AgentSelectorErrorCode = 'agent_not_in_brand_json' | 'brand_json_ambiguous';
|
|
15
4
|
export interface AgentEntry {
|
|
16
5
|
url: string;
|
|
@@ -18,6 +7,12 @@ export interface AgentEntry {
|
|
|
18
7
|
/** Other fields preserved verbatim — the resolver passes the entry to the caller. */
|
|
19
8
|
[key: string]: unknown;
|
|
20
9
|
}
|
|
10
|
+
export interface AgentSelector {
|
|
11
|
+
agentType?: string;
|
|
12
|
+
agentId?: string;
|
|
13
|
+
}
|
|
14
|
+
/** The key source always belongs to the matched entry, never to the artifact. */
|
|
15
|
+
export declare function agentJwksUri(entry: AgentEntry): string;
|
|
21
16
|
export declare class AgentSelectorError extends Error {
|
|
22
17
|
readonly code: AgentSelectorErrorCode;
|
|
23
18
|
readonly detail: {
|
|
@@ -40,7 +35,9 @@ export declare class AgentSelectorError extends Error {
|
|
|
40
35
|
*/
|
|
41
36
|
export declare function collectAgentEntries(brandJson: unknown): AgentEntry[];
|
|
42
37
|
/**
|
|
43
|
-
* Find the unique
|
|
38
|
+
* Find the unique canonical match. Portfolio repetitions across collections
|
|
39
|
+
* count once when their type and canonical JWKS source agree. Duplicates
|
|
40
|
+
* within one collection remain ambiguous. Type/id only narrow URL matches.
|
|
44
41
|
*
|
|
45
42
|
* - Returns the matched entry on a unique hit.
|
|
46
43
|
* - Throws `AgentSelectorError('agent_not_in_brand_json')` on zero matches.
|
|
@@ -50,4 +47,6 @@ export declare function collectAgentEntries(brandJson: unknown): AgentEntry[];
|
|
|
50
47
|
* fields. `matched_entries` reflects counterparty-controlled state and is
|
|
51
48
|
* marked attacker-influenceable in the resolver's error mapping.
|
|
52
49
|
*/
|
|
53
|
-
export declare function selectAgentByUrl(brandJson: unknown, agentUrl: string): AgentEntry;
|
|
50
|
+
export declare function selectAgentByUrl(brandJson: unknown, agentUrl: string, selector?: AgentSelector): AgentEntry;
|
|
51
|
+
/** Select a relying party's collection without incorporating sibling brands. */
|
|
52
|
+
export declare function relyingPartyAgentRecord(brandJson: unknown, brandDomain: string): unknown;
|
|
@@ -1,16 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
* `agents[]` entry whose `url` byte-equals the agent URL the verifier is
|
|
4
|
-
* resolving. The byte-equal rule is deliberate (security.mdx §"Discovering
|
|
5
|
-
* an agent's signing keys via `brand_json_url`" step 5) — no canonicalization,
|
|
6
|
-
* because the most common operator misconfiguration is a trailing-slash or
|
|
7
|
-
* scheme mismatch (`https://x.com/mcp` vs `https://x.com/mcp/`), and silently
|
|
8
|
-
* canonicalizing past that hides the bug. The match must be exact.
|
|
9
|
-
*
|
|
10
|
-
* Both flat (`agents[]` at top level) and house-portfolio
|
|
11
|
-
* (`house.agents[]` + `brands[].agents[]`) brand.json shapes are supported,
|
|
12
|
-
* because either may carry the entry that matches the agent URL.
|
|
13
|
-
*/
|
|
1
|
+
/** Shared identifier rules, including query escapes (unlike the signing profile's raw query). */
|
|
2
|
+
export declare function canonicalAgentUrl(raw: string): string;
|
|
14
3
|
export type AgentSelectorErrorCode = 'agent_not_in_brand_json' | 'brand_json_ambiguous';
|
|
15
4
|
export interface AgentEntry {
|
|
16
5
|
url: string;
|
|
@@ -18,6 +7,12 @@ export interface AgentEntry {
|
|
|
18
7
|
/** Other fields preserved verbatim — the resolver passes the entry to the caller. */
|
|
19
8
|
[key: string]: unknown;
|
|
20
9
|
}
|
|
10
|
+
export interface AgentSelector {
|
|
11
|
+
agentType?: string;
|
|
12
|
+
agentId?: string;
|
|
13
|
+
}
|
|
14
|
+
/** The key source always belongs to the matched entry, never to the artifact. */
|
|
15
|
+
export declare function agentJwksUri(entry: AgentEntry): string;
|
|
21
16
|
export declare class AgentSelectorError extends Error {
|
|
22
17
|
readonly code: AgentSelectorErrorCode;
|
|
23
18
|
readonly detail: {
|
|
@@ -40,7 +35,9 @@ export declare class AgentSelectorError extends Error {
|
|
|
40
35
|
*/
|
|
41
36
|
export declare function collectAgentEntries(brandJson: unknown): AgentEntry[];
|
|
42
37
|
/**
|
|
43
|
-
* Find the unique
|
|
38
|
+
* Find the unique canonical match. Portfolio repetitions across collections
|
|
39
|
+
* count once when their type and canonical JWKS source agree. Duplicates
|
|
40
|
+
* within one collection remain ambiguous. Type/id only narrow URL matches.
|
|
44
41
|
*
|
|
45
42
|
* - Returns the matched entry on a unique hit.
|
|
46
43
|
* - Throws `AgentSelectorError('agent_not_in_brand_json')` on zero matches.
|
|
@@ -50,4 +47,6 @@ export declare function collectAgentEntries(brandJson: unknown): AgentEntry[];
|
|
|
50
47
|
* fields. `matched_entries` reflects counterparty-controlled state and is
|
|
51
48
|
* marked attacker-influenceable in the resolver's error mapping.
|
|
52
49
|
*/
|
|
53
|
-
export declare function selectAgentByUrl(brandJson: unknown, agentUrl: string): AgentEntry;
|
|
50
|
+
export declare function selectAgentByUrl(brandJson: unknown, agentUrl: string, selector?: AgentSelector): AgentEntry;
|
|
51
|
+
/** Select a relying party's collection without incorporating sibling brands. */
|
|
52
|
+
export declare function relyingPartyAgentRecord(brandJson: unknown, brandDomain: string): unknown;
|
|
@@ -19,10 +19,34 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
var select_agent_exports = {};
|
|
20
20
|
__export(select_agent_exports, {
|
|
21
21
|
AgentSelectorError: () => AgentSelectorError,
|
|
22
|
+
agentJwksUri: () => agentJwksUri,
|
|
23
|
+
canonicalAgentUrl: () => canonicalAgentUrl,
|
|
22
24
|
collectAgentEntries: () => collectAgentEntries,
|
|
25
|
+
relyingPartyAgentRecord: () => relyingPartyAgentRecord,
|
|
23
26
|
selectAgentByUrl: () => selectAgentByUrl
|
|
24
27
|
});
|
|
25
28
|
module.exports = __toCommonJS(select_agent_exports);
|
|
29
|
+
var import_canonicalize = require('../canonicalize.js');
|
|
30
|
+
function canonicalAgentUrl(raw) {
|
|
31
|
+
const authority = /^[a-z][a-z0-9+.\-]*:\/\/([^/?#]*)/i.exec(raw)?.[1];
|
|
32
|
+
if (authority) (0, import_canonicalize.rejectNonAsciiHost)(`https://${authority.slice(authority.lastIndexOf("@") + 1)}`);
|
|
33
|
+
const canonical = (0, import_canonicalize.canonicalTargetUri)(raw, "3.2");
|
|
34
|
+
if (!/^https?:\/\//.test(canonical)) throw new TypeError("Agent identifier must be an HTTP(S) URL");
|
|
35
|
+
return canonical.replace(/%([0-9a-f]{2})/gi, (_escape, hex) => {
|
|
36
|
+
const character = String.fromCharCode(parseInt(hex, 16));
|
|
37
|
+
return /^[a-z0-9._~-]$/i.test(character) ? character : `%${hex.toUpperCase()}`;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function agentJwksUri(entry) {
|
|
41
|
+
if (entry.jwks_uri !== void 0) {
|
|
42
|
+
if (typeof entry.jwks_uri !== "string" || !entry.jwks_uri) throw new TypeError("Invalid jwks_uri");
|
|
43
|
+
const source = new URL(entry.jwks_uri);
|
|
44
|
+
if (source.username || source.password) throw new TypeError("JWKS URI must not contain userinfo");
|
|
45
|
+
canonicalAgentUrl(entry.jwks_uri);
|
|
46
|
+
return entry.jwks_uri;
|
|
47
|
+
}
|
|
48
|
+
return `${new URL(canonicalAgentUrl(entry.url)).origin}/.well-known/jwks.json`;
|
|
49
|
+
}
|
|
26
50
|
class AgentSelectorError extends Error {
|
|
27
51
|
code;
|
|
28
52
|
detail;
|
|
@@ -34,22 +58,31 @@ class AgentSelectorError extends Error {
|
|
|
34
58
|
}
|
|
35
59
|
}
|
|
36
60
|
function collectAgentEntries(brandJson) {
|
|
61
|
+
return agentCollections(brandJson).flat();
|
|
62
|
+
}
|
|
63
|
+
function agentCollections(brandJson) {
|
|
37
64
|
if (!brandJson || typeof brandJson !== "object") return [];
|
|
38
|
-
const
|
|
65
|
+
const collections = [];
|
|
39
66
|
const obj = brandJson;
|
|
40
|
-
|
|
67
|
+
const add = (value) => {
|
|
68
|
+
const entries = [];
|
|
69
|
+
pushAgentArray(value, entries);
|
|
70
|
+
collections.push(entries);
|
|
71
|
+
};
|
|
72
|
+
if (obj.agents !== void 0) {
|
|
73
|
+
add(obj.agents);
|
|
74
|
+
return collections;
|
|
75
|
+
}
|
|
41
76
|
const house = obj.house;
|
|
42
77
|
if (house && typeof house === "object") {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (brand && typeof brand === "object") {
|
|
48
|
-
pushAgentArray(brand.agents, entries);
|
|
78
|
+
add(house.agents);
|
|
79
|
+
if (Array.isArray(obj.brands)) {
|
|
80
|
+
for (const brand of obj.brands) {
|
|
81
|
+
if (brand && typeof brand === "object") add(brand.agents);
|
|
49
82
|
}
|
|
50
83
|
}
|
|
51
84
|
}
|
|
52
|
-
return
|
|
85
|
+
return collections;
|
|
53
86
|
}
|
|
54
87
|
function pushAgentArray(value, out) {
|
|
55
88
|
if (!Array.isArray(value)) return;
|
|
@@ -62,13 +95,37 @@ function pushAgentArray(value, out) {
|
|
|
62
95
|
}
|
|
63
96
|
}
|
|
64
97
|
}
|
|
65
|
-
function selectAgentByUrl(brandJson, agentUrl) {
|
|
66
|
-
const
|
|
67
|
-
const matches =
|
|
98
|
+
function selectAgentByUrl(brandJson, agentUrl, selector = {}) {
|
|
99
|
+
const canonicalUrl = canonicalAgentUrl(agentUrl);
|
|
100
|
+
const matches = [];
|
|
101
|
+
const seen = /* @__PURE__ */ new Set();
|
|
102
|
+
for (const collection of agentCollections(brandJson)) {
|
|
103
|
+
const local = collection.filter((e) => {
|
|
104
|
+
try {
|
|
105
|
+
return canonicalAgentUrl(e.url) === canonicalUrl && (selector.agentType === void 0 || e.type === selector.agentType) && (selector.agentId === void 0 || e.id === selector.agentId);
|
|
106
|
+
} catch {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
if (local.length > 1) {
|
|
111
|
+
throw new AgentSelectorError("brand_json_ambiguous", `Multiple brand.json agent entries match ${agentUrl}`, {
|
|
112
|
+
agent_url: agentUrl,
|
|
113
|
+
matched_count: local.length,
|
|
114
|
+
matched_entries: local
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
for (const entry of local) {
|
|
118
|
+
const source = JSON.stringify([entry.type, canonicalAgentUrl(agentJwksUri(entry))]);
|
|
119
|
+
if (!seen.has(source)) {
|
|
120
|
+
seen.add(source);
|
|
121
|
+
matches.push(entry);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
68
125
|
if (matches.length === 0) {
|
|
69
126
|
throw new AgentSelectorError(
|
|
70
127
|
"agent_not_in_brand_json",
|
|
71
|
-
`No brand.json agent entry has
|
|
128
|
+
`No brand.json agent entry has canonical URL ${canonicalUrl}`,
|
|
72
129
|
{ agent_url: agentUrl }
|
|
73
130
|
);
|
|
74
131
|
}
|
|
@@ -81,9 +138,37 @@ function selectAgentByUrl(brandJson, agentUrl) {
|
|
|
81
138
|
}
|
|
82
139
|
return matches[0];
|
|
83
140
|
}
|
|
141
|
+
function relyingPartyAgentRecord(brandJson, brandDomain) {
|
|
142
|
+
if (!brandJson || typeof brandJson !== "object") throw new TypeError("Invalid brand.json");
|
|
143
|
+
const obj = brandJson;
|
|
144
|
+
if (!obj.house || typeof obj.house !== "object") return obj;
|
|
145
|
+
const domainHost = (domain) => {
|
|
146
|
+
if (typeof domain !== "string" || !/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*\.?$/i.test(domain))
|
|
147
|
+
throw new TypeError("Invalid brand domain");
|
|
148
|
+
return new URL(canonicalAgentUrl(`https://${domain}`)).hostname;
|
|
149
|
+
};
|
|
150
|
+
const host = domainHost(brandDomain);
|
|
151
|
+
const house = obj.house;
|
|
152
|
+
if (house.domain !== void 0 && domainHost(house.domain) === host) {
|
|
153
|
+
return { agents: house.agents };
|
|
154
|
+
}
|
|
155
|
+
const brands = Array.isArray(obj.brands) ? obj.brands : [];
|
|
156
|
+
const matches = brands.filter((b) => {
|
|
157
|
+
try {
|
|
158
|
+
return b && typeof b === "object" && typeof b.url === "string" && new URL(canonicalAgentUrl(b.url)).hostname === host;
|
|
159
|
+
} catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
if (matches.length !== 1) throw new TypeError("Brand domain is absent or ambiguous in portfolio");
|
|
164
|
+
return { agents: matches[0].agents === void 0 ? house.agents : matches[0].agents };
|
|
165
|
+
}
|
|
84
166
|
// Annotate the CommonJS export names for ESM import in node:
|
|
85
167
|
0 && (module.exports = {
|
|
86
168
|
AgentSelectorError,
|
|
169
|
+
agentJwksUri,
|
|
170
|
+
canonicalAgentUrl,
|
|
87
171
|
collectAgentEntries,
|
|
172
|
+
relyingPartyAgentRecord,
|
|
88
173
|
selectAgentByUrl
|
|
89
174
|
});
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
import { canonicalTargetUri, rejectNonAsciiHost } from "../canonicalize.mjs";
|
|
2
|
+
function canonicalAgentUrl(raw) {
|
|
3
|
+
const authority = /^[a-z][a-z0-9+.\-]*:\/\/([^/?#]*)/i.exec(raw)?.[1];
|
|
4
|
+
if (authority) rejectNonAsciiHost(`https://${authority.slice(authority.lastIndexOf("@") + 1)}`);
|
|
5
|
+
const canonical = canonicalTargetUri(raw, "3.2");
|
|
6
|
+
if (!/^https?:\/\//.test(canonical)) throw new TypeError("Agent identifier must be an HTTP(S) URL");
|
|
7
|
+
return canonical.replace(/%([0-9a-f]{2})/gi, (_escape, hex) => {
|
|
8
|
+
const character = String.fromCharCode(parseInt(hex, 16));
|
|
9
|
+
return /^[a-z0-9._~-]$/i.test(character) ? character : `%${hex.toUpperCase()}`;
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function agentJwksUri(entry) {
|
|
13
|
+
if (entry.jwks_uri !== void 0) {
|
|
14
|
+
if (typeof entry.jwks_uri !== "string" || !entry.jwks_uri) throw new TypeError("Invalid jwks_uri");
|
|
15
|
+
const source = new URL(entry.jwks_uri);
|
|
16
|
+
if (source.username || source.password) throw new TypeError("JWKS URI must not contain userinfo");
|
|
17
|
+
canonicalAgentUrl(entry.jwks_uri);
|
|
18
|
+
return entry.jwks_uri;
|
|
19
|
+
}
|
|
20
|
+
return `${new URL(canonicalAgentUrl(entry.url)).origin}/.well-known/jwks.json`;
|
|
21
|
+
}
|
|
1
22
|
class AgentSelectorError extends Error {
|
|
2
23
|
code;
|
|
3
24
|
detail;
|
|
@@ -9,22 +30,31 @@ class AgentSelectorError extends Error {
|
|
|
9
30
|
}
|
|
10
31
|
}
|
|
11
32
|
function collectAgentEntries(brandJson) {
|
|
33
|
+
return agentCollections(brandJson).flat();
|
|
34
|
+
}
|
|
35
|
+
function agentCollections(brandJson) {
|
|
12
36
|
if (!brandJson || typeof brandJson !== "object") return [];
|
|
13
|
-
const
|
|
37
|
+
const collections = [];
|
|
14
38
|
const obj = brandJson;
|
|
15
|
-
|
|
39
|
+
const add = (value) => {
|
|
40
|
+
const entries = [];
|
|
41
|
+
pushAgentArray(value, entries);
|
|
42
|
+
collections.push(entries);
|
|
43
|
+
};
|
|
44
|
+
if (obj.agents !== void 0) {
|
|
45
|
+
add(obj.agents);
|
|
46
|
+
return collections;
|
|
47
|
+
}
|
|
16
48
|
const house = obj.house;
|
|
17
49
|
if (house && typeof house === "object") {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (brand && typeof brand === "object") {
|
|
23
|
-
pushAgentArray(brand.agents, entries);
|
|
50
|
+
add(house.agents);
|
|
51
|
+
if (Array.isArray(obj.brands)) {
|
|
52
|
+
for (const brand of obj.brands) {
|
|
53
|
+
if (brand && typeof brand === "object") add(brand.agents);
|
|
24
54
|
}
|
|
25
55
|
}
|
|
26
56
|
}
|
|
27
|
-
return
|
|
57
|
+
return collections;
|
|
28
58
|
}
|
|
29
59
|
function pushAgentArray(value, out) {
|
|
30
60
|
if (!Array.isArray(value)) return;
|
|
@@ -37,13 +67,37 @@ function pushAgentArray(value, out) {
|
|
|
37
67
|
}
|
|
38
68
|
}
|
|
39
69
|
}
|
|
40
|
-
function selectAgentByUrl(brandJson, agentUrl) {
|
|
41
|
-
const
|
|
42
|
-
const matches =
|
|
70
|
+
function selectAgentByUrl(brandJson, agentUrl, selector = {}) {
|
|
71
|
+
const canonicalUrl = canonicalAgentUrl(agentUrl);
|
|
72
|
+
const matches = [];
|
|
73
|
+
const seen = /* @__PURE__ */ new Set();
|
|
74
|
+
for (const collection of agentCollections(brandJson)) {
|
|
75
|
+
const local = collection.filter((e) => {
|
|
76
|
+
try {
|
|
77
|
+
return canonicalAgentUrl(e.url) === canonicalUrl && (selector.agentType === void 0 || e.type === selector.agentType) && (selector.agentId === void 0 || e.id === selector.agentId);
|
|
78
|
+
} catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
if (local.length > 1) {
|
|
83
|
+
throw new AgentSelectorError("brand_json_ambiguous", `Multiple brand.json agent entries match ${agentUrl}`, {
|
|
84
|
+
agent_url: agentUrl,
|
|
85
|
+
matched_count: local.length,
|
|
86
|
+
matched_entries: local
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
for (const entry of local) {
|
|
90
|
+
const source = JSON.stringify([entry.type, canonicalAgentUrl(agentJwksUri(entry))]);
|
|
91
|
+
if (!seen.has(source)) {
|
|
92
|
+
seen.add(source);
|
|
93
|
+
matches.push(entry);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
43
97
|
if (matches.length === 0) {
|
|
44
98
|
throw new AgentSelectorError(
|
|
45
99
|
"agent_not_in_brand_json",
|
|
46
|
-
`No brand.json agent entry has
|
|
100
|
+
`No brand.json agent entry has canonical URL ${canonicalUrl}`,
|
|
47
101
|
{ agent_url: agentUrl }
|
|
48
102
|
);
|
|
49
103
|
}
|
|
@@ -56,8 +110,36 @@ function selectAgentByUrl(brandJson, agentUrl) {
|
|
|
56
110
|
}
|
|
57
111
|
return matches[0];
|
|
58
112
|
}
|
|
113
|
+
function relyingPartyAgentRecord(brandJson, brandDomain) {
|
|
114
|
+
if (!brandJson || typeof brandJson !== "object") throw new TypeError("Invalid brand.json");
|
|
115
|
+
const obj = brandJson;
|
|
116
|
+
if (!obj.house || typeof obj.house !== "object") return obj;
|
|
117
|
+
const domainHost = (domain) => {
|
|
118
|
+
if (typeof domain !== "string" || !/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*\.?$/i.test(domain))
|
|
119
|
+
throw new TypeError("Invalid brand domain");
|
|
120
|
+
return new URL(canonicalAgentUrl(`https://${domain}`)).hostname;
|
|
121
|
+
};
|
|
122
|
+
const host = domainHost(brandDomain);
|
|
123
|
+
const house = obj.house;
|
|
124
|
+
if (house.domain !== void 0 && domainHost(house.domain) === host) {
|
|
125
|
+
return { agents: house.agents };
|
|
126
|
+
}
|
|
127
|
+
const brands = Array.isArray(obj.brands) ? obj.brands : [];
|
|
128
|
+
const matches = brands.filter((b) => {
|
|
129
|
+
try {
|
|
130
|
+
return b && typeof b === "object" && typeof b.url === "string" && new URL(canonicalAgentUrl(b.url)).hostname === host;
|
|
131
|
+
} catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
if (matches.length !== 1) throw new TypeError("Brand domain is absent or ambiguous in portfolio");
|
|
136
|
+
return { agents: matches[0].agents === void 0 ? house.agents : matches[0].agents };
|
|
137
|
+
}
|
|
59
138
|
export {
|
|
60
139
|
AgentSelectorError,
|
|
140
|
+
agentJwksUri,
|
|
141
|
+
canonicalAgentUrl,
|
|
61
142
|
collectAgentEntries,
|
|
143
|
+
relyingPartyAgentRecord,
|
|
62
144
|
selectAgentByUrl
|
|
63
145
|
};
|
|
@@ -1,32 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Receiver-side ergonomic: resolve a sender's JWKS by fetching their
|
|
3
|
-
* `brand.json`, extracting the `jwks_uri` for the agent whose webhooks we're
|
|
4
|
-
* verifying, and delegating to {@link HttpsJwksResolver}.
|
|
5
|
-
*
|
|
6
|
-
* Hand the resulting instance to `verifyWebhookSignature.jwks` (or
|
|
7
|
-
* `verifyRequestSignature.jwks`) and the receiver never has to know where the
|
|
8
|
-
* sender hosts their keys — brand.json is the single source of truth.
|
|
9
|
-
*
|
|
10
|
-
* Follows the two documented redirect variants of `brand.json`
|
|
11
|
-
* (`authoritative_location` and `house`) up to a caller-configurable hop
|
|
12
|
-
* depth. When the selected agent has no `jwks_uri`, falls back to
|
|
13
|
-
* `/.well-known/jwks.json` on the origin of the agent's `url` — but only
|
|
14
|
-
* when that origin matches the final brand.json origin. The spec's
|
|
15
|
-
* well-known fallback exists so publishers hosting brand.json and their
|
|
16
|
-
* agents on the same origin can skip the explicit `jwks_uri`; accepting a
|
|
17
|
-
* cross-origin fallback would let an attacker pivot the trust anchor to a
|
|
18
|
-
* controlled host with a permissive JWKS.
|
|
19
|
-
*
|
|
20
|
-
* Caching is stacked with the inner {@link HttpsJwksResolver}: brand.json
|
|
21
|
-
* honors its own `Cache-Control`/`ETag` (bounded by `maxAgeSeconds`), and
|
|
22
|
-
* unknown-kid refreshes cascade — first to the JWKS endpoint, then (if the
|
|
23
|
-
* JWKS still doesn't have the kid and the brand.json cooldown has elapsed) to
|
|
24
|
-
* brand.json itself, in case the sender rotated `jwks_uri`.
|
|
25
|
-
*/
|
|
1
|
+
/** Capability-confirmed sender key discovery and SSRF-safe brand.json fetching. */
|
|
26
2
|
import { type SsrfDnsLookup } from '../net/index.mjs';
|
|
27
3
|
import type { JwksResolver } from './jwks.mjs';
|
|
28
4
|
import { type HttpsJwksResolverOptions } from './jwks-https.mjs';
|
|
29
5
|
import type { AdcpJsonWebKey } from './types.mjs';
|
|
6
|
+
import type { AgentProtocol, FetchCapabilitiesFn } from './agent-resolver/resolve-agent.mjs';
|
|
7
|
+
import type { JwksResolution } from './jwks.mjs';
|
|
30
8
|
export type BrandAgentType = 'brand' | 'rights' | 'measurement' | 'governance' | 'creative' | 'sales' | 'buying' | 'signals';
|
|
31
9
|
export type BrandJsonResolverErrorCode = 'invalid_url' | 'invalid_house' | 'redirect_loop' | 'redirect_depth_exceeded' | 'fetch_failed' | 'invalid_body' | 'schema_invalid' | 'agent_not_found' | 'agent_ambiguous' | 'jwks_origin_mismatch';
|
|
32
10
|
/**
|
|
@@ -45,21 +23,20 @@ export declare class BrandJsonResolverError extends Error {
|
|
|
45
23
|
});
|
|
46
24
|
}
|
|
47
25
|
export interface BrandJsonJwksResolverOptions {
|
|
26
|
+
/** Expected agent URL. Prefer supplying it; legacy configurations infer one onboarding URL, then confirm it through capabilities and canonical matching. */
|
|
27
|
+
agentUrl?: string;
|
|
28
|
+
/** Capabilities transport. Defaults to MCP; A2A integrations must set 'a2a'. */
|
|
29
|
+
protocol?: AgentProtocol;
|
|
30
|
+
/** Enable the 3.x webhook-only domain-derived fallback. Default true. Request verification refuses these keys. */
|
|
31
|
+
legacyWebhookFallback?: boolean;
|
|
32
|
+
fetchCapabilities?: FetchCapabilitiesFn;
|
|
48
33
|
/** Functional role of the agent whose keys we want to resolve. */
|
|
49
34
|
agentType: BrandAgentType;
|
|
50
35
|
/**
|
|
51
|
-
* Agent id from `agents[].id
|
|
52
|
-
* one agent of the requested type (otherwise the selector is ambiguous).
|
|
53
|
-
* Optional when the type is unique.
|
|
36
|
+
* Agent id from `agents[].id`, used only to narrow canonical URL matches.
|
|
54
37
|
*/
|
|
55
38
|
agentId?: string;
|
|
56
|
-
/**
|
|
57
|
-
* Brand id within a house portfolio (`brands[].id`). When omitted on a
|
|
58
|
-
* portfolio brand.json, the resolver looks at `house.agents[]`. When set,
|
|
59
|
-
* the resolver looks at `brands[brandId].agents[]` first and falls back to
|
|
60
|
-
* `house.agents[]` if no agent of the requested type is declared on the
|
|
61
|
-
* brand itself.
|
|
62
|
-
*/
|
|
39
|
+
/** @deprecated Used only to infer an onboarding URL when agentUrl is absent. Canonical verification searches all operator collections. */
|
|
63
40
|
brandId?: string;
|
|
64
41
|
/**
|
|
65
42
|
* Minimum seconds between brand.json refetches. Mirrors the JWKS cooldown
|
|
@@ -73,11 +50,7 @@ export interface BrandJsonJwksResolverOptions {
|
|
|
73
50
|
* allow longer. Default 3600s (1 hour).
|
|
74
51
|
*/
|
|
75
52
|
maxAgeSeconds?: number;
|
|
76
|
-
/**
|
|
77
|
-
* Maximum redirect hops to follow through `authoritative_location` /
|
|
78
|
-
* `house` variants. Default 3 — enough for `authoritative_location → house
|
|
79
|
-
* → portfolio` without inviting loops.
|
|
80
|
-
*/
|
|
53
|
+
/** @deprecated Only controls legacy onboarding indirection. Explicit capability URLs allow no redirects; webhook fallback allows one document hop. */
|
|
81
54
|
maxRedirects?: number;
|
|
82
55
|
/**
|
|
83
56
|
* Allow `http://` / private-IP brand.json and JWKS URLs (dev loops only).
|
|
@@ -90,52 +63,29 @@ export interface BrandJsonJwksResolverOptions {
|
|
|
90
63
|
* address remains subject to SSRF classification and connection pinning.
|
|
91
64
|
*/
|
|
92
65
|
lookup?: SsrfDnsLookup;
|
|
93
|
-
/**
|
|
94
|
-
* Forwarded to the inner {@link HttpsJwksResolver} constructor.
|
|
95
|
-
* `allowPrivateIp`, `lookup`, and `now` are set from the outer options and
|
|
96
|
-
* should not be passed here.
|
|
97
|
-
*/
|
|
66
|
+
/** Existing settings remain accepted. maxAgeSeconds/minCooldownSeconds configure caching; protocol floors and fail-closed behavior replace other settings. */
|
|
98
67
|
jwksOptions?: Omit<HttpsJwksResolverOptions, 'allowPrivateIp' | 'lookup' | 'now'>;
|
|
99
68
|
/** Clock override for deterministic tests. Returns epoch seconds. */
|
|
100
69
|
now?: () => number;
|
|
101
70
|
}
|
|
102
71
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* the
|
|
72
|
+
* Confirmed operator mapping. The supplied brand.json URL must agree with
|
|
73
|
+
* capabilities on every cache refresh. Prefer ResolvedAgentJwksResolver
|
|
74
|
+
* when the application's onboarding record holds only the seller URL.
|
|
106
75
|
*/
|
|
107
76
|
export declare class BrandJsonJwksResolver implements JwksResolver {
|
|
108
|
-
private readonly
|
|
109
|
-
private readonly
|
|
110
|
-
private
|
|
111
|
-
private
|
|
112
|
-
private
|
|
113
|
-
private readonly allowPrivateIp;
|
|
114
|
-
private readonly lookup;
|
|
115
|
-
private readonly jwksOptions;
|
|
116
|
-
private readonly now;
|
|
117
|
-
private snapshot;
|
|
118
|
-
private inner;
|
|
119
|
-
private inFlight;
|
|
77
|
+
private readonly brandJsonUrl;
|
|
78
|
+
private readonly options;
|
|
79
|
+
private resolver?;
|
|
80
|
+
private onboarding?;
|
|
81
|
+
private lastOnboardingAttempt;
|
|
120
82
|
constructor(brandJsonUrl: string, options: BrandJsonJwksResolverOptions);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
* expired brand.json snapshot, refreshes respecting the cooldown. Unknown
|
|
124
|
-
* kids cascade: first ask the inner HttpsJwksResolver (which will refetch
|
|
125
|
-
* its own URL if cooldown has elapsed); if still unknown, refresh
|
|
126
|
-
* brand.json in case `jwks_uri` rotated.
|
|
127
|
-
*/
|
|
83
|
+
private createResolver;
|
|
84
|
+
private confirmedResolver;
|
|
128
85
|
resolve(keyid: string): Promise<AdcpJsonWebKey | null>;
|
|
129
|
-
|
|
130
|
-
* The agent URL we resolved `jwks_uri` from. Populated after the first
|
|
131
|
-
* successful refresh; useful for verifier result attribution
|
|
132
|
-
* (`VerifyWebhookOptions.agentUrlForKeyid`).
|
|
133
|
-
*/
|
|
86
|
+
resolveWithMetadata(keyid: string): Promise<JwksResolution>;
|
|
134
87
|
get agentUrl(): string | undefined;
|
|
135
|
-
/** Force a refetch of both brand.json and the inner JWKS, bypassing the cooldown. */
|
|
136
88
|
forceRefresh(): Promise<void>;
|
|
137
|
-
private refresh;
|
|
138
|
-
private doRefresh;
|
|
139
89
|
}
|
|
140
90
|
export interface FetchedBrandJson {
|
|
141
91
|
status: 'ok' | 'not_modified';
|
|
@@ -149,6 +99,8 @@ export interface FetchBrandJsonOptions {
|
|
|
149
99
|
startUrl: string;
|
|
150
100
|
/** ETag sent only to the entry URL for cache revalidation. */
|
|
151
101
|
currentEtag?: string;
|
|
102
|
+
/** Initial domain-derived fetch may redirect only between host and exact www counterpart. */
|
|
103
|
+
domainDerived?: boolean;
|
|
152
104
|
/** Maximum JSON-level `authoritative_location` / `house` hops. Default 3, hard maximum 10. */
|
|
153
105
|
maxRedirects?: number;
|
|
154
106
|
/** Permit HTTP and private addresses for controlled development environments. */
|