@adcp/sdk 16.0.0-beta.1 → 16.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/README.md +54 -0
  2. package/dist/lib/core/ConversationTypes.d.mts +8 -3
  3. package/dist/lib/core/ConversationTypes.d.ts +8 -3
  4. package/dist/lib/core/SingleAgentClient.d.mts +15 -4
  5. package/dist/lib/core/SingleAgentClient.d.ts +15 -4
  6. package/dist/lib/core/SingleAgentClient.js +29 -8
  7. package/dist/lib/core/SingleAgentClient.mjs +29 -8
  8. package/dist/lib/core/TaskExecutor.js +5 -1
  9. package/dist/lib/core/TaskExecutor.mjs +5 -1
  10. package/dist/lib/errors/index.d.mts +9 -4
  11. package/dist/lib/errors/index.d.ts +9 -4
  12. package/dist/lib/identity-3.3/generated-compat.d.mts +34 -0
  13. package/dist/lib/identity-3.3/generated-compat.d.ts +34 -0
  14. package/dist/lib/identity-3.3/generated-compat.js +16 -0
  15. package/dist/lib/identity-3.3/generated-compat.mjs +0 -0
  16. package/dist/lib/identity-3.3/index.d.mts +2 -2
  17. package/dist/lib/identity-3.3/index.d.ts +2 -2
  18. package/dist/lib/identity-3.3/index.js +4 -0
  19. package/dist/lib/identity-3.3/index.mjs +5 -1
  20. package/dist/lib/identity-3.3/lifecycle.d.mts +17 -5
  21. package/dist/lib/identity-3.3/lifecycle.d.ts +17 -5
  22. package/dist/lib/identity-3.3/lifecycle.js +32 -5
  23. package/dist/lib/identity-3.3/lifecycle.mjs +38 -6
  24. package/dist/lib/identity-3.3/revocation.d.mts +39 -3
  25. package/dist/lib/identity-3.3/revocation.d.ts +39 -3
  26. package/dist/lib/identity-3.3/revocation.js +88 -18
  27. package/dist/lib/identity-3.3/revocation.mjs +89 -19
  28. package/dist/lib/identity-3.3/server.d.mts +12 -0
  29. package/dist/lib/identity-3.3/server.d.ts +12 -0
  30. package/dist/lib/identity-3.3/server.js +103 -43
  31. package/dist/lib/identity-3.3/server.mjs +111 -45
  32. package/dist/lib/identity-3.3/trust.d.mts +62 -1
  33. package/dist/lib/identity-3.3/trust.d.ts +62 -1
  34. package/dist/lib/identity-3.3/trust.js +126 -21
  35. package/dist/lib/identity-3.3/trust.mjs +124 -21
  36. package/dist/lib/identity-3.3/types.d.mts +39 -11
  37. package/dist/lib/identity-3.3/types.d.ts +39 -11
  38. package/dist/lib/index.d.mts +3 -3
  39. package/dist/lib/index.d.ts +3 -3
  40. package/dist/lib/index.js +8 -0
  41. package/dist/lib/index.mjs +9 -1
  42. package/dist/lib/negotiation/types.d.mts +20 -39
  43. package/dist/lib/negotiation/types.d.ts +20 -39
  44. package/dist/lib/protocols/index.d.mts +61 -25
  45. package/dist/lib/protocols/index.d.ts +61 -25
  46. package/dist/lib/protocols/index.js +11 -6
  47. package/dist/lib/protocols/index.mjs +5 -2
  48. package/dist/lib/protocols/mcp-modern.js +1 -0
  49. package/dist/lib/protocols/mcp-modern.mjs +2 -1
  50. package/dist/lib/protocols/mcp.js +1 -0
  51. package/dist/lib/protocols/mcp.mjs +2 -1
  52. package/dist/lib/protocols/responseSizeLimit.d.mts +91 -5
  53. package/dist/lib/protocols/responseSizeLimit.d.ts +91 -5
  54. package/dist/lib/protocols/responseSizeLimit.js +195 -31
  55. package/dist/lib/protocols/responseSizeLimit.mjs +192 -31
  56. package/dist/lib/reporting/bigquery/index.js +8 -0
  57. package/dist/lib/reporting/bigquery/index.mjs +8 -0
  58. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  59. package/dist/lib/server/auth-signature.d.mts +6 -1
  60. package/dist/lib/server/auth-signature.d.ts +6 -1
  61. package/dist/lib/server/auth-signature.js +24 -13
  62. package/dist/lib/server/auth-signature.mjs +29 -13
  63. package/dist/lib/server/auth.d.mts +2 -2
  64. package/dist/lib/server/auth.d.ts +2 -2
  65. package/dist/lib/server/create-adcp-server.js +31 -10
  66. package/dist/lib/server/create-adcp-server.mjs +37 -11
  67. package/dist/lib/signing/crypto.d.mts +15 -0
  68. package/dist/lib/signing/crypto.d.ts +15 -0
  69. package/dist/lib/signing/crypto.js +83 -3
  70. package/dist/lib/signing/crypto.mjs +86 -3
  71. package/dist/lib/signing/fetch-async.js +1 -1
  72. package/dist/lib/signing/fetch-async.mjs +1 -1
  73. package/dist/lib/signing/fetch.js +1 -1
  74. package/dist/lib/signing/fetch.mjs +1 -1
  75. package/dist/lib/signing/middleware.js +9 -0
  76. package/dist/lib/signing/middleware.mjs +14 -0
  77. package/dist/lib/signing/parser.d.mts +8 -5
  78. package/dist/lib/signing/parser.d.ts +8 -5
  79. package/dist/lib/signing/parser.js +77 -61
  80. package/dist/lib/signing/parser.mjs +77 -61
  81. package/dist/lib/signing/revocation.js +3 -0
  82. package/dist/lib/signing/revocation.mjs +3 -0
  83. package/dist/lib/signing/server.d.mts +1 -1
  84. package/dist/lib/signing/server.d.ts +1 -1
  85. package/dist/lib/signing/server.js +2 -0
  86. package/dist/lib/signing/server.mjs +2 -1
  87. package/dist/lib/signing/signer.js +2 -8
  88. package/dist/lib/signing/signer.mjs +3 -9
  89. package/dist/lib/signing/testing.js +2 -6
  90. package/dist/lib/signing/testing.mjs +3 -7
  91. package/dist/lib/signing/verifier.js +3 -3
  92. package/dist/lib/signing/verifier.mjs +4 -4
  93. package/dist/lib/signing/web-bot-auth.d.mts +84 -10
  94. package/dist/lib/signing/web-bot-auth.d.ts +84 -10
  95. package/dist/lib/signing/web-bot-auth.js +278 -88
  96. package/dist/lib/signing/web-bot-auth.mjs +279 -94
  97. package/dist/lib/signing/webhook-verifier.js +3 -3
  98. package/dist/lib/signing/webhook-verifier.mjs +4 -4
  99. package/dist/lib/substitution/compile.js +2 -1
  100. package/dist/lib/substitution/compile.mjs +2 -1
  101. package/dist/lib/substitution/consent-macros.d.mts +4 -0
  102. package/dist/lib/substitution/consent-macros.d.ts +4 -0
  103. package/dist/lib/substitution/consent-macros.js +45 -0
  104. package/dist/lib/substitution/consent-macros.mjs +20 -0
  105. package/dist/lib/substitution/index.d.mts +1 -1
  106. package/dist/lib/substitution/index.d.ts +1 -1
  107. package/dist/lib/substitution/index.js +2 -0
  108. package/dist/lib/substitution/index.mjs +2 -1
  109. package/dist/lib/substitution/translate.d.mts +15 -2
  110. package/dist/lib/substitution/translate.d.ts +15 -2
  111. package/dist/lib/substitution/translate.js +148 -26
  112. package/dist/lib/substitution/translate.mjs +147 -26
  113. package/dist/lib/testing/client.js +2 -1
  114. package/dist/lib/testing/client.mjs +2 -1
  115. package/dist/lib/testing/scenarios/capabilities.js +1 -1
  116. package/dist/lib/testing/scenarios/capabilities.mjs +1 -1
  117. package/dist/lib/testing/storyboard/request-signing/builder.js +2 -5
  118. package/dist/lib/testing/storyboard/request-signing/builder.mjs +3 -6
  119. package/dist/lib/testing/storyboard/signer-grader/grader.js +2 -7
  120. package/dist/lib/testing/storyboard/signer-grader/grader.mjs +2 -7
  121. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  122. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  123. package/dist/lib/types/enums.generated.d.mts +3 -0
  124. package/dist/lib/types/enums.generated.d.ts +3 -0
  125. package/dist/lib/types/enums.generated.js +9 -0
  126. package/dist/lib/types/enums.generated.mjs +6 -0
  127. package/dist/lib/v2/projection/catalog-snapshot.js +3 -1
  128. package/dist/lib/v2/projection/catalog-snapshot.mjs +3 -1
  129. package/dist/lib/v2/projection/creative-delivery.d.mts +31 -9
  130. package/dist/lib/v2/projection/creative-delivery.d.ts +31 -9
  131. package/dist/lib/v2/projection/index.d.mts +1 -1
  132. package/dist/lib/v2/projection/index.d.ts +1 -1
  133. package/dist/lib/v2/projection/index.js +2 -0
  134. package/dist/lib/v2/projection/index.mjs +2 -0
  135. package/dist/lib/v2/projection/legacy-routes.d.mts +10 -0
  136. package/dist/lib/v2/projection/legacy-routes.d.ts +10 -0
  137. package/dist/lib/v2/projection/legacy-routes.js +7 -0
  138. package/dist/lib/v2/projection/legacy-routes.mjs +6 -0
  139. package/dist/lib/validation/schema-loader.js +25 -0
  140. package/dist/lib/validation/schema-loader.mjs +24 -0
  141. package/dist/lib/version.d.mts +3 -3
  142. package/dist/lib/version.d.ts +3 -3
  143. package/dist/lib/version.js +3 -3
  144. package/dist/lib/version.mjs +3 -3
  145. package/docs/TYPE-SUMMARY.md +7 -2
  146. package/docs/guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md +3 -2
  147. package/docs/guides/REPORTING-LEDGER.md +15 -5
  148. package/docs/guides/REPORTING-OPERATIONS.md +17 -11
  149. package/docs/guides/UNIVERSAL-MACRO-COMPILATION.md +38 -0
  150. package/docs/guides/VALIDATE-YOUR-AGENT.md +12 -0
  151. package/docs/llms.txt +2 -2
  152. package/docs/migration-12-to-13.md +8 -0
  153. package/docs/migration-14.x-rc-worksheet.md +4 -4
  154. package/docs/migration-agent-identity-3.3.md +150 -11
  155. package/package.json +4 -1
