@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
@@ -6,7 +6,7 @@ import { parseStrictJson } from "../signing/agent-resolver/strict-json.mjs";
6
6
  import {
7
7
  SignatureDirectoryResolver,
8
8
  WebBotAuthError,
9
- verifySignatureDirectory,
9
+ WebBotAuthUnavailableError,
10
10
  jwkThumbprint
11
11
  } from "../signing/web-bot-auth.mjs";
12
12
  const REASONS = [
@@ -24,28 +24,51 @@ const REASONS = [
24
24
  import { mergeRevocationDetails } from "../signing/revocation.mjs";
25
25
  import { mergeRevocationDetails as mergeRevocationDetail } from "../signing/revocation.mjs";
26
26
  class InMemoryOriginRevocationStore {
27
- constructor(limits = { perOrigin: 16384, total: 1e5 }) {
28
- this.limits = limits;
29
- }
30
- limits;
31
27
  durability = "process-local";
32
28
  revoked = /* @__PURE__ */ new Map();
29
+ originKeys = /* @__PURE__ */ new Map();
30
+ operatorKeys = /* @__PURE__ */ new Map();
31
+ limits;
32
+ constructor(limits = {}) {
33
+ this.limits = {
34
+ perOrigin: limits.perOrigin ?? 16384,
35
+ perOperator: limits.perOperator ?? 16384,
36
+ total: limits.total ?? 1e5
37
+ };
38
+ if (Object.values(this.limits).some((value) => !Number.isSafeInteger(value) || value < 1))
39
+ throw new TypeError("Revocation limits must be positive safe integers");
40
+ }
33
41
  async merge(origin, snapshot) {
34
42
  origin = canonicalAgentOrigin(origin);
43
+ const operator = operatorOf(origin);
35
44
  const additions = new Set(snapshot.revoked_kids.filter((kid) => !this.revoked.has(JSON.stringify([origin, kid]))));
36
- const count = [...this.revoked.keys()].filter((key) => JSON.parse(key)[0] === origin).length;
37
- if (count + additions.size > this.limits.perOrigin || this.revoked.size + additions.size > this.limits.total)
45
+ const originCount = this.originKeys.get(origin) ?? 0, operatorCount = this.operatorKeys.get(operator) ?? 0;
46
+ if (originCount + additions.size > this.limits.perOrigin || operatorCount + additions.size > this.limits.perOperator || this.revoked.size + additions.size > this.limits.total)
38
47
  throw new WebBotAuthError(403, "revocation_storage_limit");
39
48
  for (const kid of snapshot.revoked_kids) {
40
49
  const key = JSON.stringify([origin, kid]), prior = this.revoked.get(key), detail = snapshot.revocation_details?.[kid] ?? {};
41
50
  this.revoked.set(key, mergeRevocationDetail(prior, detail));
42
51
  }
52
+ if (additions.size) {
53
+ this.originKeys.set(origin, originCount + additions.size);
54
+ this.operatorKeys.set(operator, operatorCount + additions.size);
55
+ }
43
56
  }
44
57
  async get(origin, keyid) {
45
58
  const entry = this.revoked.get(JSON.stringify([canonicalAgentOrigin(origin), keyid]));
46
59
  return entry && { ...entry };
47
60
  }
48
61
  }
62
+ function operatorOf(origin) {
63
+ try {
64
+ return eTldPlusOne(origin);
65
+ } catch {
66
+ return new URL(origin).hostname;
67
+ }
68
+ }
69
+ const MAX_POLLING_SECONDS = 1800;
70
+ class RevocationListAbsentError extends Error {
71
+ }
49
72
  class OriginRevocationResolver {
50
73
  constructor(options) {
51
74
  this.options = options;
@@ -61,31 +84,73 @@ class OriginRevocationResolver {
61
84
  pending = /* @__PURE__ */ new Map();
62
85
  failures = /* @__PURE__ */ new Map();
63
86
  persistenceFailures = /* @__PURE__ */ new Set();
87
+ absentUntil = /* @__PURE__ */ new Map();
88
+ /**
89
+ * Compatibility wrapper over {@link status}: `true` when revoked, `false`
90
+ * when the list is current, and `revocation_unavailable` when the identity
91
+ * publishes no list (`absent`) or its status is not known (`unknown`).
92
+ */
64
93
  async isRevoked(originUrl, keyid) {
94
+ const state = await this.status(originUrl, keyid);
95
+ if (state === "revoked") return true;
96
+ if (state === "current") return false;
97
+ throw new WebBotAuthError(403, state === "absent" ? "revocation_absent" : "revocation_unavailable");
98
+ }
99
+ /**
100
+ * Revocation status of `keyid` at `origin`:
101
+ * - `revoked`: a stored (permanent) revocation covers the key.
102
+ * - `current`: a signed list is current, or within `next_update` + grace.
103
+ * - `absent`: the origin serves no list (404/410). The identity is still
104
+ * established but cannot be bound for spend.
105
+ * - `unknown`: no list could be verified and grace has passed (or there was
106
+ * never a list). The identity is not established.
107
+ * Throws {@link WebBotAuthUnavailableError} (`revocation_persistence_failed`)
108
+ * when the history store fails: an infrastructure outage, never a
109
+ * revocation outcome.
110
+ */
111
+ async status(originUrl, keyid) {
112
+ try {
113
+ return await this.currentStatus(originUrl, keyid);
114
+ } catch (error) {
115
+ if (error instanceof WebBotAuthError && error.reason === "revocation_persistence_failed")
116
+ throw new WebBotAuthUnavailableError("revocation_persistence_failed", error);
117
+ throw error;
118
+ }
119
+ }
120
+ async currentStatus(originUrl, keyid) {
65
121
  const origin = canonicalAgentOrigin(originUrl), now = this.now();
66
- if (await this.readStored(origin, keyid)) return true;
122
+ if (await this.readStored(origin, keyid)) return "revoked";
123
+ const settled = async () => {
124
+ if (await this.readStored(origin, keyid)) return "revoked";
125
+ if ((this.absentUntil.get(origin) ?? 0) > now) return "absent";
126
+ return (this.freshness.get(origin) ?? 0) > now || (this.graceUntil.get(origin) ?? 0) > now ? "current" : "unknown";
127
+ };
67
128
  if ((this.freshness.get(origin) ?? 0) <= now) {
68
129
  if ((this.failures.get(origin) ?? 0) > now) {
69
130
  if (this.persistenceFailures.has(origin)) throw new WebBotAuthError(403, "revocation_persistence_failed");
70
- if ((this.graceUntil.get(origin) ?? 0) > now) return false;
71
- throw new WebBotAuthError(403, "revocation_unavailable");
131
+ return settled();
72
132
  }
73
133
  let promise = this.pending.get(origin);
74
134
  if (!promise) {
75
- if (this.pending.size >= 16 || [...this.pending.keys()].filter((pendingOrigin) => eTldPlusOne(pendingOrigin) === eTldPlusOne(origin)).length >= 2) {
76
- throw new WebBotAuthError(403, "revocation_unavailable");
135
+ if (this.pending.size >= 16 || [...this.pending.keys()].filter((pendingOrigin) => operatorOf(pendingOrigin) === operatorOf(origin)).length >= 2) {
136
+ return settled();
77
137
  }
78
138
  promise = this.refresh(origin).catch((error) => {
79
139
  if (this.failures.size >= (this.options.maxOrigins ?? 1e3)) {
80
140
  const oldest = this.failures.keys().next().value;
81
141
  this.failures.delete(oldest);
82
142
  this.persistenceFailures.delete(oldest);
143
+ this.absentUntil.delete(oldest);
83
144
  }
84
145
  this.failures.set(origin, now + 60);
85
146
  if (error instanceof WebBotAuthError && error.reason === "revocation_persistence_failed") {
86
147
  this.persistenceFailures.add(origin);
87
148
  this.graceUntil.delete(origin);
88
149
  }
150
+ if (error.cause instanceof RevocationListAbsentError) {
151
+ this.absentUntil.set(origin, now + 60);
152
+ this.graceUntil.delete(origin);
153
+ }
89
154
  throw error;
90
155
  });
91
156
  this.pending.set(origin, promise);
@@ -94,12 +159,11 @@ class OriginRevocationResolver {
94
159
  await promise;
95
160
  } catch (error) {
96
161
  if (error instanceof WebBotAuthError && error.reason === "revocation_persistence_failed") throw error;
97
- if ((this.graceUntil.get(origin) ?? 0) <= now) throw error;
98
162
  } finally {
99
163
  if (this.pending.get(origin) === promise) this.pending.delete(origin);
100
164
  }
101
165
  }
102
- return !!await this.readStored(origin, keyid);
166
+ return settled();
103
167
  }
104
168
  async readStored(origin, keyid) {
105
169
  try {
@@ -117,6 +181,7 @@ class OriginRevocationResolver {
117
181
  maxBodyBytes: 256 * 1024,
118
182
  timeoutMs: 1e4
119
183
  });
184
+ if (response.status === 404 || response.status === 410) throw new RevocationListAbsentError();
120
185
  if (response.status !== 200) throw new Error("Unavailable revocation list");
121
186
  const body = new TextDecoder("utf-8", { fatal: true }).decode(response.body);
122
187
  let tokens;
@@ -142,9 +207,9 @@ class OriginRevocationResolver {
142
207
  if (header.typ !== "adcp-gov-revocation+jws" || typeof header.kid !== "string" || typeof header.alg !== "string" || !["EdDSA", "ES256"].includes(header.alg))
143
208
  continue;
144
209
  await this.options.directory.resolve(header.kid, origin);
145
- const proof = this.options.directory.getAuditProof(origin);
146
- if (!proof || new URL(proof.request.url).origin !== origin) continue;
147
- const checked = verifySignatureDirectory(proof, this.now(), this.options.directory.allowsTestKeys);
210
+ const checked = this.options.directory.getVerifiedDirectory(origin);
211
+ if (!checked || new URL(checked.proof.request.url).origin !== origin || checked.expires <= this.now())
212
+ continue;
148
213
  const jwk = checked.keys.find((key) => key.kid === header.kid);
149
214
  if (!jwk) continue;
150
215
  const result = await compactVerify(token, await importJWK(jwk, header.alg), {
@@ -161,7 +226,8 @@ class OriginRevocationResolver {
161
226
  32
162
227
  );
163
228
  const updated = Date.parse(snapshot.updated) / 1e3, next = Date.parse(snapshot.next_update) / 1e3, now = this.now();
164
- 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))
229
+ if (snapshot.issuer !== origin || !Number.isFinite(updated) || !Number.isFinite(next) || // Tolerate a publisher clock slightly ahead, as for request `created`.
230
+ 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))
165
231
  throw new Error("Invalid revocation snapshot");
166
232
  for (const [kid, detail] of Object.entries(snapshot.revocation_details ?? {}))
167
233
  if (!snapshot.revoked_kids.includes(kid) || !detail || detail.reason && !REASONS.includes(detail.reason) || detail.invalidity_date && !Number.isFinite(Date.parse(detail.invalidity_date)))
@@ -179,11 +245,15 @@ class OriginRevocationResolver {
179
245
  this.updated.delete(oldest);
180
246
  this.graceUntil.delete(oldest);
181
247
  }
182
- this.graceUntil.set(origin, next + 4 * (this.options.pollingIntervalSeconds ?? 300));
248
+ this.graceUntil.set(
249
+ origin,
250
+ Math.min(next, now + MAX_POLLING_SECONDS) + 4 * (this.options.pollingIntervalSeconds ?? 300)
251
+ );
183
252
  this.updated.set(origin, updated);
184
253
  this.freshness.set(origin, Math.min(next, now + (this.options.pollingIntervalSeconds ?? 300)));
185
254
  this.failures.delete(origin);
186
255
  this.persistenceFailures.delete(origin);
256
+ this.absentUntil.delete(origin);
187
257
  } catch (cause) {
188
258
  if (cause instanceof WebBotAuthError && cause.reason === "revocation_persistence_failed") throw cause;
189
259
  const error = new WebBotAuthError(403, "revocation_unavailable");
@@ -1,3 +1,4 @@
1
+ import type { OriginRevocationStatus } from './revocation.mjs';
1
2
  import type { AuthPrincipal } from '../server/auth.mjs';
2
3
  import type { HandlerContext, ProtocolHandlers } from '../server/create-adcp-server.mjs';
3
4
  import { type VerifiedWebBotSigner } from '../signing/web-bot-auth.mjs';
@@ -10,6 +11,17 @@ export declare function createIdentity33PrincipalResolver(options: {
10
11
  lifecycle: OriginIdentityLifecycle;
11
12
  anonymousPrincipal?: string;
12
13
  anonymousTools?: readonly string[];
14
+ /**
15
+ * Origin revocation, checked here (the identity layer), not during signature
16
+ * verification. A revoked key is refused without a challenge; unknown
17
+ * status leaves the identity unverified; an origin serving no list stays
18
+ * identity-established and cannot be bound or spend. Usually an
19
+ * `OriginRevocationResolver`. Without it, the server must supply the
20
+ * deprecated `webBotAuth.isRevoked`.
21
+ */
22
+ revocation?: {
23
+ status(origin: string, keyid: string): Promise<OriginRevocationStatus>;
24
+ };
13
25
  }): (signers: readonly VerifiedWebBotSigner[], operation: ResolvedOperation) => Promise<AuthPrincipal>;
14
26
  /** Pass to createAdcpServer.identity33.authorize; framework runs it before its replay cache. */
15
27
  export declare function createIdentity33Authorization<TAccount>(options: {
@@ -1,3 +1,4 @@
1
+ import type { OriginRevocationStatus } from './revocation';
1
2
  import type { AuthPrincipal } from '../server/auth';
2
3
  import type { HandlerContext, ProtocolHandlers } from '../server/create-adcp-server';
3
4
  import { type VerifiedWebBotSigner } from '../signing/web-bot-auth';
@@ -10,6 +11,17 @@ export declare function createIdentity33PrincipalResolver(options: {
10
11
  lifecycle: OriginIdentityLifecycle;
11
12
  anonymousPrincipal?: string;
12
13
  anonymousTools?: readonly string[];
14
+ /**
15
+ * Origin revocation, checked here (the identity layer), not during signature
16
+ * verification. A revoked key is refused without a challenge; unknown
17
+ * status leaves the identity unverified; an origin serving no list stays
18
+ * identity-established and cannot be bound or spend. Usually an
19
+ * `OriginRevocationResolver`. Without it, the server must supply the
20
+ * deprecated `webBotAuth.isRevoked`.
21
+ */
22
+ revocation?: {
23
+ status(origin: string, keyid: string): Promise<OriginRevocationStatus>;
24
+ };
13
25
  }): (signers: readonly VerifiedWebBotSigner[], operation: ResolvedOperation) => Promise<AuthPrincipal>;
14
26
  /** Pass to createAdcpServer.identity33.authorize; framework runs it before its replay cache. */
15
27
  export declare function createIdentity33Authorization<TAccount>(options: {
@@ -50,6 +50,9 @@ function claimFromContext(ctx) {
50
50
  const claim = ctx.authInfo?.extra?.identity33;
51
51
  return claim && (0, import_web_bot_auth3.isVerifiedWebBotSigner)(claim.signer) ? claim : void 0;
52
52
  }
53
+ function anonymousPrincipalFor(origin, prefix = "anonymous") {
54
+ return `${prefix}:${(0, import_crypto.createHash)("sha256").update(origin).digest("hex")}`;
55
+ }
53
56
  function isIdentity33UnboundContext(ctx) {
54
57
  return claimFromContext(ctx)?.state === "identity-established";
55
58
  }
@@ -67,17 +70,44 @@ function createIdentity33PrincipalResolver(options) {
67
70
  if (!signers.length || signers.some((s) => !(0, import_web_bot_auth3.isVerifiedWebBotSigner)(s)))
68
71
  throw new import_web_bot_auth3.WebBotAuthError(403, "unverified_identity");
69
72
  const signer = signers[signers.length - 1];
73
+ let revocation;
74
+ if (options.revocation) {
75
+ revocation = "current";
76
+ for (const each of signers) {
77
+ let status;
78
+ try {
79
+ status = await options.revocation.status(each.origin, each.keyid);
80
+ } catch (error) {
81
+ throw new import_web_bot_auth.WebBotAuthUnavailableError("revocation_persistence_failed", error);
82
+ }
83
+ if (status === "revoked") throw new import_web_bot_auth3.WebBotAuthError(403, "key_revoked");
84
+ if (status === "unknown") throw new import_web_bot_auth3.WebBotAuthError(403, "identity_unverified");
85
+ if (status === "absent") revocation = "absent";
86
+ }
87
+ }
70
88
  const revoked = signers.find((s) => s.authorizationFailure);
71
- const { binding } = revoked ? { binding: void 0 } : await options.lifecycle.getPrincipal(signer.origin);
89
+ let { binding } = revoked || revocation === "absent" ? { binding: void 0 } : await options.lifecycle.getPrincipal(signer.origin);
90
+ if (binding) {
91
+ try {
92
+ await options.lifecycle.authorizeBound(signer.origin, { readOnly: true });
93
+ } catch (error) {
94
+ if (error instanceof import_trust.TrustMembershipError) binding = void 0;
95
+ else if (error instanceof import_trust.TrustRecordUnavailableError)
96
+ throw new import_web_bot_auth.WebBotAuthUnavailableError("trust_record_unavailable", error);
97
+ else throw error;
98
+ }
99
+ }
72
100
  if (!binding && operation !== void 0 && typeof operation !== "string")
73
101
  throw new import_web_bot_auth3.WebBotAuthError(400, "operation");
74
102
  const authorizationFailure = revoked?.authorizationFailure ?? (!binding && typeof operation === "string" && !anonymousTools.has(operation) ? (0, import_web_bot_auth2.createWebBotAuthAuthorizationFailure)("identity_not_bound") : void 0);
75
103
  return {
76
- principal: binding?.principalId ?? `${options.anonymousPrincipal ?? "anonymous"}:${(0, import_crypto.createHash)("sha256").update(signer.origin).digest("hex")}`,
104
+ principal: binding?.principalId ?? anonymousPrincipalFor(signer.origin, options.anonymousPrincipal),
77
105
  extra: {
78
106
  identity33: {
79
107
  state: binding ? "bound" : "identity-established",
80
108
  signer: revoked ?? signer,
109
+ ...revocation && { revocation },
110
+ anonymousPrincipal: anonymousPrincipalFor(signer.origin, options.anonymousPrincipal),
81
111
  ...authorizationFailure && { authorizationFailure }
82
112
  }
83
113
  },
@@ -91,6 +121,7 @@ function createIdentity33PrincipalResolver(options) {
91
121
  } : {}
92
122
  };
93
123
  };
124
+ if (options.revocation) (0, import_web_bot_auth.registerWebBotAuthRevocationCheck)(resolver);
94
125
  (0, import_web_bot_auth.registerWebBotAuthLegacyOriginGate)(resolver, async (origin) => {
95
126
  if ((await options.lifecycle.getPrincipal(origin)).binding)
96
127
  throw new import_errors.RequestSignatureError(
@@ -102,6 +133,37 @@ function createIdentity33PrincipalResolver(options) {
102
133
  return resolver;
103
134
  }
104
135
  function createIdentity33Authorization(options) {
136
+ const anonymousTools = options.anonymousTools ?? [
137
+ "get_principal",
138
+ "get_adcp_capabilities",
139
+ "list_creative_formats",
140
+ "get_products"
141
+ ];
142
+ const authorizeUnbound = async (ctx, identity, tool, params) => {
143
+ if (tool === "sync_principal") {
144
+ if (identity.revocation === "absent")
145
+ throw new import_async_outcome.AdcpError("PERMISSION_DENIED", { message: "Agent identity cannot be onboarded" });
146
+ const dryRun = !!params && typeof params === "object" && params.dry_run === true;
147
+ const binding = await options.lifecycle.syncOpen(identity.signer.origin, { dryRun });
148
+ if (dryRun) return;
149
+ if (!ctx.authInfo) throw new import_trust.IdentityAuthorizationError();
150
+ ctx.authInfo.clientId = binding.principalId;
151
+ ctx.authInfo.credential = (0, import_buyer_agent.markVerifiedHttpSig)({
152
+ kind: "http_sig",
153
+ keyid: identity.signer.keyid,
154
+ agent_url: identity.signer.origin,
155
+ verified_at: identity.signer.verified_at
156
+ });
157
+ ctx.authInfo.extra = {
158
+ ...ctx.authInfo.extra,
159
+ credential: ctx.authInfo.credential,
160
+ identity33: { ...identity, state: "bound" }
161
+ };
162
+ return;
163
+ }
164
+ if (anonymousTools.includes(tool) && !needsSpendAuthorization(tool, params)) return;
165
+ throw new import_async_outcome.AdcpError("PERMISSION_DENIED", { message: "Agent identity is not onboarded for this operation" });
166
+ };
105
167
  return async (ctx, tool, params) => {
106
168
  const identity = claimFromContext(ctx);
107
169
  const failure = (0, import_web_bot_auth2.getWebBotAuthAuthorizationFailure)(ctx.authInfo?.extra);
@@ -114,48 +176,43 @@ function createIdentity33Authorization(options) {
114
176
  });
115
177
  return;
116
178
  }
117
- if (identity.state === "identity-established") {
118
- if (tool === "sync_principal") {
119
- const dryRun = !!params && typeof params === "object" && params.dry_run === true;
120
- const binding = await options.lifecycle.syncOpen(identity.signer.origin, { dryRun });
121
- if (dryRun) return;
122
- if (!ctx.authInfo) throw new import_trust.IdentityAuthorizationError();
123
- ctx.authInfo.clientId = binding.principalId;
124
- ctx.authInfo.credential = (0, import_buyer_agent.markVerifiedHttpSig)({
125
- kind: "http_sig",
126
- keyid: identity.signer.keyid,
127
- agent_url: identity.signer.origin,
128
- verified_at: identity.signer.verified_at
129
- });
130
- ctx.authInfo.extra = {
131
- ...ctx.authInfo.extra,
132
- credential: ctx.authInfo.credential,
133
- identity33: { ...identity, state: "bound" }
134
- };
179
+ if (identity.state === "identity-established") return authorizeUnbound(ctx, identity, tool, params);
180
+ const origin = identity.signer.origin;
181
+ const spend = !IDENTITY_ADMIN_TOOLS.has(tool) && needsSpendAuthorization(tool, params) && !(!KNOWN_TOOLS.has(tool) && options.readOnlyTools?.includes(tool));
182
+ try {
183
+ if (IDENTITY_ADMIN_TOOLS.has(tool)) {
184
+ await options.lifecycle.authorizeBound(origin, { identityConfiguration: true });
135
185
  return;
136
186
  }
137
- if ((options.anonymousTools ?? ["get_principal", "get_adcp_capabilities", "list_creative_formats", "get_products"]).includes(tool) && !needsSpendAuthorization(tool, params))
187
+ if (!spend) {
188
+ await options.lifecycle.authorizeBound(origin, { readOnly: true });
138
189
  return;
139
- throw new import_async_outcome.AdcpError("PERMISSION_DENIED", { message: "Agent identity is not onboarded for this operation" });
140
- }
141
- const origin = identity.signer.origin;
142
- if (IDENTITY_ADMIN_TOOLS.has(tool)) {
143
- await options.lifecycle.authorizeBound(origin, { identityConfiguration: true });
144
- return;
145
- }
146
- if (!needsSpendAuthorization(tool, params) || !KNOWN_TOOLS.has(tool) && options.readOnlyTools?.includes(tool)) {
147
- await options.lifecycle.authorizeBound(origin, { readOnly: true });
148
- return;
149
- }
150
- const claims = await options.resolveClaims(ctx, tool, params);
151
- if (!claims.length) throw new import_trust.IdentityAuthorizationError();
152
- for (const claim of claims) {
153
- await options.lifecycle.authorizeBound(origin, {
154
- sandbox: claim.sandbox,
155
- billing: claim.billing,
156
- brandDomain: claim.brand.domain
157
- });
158
- await (0, import_trust.authorizeDemandGrant)({ ...claim, signerOrigin: origin }, options.grants);
190
+ }
191
+ const claims = await options.resolveClaims(ctx, tool, params);
192
+ if (!claims.length) throw new import_trust.IdentityAuthorizationError();
193
+ for (const claim of claims) {
194
+ await options.lifecycle.authorizeBound(origin, {
195
+ sandbox: claim.sandbox,
196
+ billing: claim.billing,
197
+ brandDomain: claim.brand.domain
198
+ });
199
+ await (0, import_trust.authorizeDemandGrant)({ ...claim, signerOrigin: origin }, options.grants);
200
+ }
201
+ } catch (error) {
202
+ if (error instanceof import_trust.TrustRecordUnavailableError)
203
+ throw new import_async_outcome.AdcpError("SERVICE_UNAVAILABLE", { message: error.message, recovery: "transient" });
204
+ if (error instanceof import_trust.TrustMembershipError && !spend) {
205
+ const demoted = { ...identity, state: "identity-established" };
206
+ if (ctx.authInfo) {
207
+ const { credential: _credential, ...extra } = ctx.authInfo.extra ?? {};
208
+ ctx.authInfo.clientId = identity.anonymousPrincipal ?? anonymousPrincipalFor(origin);
209
+ delete ctx.authInfo.credential;
210
+ ctx.authInfo.extra = { ...extra, identity33: demoted };
211
+ }
212
+ delete ctx.agent;
213
+ return authorizeUnbound(ctx, demoted, tool, params);
214
+ }
215
+ throw error;
159
216
  }
160
217
  };
161
218
  }
@@ -176,7 +233,10 @@ function withIdentity33PrincipalHandlers(base, lifecycle) {
176
233
  return base.getPrincipal(params, ctx);
177
234
  }
178
235
  const read = await lifecycle.getPrincipal(identity.signer.origin);
179
- if (!read.binding) return { result: read.result };
236
+ if (!read.binding || identity.state === "identity-established")
237
+ return {
238
+ result: { kind: "unconfigured", ...read.result.relationship && { relationship: read.result.relationship } }
239
+ };
180
240
  if (!base.getPrincipal) return { result: read.result };
181
241
  const response = await base.getPrincipal(params, ctx);
182
242
  if (read.result.relationship && response && typeof response === "object") {
@@ -200,7 +260,7 @@ function createIdentity33PrincipalScopeResolver(options) {
200
260
  return options.legacy(ctx);
201
261
  }
202
262
  const { binding } = await options.lifecycle.getPrincipal(identity.signer.origin);
203
- if (!binding) throw new import_trust.IdentityAuthorizationError();
263
+ if (!binding || identity.state !== "bound") throw new import_trust.IdentityAuthorizationError();
204
264
  return {
205
265
  tenant_id: options.tenantId,
206
266
  principal_id: binding.principalId,