@@ -1,6 +1,6 @@
1
1
  import type { PrincipalKind } from '../server/principal/types.mjs';
2
2
  import { TrustEvaluator } from './trust.mjs';
3
- import type { AgentOnboarding, AgentOnboardingRequirement, BillingParty, PrincipalRelationship, IdentityPrincipalChangedReason } from './types.mjs';
3
+ import type { AgentOnboarding, AgentOnboardingRequirement, ObservedAgentOnboardingRequirement, BillingParty, PrincipalRelationship, IdentityPrincipalChangedReason } from './types.mjs';
4
4
  export interface OriginBinding {
5
5
  origin: string;
6
6
  operator: string;
@@ -47,10 +47,13 @@ export declare class InMemoryOriginIdentityStore implements OriginIdentityStore
47
47
  getApplication(origin: string): Promise<IdentityApplication | undefined>;
48
48
  compareAndSetApplication(origin: string, expectedRevision: number | undefined, application: IdentityApplication): Promise<boolean>;
49
49
  }
50
- /** Wrong-direction gates are ignored; unknown gates remain unsatisfied. */
51
- export declare function evaluateOnboardingRequirements(requirements: readonly AgentOnboardingRequirement[], direction: 'seller' | 'buyer', check: (requirement: AgentOnboardingRequirement) => Promise<boolean>): Promise<{
50
+ /**
51
+ * Wrong-direction gates are ignored; unknown gates remain unsatisfied. Accepts
52
+ * requirements read off the wire, whose `kind` may be one this SDK does not know.
53
+ */
54
+ export declare function evaluateOnboardingRequirements<R extends ObservedAgentOnboardingRequirement = AgentOnboardingRequirement>(requirements: readonly R[], direction: 'seller' | 'buyer', check: (requirement: R) => Promise<boolean>): Promise<{
52
55
  satisfied: boolean;
53
- unsatisfied: AgentOnboardingRequirement[];
56
+ unsatisfied: R[];
54
57
  }>;
55
58
  export declare function validateAgentOnboarding(posture: AgentOnboarding, account: {
56
59
  sandbox?: boolean;
@@ -93,7 +96,16 @@ export declare class OriginIdentityLifecycle {
93
96
  migrationApproved?: boolean;
94
97
  sharedOrigin?: boolean;
95
98
  }): Promise<OriginBinding>;
96
- /** Cryptographic verification does not call this. Authorization revalidates delisting. */
99
+ /**
100
+ * Cryptographic verification does not call this. Authorization revalidates
101
+ * delisting: the last record read does not list the origin (an empty
102
+ * `agents[]` retires it; a known delisting stays delisted through a failed
103
+ * refresh) and throws {@link TrustMembershipError}. An unreadable record
104
+ * (404/410 included) throws {@link TrustRecordUnavailableError} for spend;
105
+ * read-only and identity-configuration calls continue under the binding only
106
+ * while the last record read listed the origin and is within the evaluator's
107
+ * `maxOutageContinuationSeconds`.
108
+ */
97
109
  authorizeBound(originUrl: string, access?: {
98
110
  readOnly?: true;
99
111
  identityConfiguration?: true;
@@ -1,6 +1,6 @@
1
1
  import type { PrincipalKind } from '../server/principal/types';
2
2
  import { TrustEvaluator } from './trust';
3
- import type { AgentOnboarding, AgentOnboardingRequirement, BillingParty, PrincipalRelationship, IdentityPrincipalChangedReason } from './types';
3
+ import type { AgentOnboarding, AgentOnboardingRequirement, ObservedAgentOnboardingRequirement, BillingParty, PrincipalRelationship, IdentityPrincipalChangedReason } from './types';
4
4
  export interface OriginBinding {
5
5
  origin: string;
6
6
  operator: string;
@@ -47,10 +47,13 @@ export declare class InMemoryOriginIdentityStore implements OriginIdentityStore
47
47
  getApplication(origin: string): Promise<IdentityApplication | undefined>;
48
48
  compareAndSetApplication(origin: string, expectedRevision: number | undefined, application: IdentityApplication): Promise<boolean>;
49
49
  }
50
- /** Wrong-direction gates are ignored; unknown gates remain unsatisfied. */
51
- export declare function evaluateOnboardingRequirements(requirements: readonly AgentOnboardingRequirement[], direction: 'seller' | 'buyer', check: (requirement: AgentOnboardingRequirement) => Promise<boolean>): Promise<{
50
+ /**
51
+ * Wrong-direction gates are ignored; unknown gates remain unsatisfied. Accepts
52
+ * requirements read off the wire, whose `kind` may be one this SDK does not know.
53
+ */
54
+ export declare function evaluateOnboardingRequirements<R extends ObservedAgentOnboardingRequirement = AgentOnboardingRequirement>(requirements: readonly R[], direction: 'seller' | 'buyer', check: (requirement: R) => Promise<boolean>): Promise<{
52
55
  satisfied: boolean;
53
- unsatisfied: AgentOnboardingRequirement[];
56
+ unsatisfied: R[];
54
57
  }>;
55
58
  export declare function validateAgentOnboarding(posture: AgentOnboarding, account: {
56
59
  sandbox?: boolean;
@@ -93,7 +96,16 @@ export declare class OriginIdentityLifecycle {
93
96
  migrationApproved?: boolean;
94
97
  sharedOrigin?: boolean;
95
98
  }): Promise<OriginBinding>;
96
- /** Cryptographic verification does not call this. Authorization revalidates delisting. */
99
+ /**
100
+ * Cryptographic verification does not call this. Authorization revalidates
101
+ * delisting: the last record read does not list the origin (an empty
102
+ * `agents[]` retires it; a known delisting stays delisted through a failed
103
+ * refresh) and throws {@link TrustMembershipError}. An unreadable record
104
+ * (404/410 included) throws {@link TrustRecordUnavailableError} for spend;
105
+ * read-only and identity-configuration calls continue under the binding only
106
+ * while the last record read listed the origin and is within the evaluator's
107
+ * `maxOutageContinuationSeconds`.
108
+ */
97
109
  authorizeBound(originUrl: string, access?: {
98
110
  readOnly?: true;
99
111
  identityConfiguration?: true;
@@ -66,14 +66,20 @@ class InMemoryOriginIdentityStore {
66
66
  }
67
67
  }
68
68
  const BUYER_GATES = /* @__PURE__ */ new Set(["aao_verified", "storyboard", "sandbox", "experimental_feature"]);
69
- const SELLER_GATES = /* @__PURE__ */ new Set(["agent_identity", "grant", "billing_entity", "contract", "credit_application"]);
69
+ const SELLER_GATES = /* @__PURE__ */ new Set([
70
+ "agent_identity",
71
+ "grant",
72
+ "billing_entity",
73
+ "contract",
74
+ "credit_application"
75
+ ]);
70
76
  async function evaluateOnboardingRequirements(requirements, direction, check) {
71
77
  const relevant = direction === "seller" ? SELLER_GATES : BUYER_GATES;
72
78
  const wrong = direction === "seller" ? BUYER_GATES : SELLER_GATES;
73
79
  const unsatisfied = [];
74
80
  for (const requirement of requirements) {
75
81
  if (wrong.has(requirement.kind)) continue;
76
- const wellFormed = requirement.kind !== "aao_verified" || !!requirement.role && !!requirement.verification_modes?.length && !!requirement.specialisms?.length && !!requirement.adcp_version;
82
+ const wellFormed = requirement.kind !== "aao_verified" || !!requirement.role && !!requirement.verification_modes?.length && !!(requirement.verified_specialisms ?? requirement.specialisms)?.length && !!requirement.adcp_version;
77
83
  const hasId = !["storyboard", "experimental_feature"].includes(requirement.kind) || !!requirement.id;
78
84
  if (!relevant.has(requirement.kind) || !wellFormed || !hasId || !await check(requirement))
79
85
  unsatisfied.push(requirement);
@@ -126,13 +132,34 @@ class OriginIdentityLifecycle {
126
132
  await this.options.store.bind(binding);
127
133
  return binding;
128
134
  }
129
- /** Cryptographic verification does not call this. Authorization revalidates delisting. */
135
+ /**
136
+ * Cryptographic verification does not call this. Authorization revalidates
137
+ * delisting: the last record read does not list the origin (an empty
138
+ * `agents[]` retires it; a known delisting stays delisted through a failed
139
+ * refresh) and throws {@link TrustMembershipError}. An unreadable record
140
+ * (404/410 included) throws {@link TrustRecordUnavailableError} for spend;
141
+ * read-only and identity-configuration calls continue under the binding only
142
+ * while the last record read listed the origin and is within the evaluator's
143
+ * `maxOutageContinuationSeconds`.
144
+ */
130
145
  async authorizeBound(originUrl, access) {
131
146
  const origin = (0, import_canonicalize.canonicalAgentOrigin)(originUrl);
132
147
  const binding = await this.options.store.getBinding(origin);
133
148
  if (!binding) throw new import_trust.IdentityAuthorizationError();
134
- const trust = await this.options.trust.match(origin, "adcp:buying");
135
- if (binding.operator !== trust.operator) throw new import_trust.IdentityAuthorizationError();
149
+ try {
150
+ let trust;
151
+ try {
152
+ trust = await this.options.trust.match(origin, "adcp:buying");
153
+ } catch (error) {
154
+ if (error instanceof import_trust.TrustMembershipError && error.membership === "absent")
155
+ throw Object.assign(new import_trust.TrustRecordUnavailableError(), { cause: error });
156
+ throw error;
157
+ }
158
+ if (binding.operator !== trust.operator) throw new import_trust.TrustMembershipError();
159
+ } catch (error) {
160
+ if (!(error instanceof import_trust.TrustRecordUnavailableError) || !error.lastKnownListed || !(access?.readOnly || access?.identityConfiguration))
161
+ throw error;
162
+ }
136
163
  const relationship = (await this.options.store.getApplication(origin))?.relationship;
137
164
  if (!access?.readOnly && relationship && (relationship.status !== "active" || relationship.expires_at && Date.parse(relationship.expires_at) <= this.now() * 1e3))
138
165
  throw new import_trust.IdentityAuthorizationError();
@@ -2,7 +2,12 @@ import { randomUUID } from "crypto";
2
2
  import { parse as parseTld } from "tldts";
3
3
  import { canonicalAgentOrigin } from "../signing/agent-resolver/canonicalize.mjs";
4
4
  import { eTldPlusOne } from "../signing/agent-resolver/etld.mjs";
5
- import { IdentityAuthorizationError, validateIdentity33Schema } from "./trust.mjs";
5
+ import {
6
+ IdentityAuthorizationError,
7
+ TrustMembershipError,
8
+ TrustRecordUnavailableError,
9
+ validateIdentity33Schema
10
+ } from "./trust.mjs";
6
11
  class InMemoryOriginIdentityStore {
7
12
  durability = "process-local";
8
13
  bindings = /* @__PURE__ */ new Map();
@@ -40,14 +45,20 @@ class InMemoryOriginIdentityStore {
40
45
  }
41
46
  }
42
47
  const BUYER_GATES = /* @__PURE__ */ new Set(["aao_verified", "storyboard", "sandbox", "experimental_feature"]);
43
- const SELLER_GATES = /* @__PURE__ */ new Set(["agent_identity", "grant", "billing_entity", "contract", "credit_application"]);
48
+ const SELLER_GATES = /* @__PURE__ */ new Set([
49
+ "agent_identity",
50
+ "grant",
51
+ "billing_entity",
52
+ "contract",
53
+ "credit_application"
54
+ ]);
44
55
  async function evaluateOnboardingRequirements(requirements, direction, check) {
45
56
  const relevant = direction === "seller" ? SELLER_GATES : BUYER_GATES;
46
57
  const wrong = direction === "seller" ? BUYER_GATES : SELLER_GATES;
47
58
  const unsatisfied = [];
48
59
  for (const requirement of requirements) {
49
60
  if (wrong.has(requirement.kind)) continue;
50
- const wellFormed = requirement.kind !== "aao_verified" || !!requirement.role && !!requirement.verification_modes?.length && !!requirement.specialisms?.length && !!requirement.adcp_version;
61
+ const wellFormed = requirement.kind !== "aao_verified" || !!requirement.role && !!requirement.verification_modes?.length && !!(requirement.verified_specialisms ?? requirement.specialisms)?.length && !!requirement.adcp_version;
51
62
  const hasId = !["storyboard", "experimental_feature"].includes(requirement.kind) || !!requirement.id;
52
63
  if (!relevant.has(requirement.kind) || !wellFormed || !hasId || !await check(requirement))
53
64
  unsatisfied.push(requirement);
@@ -100,13 +111,34 @@ class OriginIdentityLifecycle {
100
111
  await this.options.store.bind(binding);
101
112
  return binding;
102
113
  }
103
- /** Cryptographic verification does not call this. Authorization revalidates delisting. */
114
+ /**
115
+ * Cryptographic verification does not call this. Authorization revalidates
116
+ * delisting: the last record read does not list the origin (an empty
117
+ * `agents[]` retires it; a known delisting stays delisted through a failed
118
+ * refresh) and throws {@link TrustMembershipError}. An unreadable record
119
+ * (404/410 included) throws {@link TrustRecordUnavailableError} for spend;
120
+ * read-only and identity-configuration calls continue under the binding only
121
+ * while the last record read listed the origin and is within the evaluator's
122
+ * `maxOutageContinuationSeconds`.
123
+ */
104
124
  async authorizeBound(originUrl, access) {
105
125
  const origin = canonicalAgentOrigin(originUrl);
106
126
  const binding = await this.options.store.getBinding(origin);
107
127
  if (!binding) throw new IdentityAuthorizationError();
108
- const trust = await this.options.trust.match(origin, "adcp:buying");
109
- if (binding.operator !== trust.operator) throw new IdentityAuthorizationError();
128
+ try {
129
+ let trust;
130
+ try {
131
+ trust = await this.options.trust.match(origin, "adcp:buying");
132
+ } catch (error) {
133
+ if (error instanceof TrustMembershipError && error.membership === "absent")
134
+ throw Object.assign(new TrustRecordUnavailableError(), { cause: error });
135
+ throw error;
136
+ }
137
+ if (binding.operator !== trust.operator) throw new TrustMembershipError();
138
+ } catch (error) {
139
+ if (!(error instanceof TrustRecordUnavailableError) || !error.lastKnownListed || !(access?.readOnly || access?.identityConfiguration))
140
+ throw error;
141
+ }
110
142
  const relationship = (await this.options.store.getApplication(origin))?.relationship;
111
143
  if (!access?.readOnly && relationship && (relationship.status !== "active" || relationship.expires_at && Date.parse(relationship.expires_at) <= this.now() * 1e3))
112
144
  throw new IdentityAuthorizationError();
@@ -14,17 +14,33 @@ export interface MonotonicOriginRevocationStore {
14
14
  merge(origin: string, snapshot: OriginRevocationSnapshot): Promise<void>;
15
15
  get(origin: string, keyid: string): Promise<RevocationDetail | undefined>;
16
16
  }
17
+ /**
18
+ * Process-local revocation history for development and tests. Public
19
+ * deployments need durable history (`createIdentity33StateStores`) so a
20
+ * restart never forgets a revocation.
21
+ *
22
+ * Capacity is bounded per origin, per operator (registrable domain) and in
23
+ * total. The operator budget stops one registrable domain's wildcard
24
+ * subdomains from filling the store alone; it does not reserve capacity for
25
+ * anyone else, so several operators together can still exhaust the total.
26
+ * Facts are never evicted to regain capacity; a refused merge fails closed.
27
+ */
17
28
  export declare class InMemoryOriginRevocationStore implements MonotonicOriginRevocationStore {
18
- private readonly limits;
19
29
  readonly durability: "process-local";
20
30
  private readonly revoked;
31
+ private readonly originKeys;
32
+ private readonly operatorKeys;
33
+ private readonly limits;
21
34
  constructor(limits?: {
22
- perOrigin: number;
23
- total: number;
35
+ perOrigin?: number;
36
+ perOperator?: number;
37
+ total?: number;
24
38
  });
25
39
  merge(origin: string, snapshot: OriginRevocationSnapshot): Promise<void>;
26
40
  get(origin: string, keyid: string): Promise<RevocationDetail | undefined>;
27
41
  }
42
+ /** See {@link OriginRevocationResolver.status}. */
43
+ export type OriginRevocationStatus = 'revoked' | 'current' | 'absent' | 'unknown';
28
44
  export declare class OriginRevocationResolver {
29
45
  private readonly options;
30
46
  private readonly freshness;
@@ -33,6 +49,7 @@ export declare class OriginRevocationResolver {
33
49
  private readonly pending;
34
50
  private readonly failures;
35
51
  private readonly persistenceFailures;
52
+ private readonly absentUntil;
36
53
  constructor(options: {
37
54
  directory: import('../signing/web-bot-auth.mjs').SignatureDirectoryResolver;
38
55
  store: MonotonicOriginRevocationStore;
@@ -41,7 +58,26 @@ export declare class OriginRevocationResolver {
41
58
  fetchOptions?: Pick<SsrfFetchOptions, 'lookup' | 'trustedFetchFn'>;
42
59
  maxOrigins?: number;
43
60
  });
61
+ /**
62
+ * Compatibility wrapper over {@link status}: `true` when revoked, `false`
63
+ * when the list is current, and `revocation_unavailable` when the identity
64
+ * publishes no list (`absent`) or its status is not known (`unknown`).
65
+ */
44
66
  isRevoked(originUrl: string, keyid: string): Promise<boolean>;
67
+ /**
68
+ * Revocation status of `keyid` at `origin`:
69
+ * - `revoked`: a stored (permanent) revocation covers the key.
70
+ * - `current`: a signed list is current, or within `next_update` + grace.
71
+ * - `absent`: the origin serves no list (404/410). The identity is still
72
+ * established but cannot be bound for spend.
73
+ * - `unknown`: no list could be verified and grace has passed (or there was
74
+ * never a list). The identity is not established.
75
+ * Throws {@link WebBotAuthUnavailableError} (`revocation_persistence_failed`)
76
+ * when the history store fails: an infrastructure outage, never a
77
+ * revocation outcome.
78
+ */
79
+ status(originUrl: string, keyid: string): Promise<OriginRevocationStatus>;
80
+ private currentStatus;
45
81
  private readStored;
46
82
  private refresh;
47
83
  private now;
@@ -14,17 +14,33 @@ export interface MonotonicOriginRevocationStore {
14
14
  merge(origin: string, snapshot: OriginRevocationSnapshot): Promise<void>;
15
15
  get(origin: string, keyid: string): Promise<RevocationDetail | undefined>;
16
16
  }
17
+ /**
18
+ * Process-local revocation history for development and tests. Public
19
+ * deployments need durable history (`createIdentity33StateStores`) so a
20
+ * restart never forgets a revocation.
21
+ *
22
+ * Capacity is bounded per origin, per operator (registrable domain) and in
23
+ * total. The operator budget stops one registrable domain's wildcard
24
+ * subdomains from filling the store alone; it does not reserve capacity for
25
+ * anyone else, so several operators together can still exhaust the total.
26
+ * Facts are never evicted to regain capacity; a refused merge fails closed.
27
+ */
17
28
  export declare class InMemoryOriginRevocationStore implements MonotonicOriginRevocationStore {
18
- private readonly limits;
19
29
  readonly durability: "process-local";
20
30
  private readonly revoked;
31
+ private readonly originKeys;
32
+ private readonly operatorKeys;
33
+ private readonly limits;
21
34
  constructor(limits?: {
22
- perOrigin: number;
23
- total: number;
35
+ perOrigin?: number;
36
+ perOperator?: number;
37
+ total?: number;
24
38
  });
25
39
  merge(origin: string, snapshot: OriginRevocationSnapshot): Promise<void>;
26
40
  get(origin: string, keyid: string): Promise<RevocationDetail | undefined>;
27
41
  }
42
+ /** See {@link OriginRevocationResolver.status}. */
43
+ export type OriginRevocationStatus = 'revoked' | 'current' | 'absent' | 'unknown';
28
44
  export declare class OriginRevocationResolver {
29
45
  private readonly options;
30
46
  private readonly freshness;
@@ -33,6 +49,7 @@ export declare class OriginRevocationResolver {
33
49
  private readonly pending;
34
50
  private readonly failures;
35
51
  private readonly persistenceFailures;
52
+ private readonly absentUntil;
36
53
  constructor(options: {
37
54
  directory: import('../signing/web-bot-auth').SignatureDirectoryResolver;
38
55
  store: MonotonicOriginRevocationStore;
@@ -41,7 +58,26 @@ export declare class OriginRevocationResolver {
41
58
  fetchOptions?: Pick<SsrfFetchOptions, 'lookup' | 'trustedFetchFn'>;
42
59
  maxOrigins?: number;
43
60
  });
61
+ /**
62
+ * Compatibility wrapper over {@link status}: `true` when revoked, `false`
63
+ * when the list is current, and `revocation_unavailable` when the identity
64
+ * publishes no list (`absent`) or its status is not known (`unknown`).
65
+ */
44
66
  isRevoked(originUrl: string, keyid: string): Promise<boolean>;
67
+ /**
68
+ * Revocation status of `keyid` at `origin`:
69
+ * - `revoked`: a stored (permanent) revocation covers the key.
70
+ * - `current`: a signed list is current, or within `next_update` + grace.
71
+ * - `absent`: the origin serves no list (404/410). The identity is still
72
+ * established but cannot be bound for spend.
73
+ * - `unknown`: no list could be verified and grace has passed (or there was
74
+ * never a list). The identity is not established.
75
+ * Throws {@link WebBotAuthUnavailableError} (`revocation_persistence_failed`)
76
+ * when the history store fails: an infrastructure outage, never a
77
+ * revocation outcome.
78
+ */
79
+ status(originUrl: string, keyid: string): Promise<OriginRevocationStatus>;
80
+ private currentStatus;
45
81
  private readStored;
46
82
  private refresh;
47
83
  private now;
@@ -45,28 +45,51 @@ const REASONS = [
45
45
  "keyCompromise"
46
46
  ];
47
47
  class InMemoryOriginRevocationStore {
48
- constructor(limits = { perOrigin: 16384, total: 1e5 }) {
49
- this.limits = limits;
50
- }
51
- limits;
52
48
  durability = "process-local";
53
49
  revoked = /* @__PURE__ */ new Map();
50
+ originKeys = /* @__PURE__ */ new Map();
51
+ operatorKeys = /* @__PURE__ */ new Map();
52
+ limits;
53
+ constructor(limits = {}) {
54
+ this.limits = {
55
+ perOrigin: limits.perOrigin ?? 16384,
56
+ perOperator: limits.perOperator ?? 16384,
57
+ total: limits.total ?? 1e5
58
+ };
59
+ if (Object.values(this.limits).some((value) => !Number.isSafeInteger(value) || value < 1))
60
+ throw new TypeError("Revocation limits must be positive safe integers");
61
+ }
54
62
  async merge(origin, snapshot) {
55
63
  origin = (0, import_canonicalize.canonicalAgentOrigin)(origin);
64
+ const operator = operatorOf(origin);
56
65
  const additions = new Set(snapshot.revoked_kids.filter((kid) => !this.revoked.has(JSON.stringify([origin, kid]))));
57
- const count = [...this.revoked.keys()].filter((key) => JSON.parse(key)[0] === origin).length;
58
- if (count + additions.size > this.limits.perOrigin || this.revoked.size + additions.size > this.limits.total)
66
+ const originCount = this.originKeys.get(origin) ?? 0, operatorCount = this.operatorKeys.get(operator) ?? 0;
67
+ if (originCount + additions.size > this.limits.perOrigin || operatorCount + additions.size > this.limits.perOperator || this.revoked.size + additions.size > this.limits.total)
59
68
  throw new import_web_bot_auth.WebBotAuthError(403, "revocation_storage_limit");
60
69
  for (const kid of snapshot.revoked_kids) {
61
70
  const key = JSON.stringify([origin, kid]), prior = this.revoked.get(key), detail = snapshot.revocation_details?.[kid] ?? {};
62
71
  this.revoked.set(key, (0, import_revocation2.mergeRevocationDetails)(prior, detail));
63
72
  }
73
+ if (additions.size) {
74
+ this.originKeys.set(origin, originCount + additions.size);
75
+ this.operatorKeys.set(operator, operatorCount + additions.size);
76
+ }
64
77
  }
65
78
  async get(origin, keyid) {
66
79
  const entry = this.revoked.get(JSON.stringify([(0, import_canonicalize.canonicalAgentOrigin)(origin), keyid]));
67
80
  return entry && { ...entry };
68
81
  }
69
82
  }
83
+ function operatorOf(origin) {
84
+ try {
85
+ return (0, import_etld.eTldPlusOne)(origin);
86
+ } catch {
87
+ return new URL(origin).hostname;
88
+ }
89
+ }
90
+ const MAX_POLLING_SECONDS = 1800;
91
+ class RevocationListAbsentError extends Error {
92
+ }
70
93
  class OriginRevocationResolver {
71
94
  constructor(options) {
72
95
  this.options = options;
@@ -82,31 +105,73 @@ class OriginRevocationResolver {
82
105
  pending = /* @__PURE__ */ new Map();
83
106
  failures = /* @__PURE__ */ new Map();
84
107
  persistenceFailures = /* @__PURE__ */ new Set();
108
+ absentUntil = /* @__PURE__ */ new Map();
109
+ /**
110
+ * Compatibility wrapper over {@link status}: `true` when revoked, `false`
111
+ * when the list is current, and `revocation_unavailable` when the identity
112
+ * publishes no list (`absent`) or its status is not known (`unknown`).
113
+ */
85
114
  async isRevoked(originUrl, keyid) {
115
+ const state = await this.status(originUrl, keyid);
116
+ if (state === "revoked") return true;
117
+ if (state === "current") return false;
118
+ throw new import_web_bot_auth.WebBotAuthError(403, state === "absent" ? "revocation_absent" : "revocation_unavailable");
119
+ }
120
+ /**
121
+ * Revocation status of `keyid` at `origin`:
122
+ * - `revoked`: a stored (permanent) revocation covers the key.
123
+ * - `current`: a signed list is current, or within `next_update` + grace.
124
+ * - `absent`: the origin serves no list (404/410). The identity is still
125
+ * established but cannot be bound for spend.
126
+ * - `unknown`: no list could be verified and grace has passed (or there was
127
+ * never a list). The identity is not established.
128
+ * Throws {@link WebBotAuthUnavailableError} (`revocation_persistence_failed`)
129
+ * when the history store fails: an infrastructure outage, never a
130
+ * revocation outcome.
131
+ */
132
+ async status(originUrl, keyid) {
133
+ try {
134
+ return await this.currentStatus(originUrl, keyid);
135
+ } catch (error) {
136
+ if (error instanceof import_web_bot_auth.WebBotAuthError && error.reason === "revocation_persistence_failed")
137
+ throw new import_web_bot_auth.WebBotAuthUnavailableError("revocation_persistence_failed", error);
138
+ throw error;
139
+ }
140
+ }
141
+ async currentStatus(originUrl, keyid) {
86
142
  const origin = (0, import_canonicalize.canonicalAgentOrigin)(originUrl), now = this.now();
87
- if (await this.readStored(origin, keyid)) return true;
143
+ if (await this.readStored(origin, keyid)) return "revoked";
144
+ const settled = async () => {
145
+ if (await this.readStored(origin, keyid)) return "revoked";
146
+ if ((this.absentUntil.get(origin) ?? 0) > now) return "absent";
147
+ return (this.freshness.get(origin) ?? 0) > now || (this.graceUntil.get(origin) ?? 0) > now ? "current" : "unknown";
148
+ };
88
149
  if ((this.freshness.get(origin) ?? 0) <= now) {
89
150
  if ((this.failures.get(origin) ?? 0) > now) {
90
151
  if (this.persistenceFailures.has(origin)) throw new import_web_bot_auth.WebBotAuthError(403, "revocation_persistence_failed");
91
- if ((this.graceUntil.get(origin) ?? 0) > now) return false;
92
- throw new import_web_bot_auth.WebBotAuthError(403, "revocation_unavailable");
152
+ return settled();
93
153
  }
94
154
  let promise = this.pending.get(origin);
95
155
  if (!promise) {
96
- if (this.pending.size >= 16 || [...this.pending.keys()].filter((pendingOrigin) => (0, import_etld.eTldPlusOne)(pendingOrigin) === (0, import_etld.eTldPlusOne)(origin)).length >= 2) {
97
- throw new import_web_bot_auth.WebBotAuthError(403, "revocation_unavailable");
156
+ if (this.pending.size >= 16 || [...this.pending.keys()].filter((pendingOrigin) => operatorOf(pendingOrigin) === operatorOf(origin)).length >= 2) {
157
+ return settled();
98
158
  }
99
159
  promise = this.refresh(origin).catch((error) => {
100
160
  if (this.failures.size >= (this.options.maxOrigins ?? 1e3)) {
101
161
  const oldest = this.failures.keys().next().value;
102
162
  this.failures.delete(oldest);
103
163
  this.persistenceFailures.delete(oldest);
164
+ this.absentUntil.delete(oldest);
104
165
  }
105
166
  this.failures.set(origin, now + 60);
106
167
  if (error instanceof import_web_bot_auth.WebBotAuthError && error.reason === "revocation_persistence_failed") {
107
168
  this.persistenceFailures.add(origin);
108
169
  this.graceUntil.delete(origin);
109
170
  }
171
+ if (error.cause instanceof RevocationListAbsentError) {
172
+ this.absentUntil.set(origin, now + 60);
173
+ this.graceUntil.delete(origin);
174
+ }
110
175
  throw error;
111
176
  });
112
177
  this.pending.set(origin, promise);
@@ -115,12 +180,11 @@ class OriginRevocationResolver {
115
180
  await promise;
116
181
  } catch (error) {
117
182
  if (error instanceof import_web_bot_auth.WebBotAuthError && error.reason === "revocation_persistence_failed") throw error;
118
- if ((this.graceUntil.get(origin) ?? 0) <= now) throw error;
119
183
  } finally {
120
184
  if (this.pending.get(origin) === promise) this.pending.delete(origin);
121
185
  }
122
186
  }
123
- return !!await this.readStored(origin, keyid);
187
+ return settled();
124
188
  }
125
189
  async readStored(origin, keyid) {
126
190
  try {
@@ -138,6 +202,7 @@ class OriginRevocationResolver {
138
202
  maxBodyBytes: 256 * 1024,
139
203
  timeoutMs: 1e4
140
204
  });
205
+ if (response.status === 404 || response.status === 410) throw new RevocationListAbsentError();
141
206
  if (response.status !== 200) throw new Error("Unavailable revocation list");
142
207
  const body = new TextDecoder("utf-8", { fatal: true }).decode(response.body);
143
208
  let tokens;
@@ -163,9 +228,9 @@ class OriginRevocationResolver {
163
228
  if (header.typ !== "adcp-gov-revocation+jws" || typeof header.kid !== "string" || typeof header.alg !== "string" || !["EdDSA", "ES256"].includes(header.alg))
164
229
  continue;
165
230
  await this.options.directory.resolve(header.kid, origin);
166
- const proof = this.options.directory.getAuditProof(origin);
167
- if (!proof || new URL(proof.request.url).origin !== origin) continue;
168
- const checked = (0, import_web_bot_auth.verifySignatureDirectory)(proof, this.now(), this.options.directory.allowsTestKeys);
231
+ const checked = this.options.directory.getVerifiedDirectory(origin);
232
+ if (!checked || new URL(checked.proof.request.url).origin !== origin || checked.expires <= this.now())
233
+ continue;
169
234
  const jwk = checked.keys.find((key) => key.kid === header.kid);
170
235
  if (!jwk) continue;
171
236
  const result = await (0, import_jose.compactVerify)(token, await (0, import_jose.importJWK)(jwk, header.alg), {
@@ -182,7 +247,8 @@ class OriginRevocationResolver {
182
247
  32
183
248
  );
184
249
  const updated = Date.parse(snapshot.updated) / 1e3, next = Date.parse(snapshot.next_update) / 1e3, now = this.now();
185
- if (snapshot.issuer !== origin || !Number.isFinite(updated) || !Number.isFinite(next) || updated > now || next <= now || next <= updated || next > now + 1800 || !Array.isArray(snapshot.revoked_kids) || snapshot.revoked_kids.length > 4096 || snapshot.revoked_kids.some((k) => typeof k !== "string" || k.length > 256) || updated < (this.updated.get(origin) ?? 0))
250
+ if (snapshot.issuer !== origin || !Number.isFinite(updated) || !Number.isFinite(next) || // Tolerate a publisher clock slightly ahead, as for request `created`.
251
+ updated > now + 60 || next <= now || next <= updated || !Array.isArray(snapshot.revoked_kids) || snapshot.revoked_kids.length > 4096 || snapshot.revoked_kids.some((k) => typeof k !== "string" || k.length > 256) || updated < (this.updated.get(origin) ?? 0))
186
252
  throw new Error("Invalid revocation snapshot");
187
253
  for (const [kid, detail] of Object.entries(snapshot.revocation_details ?? {}))
188
254
  if (!snapshot.revoked_kids.includes(kid) || !detail || detail.reason && !REASONS.includes(detail.reason) || detail.invalidity_date && !Number.isFinite(Date.parse(detail.invalidity_date)))
@@ -200,11 +266,15 @@ class OriginRevocationResolver {
200
266
  this.updated.delete(oldest);
201
267
  this.graceUntil.delete(oldest);
202
268
  }
203
- this.graceUntil.set(origin, next + 4 * (this.options.pollingIntervalSeconds ?? 300));
269
+ this.graceUntil.set(
270
+ origin,
271
+ Math.min(next, now + MAX_POLLING_SECONDS) + 4 * (this.options.pollingIntervalSeconds ?? 300)
272
+ );
204
273
  this.updated.set(origin, updated);
205
274
  this.freshness.set(origin, Math.min(next, now + (this.options.pollingIntervalSeconds ?? 300)));
206
275
  this.failures.delete(origin);
207
276
  this.persistenceFailures.delete(origin);
277
+ this.absentUntil.delete(origin);
208
278
  } catch (cause) {
209
279
  if (cause instanceof import_web_bot_auth.WebBotAuthError && cause.reason === "revocation_persistence_failed") throw cause;
210
280
  const error = new import_web_bot_auth.WebBotAuthError(403, "revocation_unavailable");