@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.
Files changed (185) hide show
  1. package/dist/lib/adapters/implicit-account-store.d.mts +12 -7
  2. package/dist/lib/adapters/implicit-account-store.d.ts +12 -7
  3. package/dist/lib/adapters/implicit-account-store.js +69 -15
  4. package/dist/lib/adapters/implicit-account-store.mjs +69 -15
  5. package/dist/lib/core/AgentClient.d.mts +1 -0
  6. package/dist/lib/core/AgentClient.d.ts +1 -0
  7. package/dist/lib/core/AgentClient.js +3 -0
  8. package/dist/lib/core/AgentClient.mjs +3 -0
  9. package/dist/lib/core/SingleAgentClient.d.mts +31 -1
  10. package/dist/lib/core/SingleAgentClient.d.ts +31 -1
  11. package/dist/lib/core/SingleAgentClient.js +355 -35
  12. package/dist/lib/core/SingleAgentClient.mjs +365 -37
  13. package/dist/lib/core/TaskExecutor.js +2 -1
  14. package/dist/lib/core/TaskExecutor.mjs +2 -1
  15. package/dist/lib/core/account-key.d.mts +3 -0
  16. package/dist/lib/core/account-key.d.ts +3 -0
  17. package/dist/lib/core/account-key.js +41 -0
  18. package/dist/lib/core/account-key.mjs +17 -0
  19. package/dist/lib/core/account-resolution.d.mts +2 -0
  20. package/dist/lib/core/account-resolution.d.ts +2 -0
  21. package/dist/lib/core/buyer-account-registry.d.mts +65 -0
  22. package/dist/lib/core/buyer-account-registry.d.ts +65 -0
  23. package/dist/lib/core/buyer-account-registry.js +518 -0
  24. package/dist/lib/core/buyer-account-registry.mjs +494 -0
  25. package/dist/lib/core/product-cache.d.mts +18 -0
  26. package/dist/lib/core/product-cache.d.ts +18 -0
  27. package/dist/lib/core/product-cache.js +137 -0
  28. package/dist/lib/core/product-cache.mjs +112 -0
  29. package/dist/lib/errors/index.d.mts +40 -1
  30. package/dist/lib/errors/index.d.ts +40 -1
  31. package/dist/lib/errors/index.js +69 -3
  32. package/dist/lib/errors/index.mjs +64 -3
  33. package/dist/lib/governance/authorization.d.mts +17 -1
  34. package/dist/lib/governance/authorization.d.ts +17 -1
  35. package/dist/lib/governance/authorization.js +55 -7
  36. package/dist/lib/governance/authorization.mjs +59 -7
  37. package/dist/lib/governance/index.d.mts +2 -2
  38. package/dist/lib/governance/index.d.ts +2 -2
  39. package/dist/lib/governance/index.js +2 -0
  40. package/dist/lib/governance/index.mjs +3 -1
  41. package/dist/lib/index.d.mts +5 -3
  42. package/dist/lib/index.d.ts +5 -3
  43. package/dist/lib/index.js +20 -0
  44. package/dist/lib/index.mjs +19 -0
  45. package/dist/lib/protocols/a2a.js +9 -1
  46. package/dist/lib/protocols/a2a.mjs +9 -1
  47. package/dist/lib/protocols/index.js +9 -2
  48. package/dist/lib/protocols/index.mjs +9 -2
  49. package/dist/lib/protocols/mcp-modern.js +2 -1
  50. package/dist/lib/protocols/mcp-modern.mjs +2 -1
  51. package/dist/lib/protocols/mcp.js +5 -2
  52. package/dist/lib/protocols/mcp.mjs +5 -2
  53. package/dist/lib/protocols/rawResponseCapture.d.mts +6 -0
  54. package/dist/lib/protocols/rawResponseCapture.d.ts +6 -0
  55. package/dist/lib/protocols/rawResponseCapture.js +41 -29
  56. package/dist/lib/protocols/rawResponseCapture.mjs +40 -29
  57. package/dist/lib/protocols/signedRequestRejection.d.mts +9 -0
  58. package/dist/lib/protocols/signedRequestRejection.d.ts +9 -0
  59. package/dist/lib/protocols/signedRequestRejection.js +209 -0
  60. package/dist/lib/protocols/signedRequestRejection.mjs +189 -0
  61. package/dist/lib/protocols/transportDiagnostics.d.mts +1 -0
  62. package/dist/lib/protocols/transportDiagnostics.d.ts +1 -0
  63. package/dist/lib/protocols/transportDiagnostics.js +2 -0
  64. package/dist/lib/protocols/transportDiagnostics.mjs +1 -0
  65. package/dist/lib/registry/types.generated.d.mts +112 -45
  66. package/dist/lib/registry/types.generated.d.ts +112 -45
  67. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  68. package/dist/lib/server/account-provisioning.d.mts +2 -0
  69. package/dist/lib/server/account-provisioning.d.ts +2 -0
  70. package/dist/lib/server/account-provisioning.js +30 -0
  71. package/dist/lib/server/account-provisioning.mjs +6 -0
  72. package/dist/lib/server/account-reference-warnings.d.mts +12 -0
  73. package/dist/lib/server/account-reference-warnings.d.ts +12 -0
  74. package/dist/lib/server/account-reference-warnings.js +48 -0
  75. package/dist/lib/server/account-reference-warnings.mjs +23 -0
  76. package/dist/lib/server/auth-signature.js +1 -0
  77. package/dist/lib/server/auth-signature.mjs +1 -0
  78. package/dist/lib/server/create-adcp-server.d.mts +34 -0
  79. package/dist/lib/server/create-adcp-server.d.ts +34 -0
  80. package/dist/lib/server/create-adcp-server.js +225 -14
  81. package/dist/lib/server/create-adcp-server.mjs +225 -14
  82. package/dist/lib/server/decisioning/account.d.mts +2 -0
  83. package/dist/lib/server/decisioning/account.d.ts +2 -0
  84. package/dist/lib/server/decisioning/runtime/from-platform.js +49 -10
  85. package/dist/lib/server/decisioning/runtime/from-platform.mjs +49 -10
  86. package/dist/lib/server/index.d.mts +2 -2
  87. package/dist/lib/server/index.d.ts +2 -2
  88. package/dist/lib/server/index.js +2 -0
  89. package/dist/lib/server/index.mjs +3 -1
  90. package/dist/lib/signing/agent-resolver/consistency.d.mts +6 -13
  91. package/dist/lib/signing/agent-resolver/consistency.d.ts +6 -13
  92. package/dist/lib/signing/agent-resolver/consistency.js +0 -1
  93. package/dist/lib/signing/agent-resolver/consistency.mjs +0 -1
  94. package/dist/lib/signing/agent-resolver/errors.d.mts +1 -1
  95. package/dist/lib/signing/agent-resolver/errors.d.ts +1 -1
  96. package/dist/lib/signing/agent-resolver/fetch-helpers.d.mts +2 -0
  97. package/dist/lib/signing/agent-resolver/fetch-helpers.d.ts +2 -0
  98. package/dist/lib/signing/agent-resolver/fetch-helpers.js +2 -1
  99. package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +2 -1
  100. package/dist/lib/signing/agent-resolver/jwks-set.js +24 -3
  101. package/dist/lib/signing/agent-resolver/jwks-set.mjs +24 -3
  102. package/dist/lib/signing/agent-resolver/legacy-brand.d.mts +15 -0
  103. package/dist/lib/signing/agent-resolver/legacy-brand.d.ts +15 -0
  104. package/dist/lib/signing/agent-resolver/legacy-brand.js +60 -0
  105. package/dist/lib/signing/agent-resolver/legacy-brand.mjs +36 -0
  106. package/dist/lib/signing/agent-resolver/operator-authorization.d.mts +13 -0
  107. package/dist/lib/signing/agent-resolver/operator-authorization.d.ts +13 -0
  108. package/dist/lib/signing/agent-resolver/operator-authorization.js +108 -0
  109. package/dist/lib/signing/agent-resolver/operator-authorization.mjs +84 -0
  110. package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +14 -4
  111. package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +14 -4
  112. package/dist/lib/signing/agent-resolver/resolve-agent.js +101 -132
  113. package/dist/lib/signing/agent-resolver/resolve-agent.mjs +102 -133
  114. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +7 -1
  115. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +7 -1
  116. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +50 -17
  117. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +50 -17
  118. package/dist/lib/signing/agent-resolver/select-agent.d.mts +14 -15
  119. package/dist/lib/signing/agent-resolver/select-agent.d.ts +14 -15
  120. package/dist/lib/signing/agent-resolver/select-agent.js +98 -13
  121. package/dist/lib/signing/agent-resolver/select-agent.mjs +95 -13
  122. package/dist/lib/signing/brand-jwks.d.mts +27 -75
  123. package/dist/lib/signing/brand-jwks.d.ts +27 -75
  124. package/dist/lib/signing/brand-jwks.js +112 -182
  125. package/dist/lib/signing/brand-jwks.mjs +112 -182
  126. package/dist/lib/signing/errors.d.mts +3 -1
  127. package/dist/lib/signing/errors.d.ts +3 -1
  128. package/dist/lib/signing/errors.js +4 -1
  129. package/dist/lib/signing/errors.mjs +4 -1
  130. package/dist/lib/signing/jwks-https.d.mts +7 -0
  131. package/dist/lib/signing/jwks-https.d.ts +7 -0
  132. package/dist/lib/signing/jwks-https.js +31 -8
  133. package/dist/lib/signing/jwks-https.mjs +31 -8
  134. package/dist/lib/signing/jwks.d.mts +8 -0
  135. package/dist/lib/signing/jwks.d.ts +8 -0
  136. package/dist/lib/signing/middleware.js +2 -1
  137. package/dist/lib/signing/middleware.mjs +2 -1
  138. package/dist/lib/signing/publisher-pins.d.mts +11 -0
  139. package/dist/lib/signing/publisher-pins.d.ts +11 -0
  140. package/dist/lib/signing/publisher-pins.js +125 -0
  141. package/dist/lib/signing/publisher-pins.mjs +101 -0
  142. package/dist/lib/signing/server.d.mts +1 -0
  143. package/dist/lib/signing/server.d.ts +1 -0
  144. package/dist/lib/signing/types.d.mts +5 -0
  145. package/dist/lib/signing/types.d.ts +5 -0
  146. package/dist/lib/signing/verifier.js +49 -4
  147. package/dist/lib/signing/verifier.mjs +49 -4
  148. package/dist/lib/signing/webhook-verifier.d.mts +7 -2
  149. package/dist/lib/signing/webhook-verifier.d.ts +7 -2
  150. package/dist/lib/signing/webhook-verifier.js +42 -2
  151. package/dist/lib/signing/webhook-verifier.mjs +43 -3
  152. package/dist/lib/testing/storyboard/account-policy.d.mts +2 -0
  153. package/dist/lib/testing/storyboard/account-policy.d.ts +2 -0
  154. package/dist/lib/testing/storyboard/account-policy.js +35 -0
  155. package/dist/lib/testing/storyboard/account-policy.mjs +11 -0
  156. package/dist/lib/testing/storyboard/context.js +6 -0
  157. package/dist/lib/testing/storyboard/context.mjs +6 -0
  158. package/dist/lib/testing/storyboard/request-builder.js +12 -2
  159. package/dist/lib/testing/storyboard/request-builder.mjs +12 -2
  160. package/dist/lib/testing/storyboard/runner.js +3 -2
  161. package/dist/lib/testing/storyboard/runner.mjs +3 -2
  162. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  163. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  164. package/dist/lib/version.d.mts +3 -3
  165. package/dist/lib/version.d.ts +3 -3
  166. package/dist/lib/version.js +3 -3
  167. package/dist/lib/version.mjs +3 -3
  168. package/dist/lib/wholesale-feed-sync/sync.d.mts +1 -0
  169. package/dist/lib/wholesale-feed-sync/sync.d.ts +1 -0
  170. package/dist/lib/wholesale-feed-sync/sync.js +92 -21
  171. package/dist/lib/wholesale-feed-sync/sync.mjs +92 -21
  172. package/dist/lib/wholesale-feed-sync/types.d.mts +4 -3
  173. package/dist/lib/wholesale-feed-sync/types.d.ts +4 -3
  174. package/docs/TYPE-SUMMARY.md +2 -2
  175. package/docs/guides/BUILD-AN-AGENT.md +2 -2
  176. package/docs/guides/BUYER-QUICKSTART-3.2.md +2 -0
  177. package/docs/guides/FIRST-CALL-TO-A-SELLER.md +104 -0
  178. package/docs/guides/SIGNING-GUIDE.md +16 -7
  179. package/docs/guides/account-resolution.md +86 -0
  180. package/docs/llms.txt +3 -2
  181. package/docs/migration-14.x-rc-worksheet.md +4 -4
  182. package/docs/migration-4.x-to-5.x.md +1 -0
  183. package/docs/migration-agent-resolution-3.3.md +123 -0
  184. package/docs/recipes/verifying-inbound-webhooks.md +56 -15
  185. package/package.json +2 -2
@@ -83,6 +83,8 @@ export interface ImplicitAccountStoreOptions<TCtxMeta = Record<string, unknown>>
83
83
  * itself is valid before a fresh `sync_accounts` is required.
84
84
  */
85
85
  ttlMs?: number;
86
+ /** Additive sync batches. Default false preserves historical replace-as-revocation behavior. */
87
+ mergeOnUpsert?: boolean;
86
88
  }
87
89
  /**
88
90
  * In-memory `AccountStore` for `resolution: 'implicit'` platforms.
@@ -125,8 +127,10 @@ export interface ImplicitAccountStoreOptions<TCtxMeta = Record<string, unknown>>
125
127
  export declare class InMemoryImplicitAccountStore<TCtxMeta = Record<string, unknown>> implements AccountStore<TCtxMeta> {
126
128
  readonly resolution: "implicit";
127
129
  private _store;
130
+ private _writes;
128
131
  private _keyFn;
129
132
  private _ttlMs;
133
+ private _mergeOnUpsert;
130
134
  private _buildAccount;
131
135
  constructor(options?: ImplicitAccountStoreOptions<TCtxMeta>);
132
136
  /**
@@ -139,14 +143,10 @@ export declare class InMemoryImplicitAccountStore<TCtxMeta = Record<string, unkn
139
143
  * - The stored entry has exceeded `ttlMs`
140
144
  * - `ctx.authInfo` is absent or carries no extractable key
141
145
  *
142
- * **Multi-account note.** When a buyer synced multiple refs in one
143
- * `sync_accounts` call, this implementation returns the _first_ stored
144
- * account. If your platform requires per-request account disambiguation
145
- * (e.g., different brands on the same buyer), switch to `'explicit'` mode
146
- * so buyers pass `ext.account_ref` on each request, or override `keyFn`
147
- * to encode the brand into the key.
146
+ * Supplied references match the complete stored natural key. An omitted
147
+ * reference retains the historical first-account selection.
148
148
  */
149
- resolve(_ref: AccountReference | undefined, ctx?: ResolveContext): Promise<Account<TCtxMeta> | null>;
149
+ resolve(ref: AccountReference | undefined, ctx?: ResolveContext): Promise<Account<TCtxMeta> | null>;
150
150
  /**
151
151
  * Process a `sync_accounts` payload: build accounts from refs and store
152
152
  * them under the caller's auth key.
@@ -163,6 +163,11 @@ export declare class InMemoryImplicitAccountStore<TCtxMeta = Record<string, unkn
163
163
  * reaching this method.
164
164
  */
165
165
  upsert(refs: AccountReference[], ctx?: ResolveContext): Promise<SyncAccountsResultRow[]>;
166
+ private withPrincipalLock;
167
+ private upsertUnlocked;
168
+ /** Revoke a single reference for this principal without clearing other brands. */
169
+ remove(ref: AccountReference, ctx?: ResolveContext): Promise<void>;
170
+ private removeUnlocked;
166
171
  /** Remove all stored sync linkages. */
167
172
  clear(): void;
168
173
  /**
@@ -83,6 +83,8 @@ export interface ImplicitAccountStoreOptions<TCtxMeta = Record<string, unknown>>
83
83
  * itself is valid before a fresh `sync_accounts` is required.
84
84
  */
85
85
  ttlMs?: number;
86
+ /** Additive sync batches. Default false preserves historical replace-as-revocation behavior. */
87
+ mergeOnUpsert?: boolean;
86
88
  }
87
89
  /**
88
90
  * In-memory `AccountStore` for `resolution: 'implicit'` platforms.
@@ -125,8 +127,10 @@ export interface ImplicitAccountStoreOptions<TCtxMeta = Record<string, unknown>>
125
127
  export declare class InMemoryImplicitAccountStore<TCtxMeta = Record<string, unknown>> implements AccountStore<TCtxMeta> {
126
128
  readonly resolution: "implicit";
127
129
  private _store;
130
+ private _writes;
128
131
  private _keyFn;
129
132
  private _ttlMs;
133
+ private _mergeOnUpsert;
130
134
  private _buildAccount;
131
135
  constructor(options?: ImplicitAccountStoreOptions<TCtxMeta>);
132
136
  /**
@@ -139,14 +143,10 @@ export declare class InMemoryImplicitAccountStore<TCtxMeta = Record<string, unkn
139
143
  * - The stored entry has exceeded `ttlMs`
140
144
  * - `ctx.authInfo` is absent or carries no extractable key
141
145
  *
142
- * **Multi-account note.** When a buyer synced multiple refs in one
143
- * `sync_accounts` call, this implementation returns the _first_ stored
144
- * account. If your platform requires per-request account disambiguation
145
- * (e.g., different brands on the same buyer), switch to `'explicit'` mode
146
- * so buyers pass `ext.account_ref` on each request, or override `keyFn`
147
- * to encode the brand into the key.
146
+ * Supplied references match the complete stored natural key. An omitted
147
+ * reference retains the historical first-account selection.
148
148
  */
149
- resolve(_ref: AccountReference | undefined, ctx?: ResolveContext): Promise<Account<TCtxMeta> | null>;
149
+ resolve(ref: AccountReference | undefined, ctx?: ResolveContext): Promise<Account<TCtxMeta> | null>;
150
150
  /**
151
151
  * Process a `sync_accounts` payload: build accounts from refs and store
152
152
  * them under the caller's auth key.
@@ -163,6 +163,11 @@ export declare class InMemoryImplicitAccountStore<TCtxMeta = Record<string, unkn
163
163
  * reaching this method.
164
164
  */
165
165
  upsert(refs: AccountReference[], ctx?: ResolveContext): Promise<SyncAccountsResultRow[]>;
166
+ private withPrincipalLock;
167
+ private upsertUnlocked;
168
+ /** Revoke a single reference for this principal without clearing other brands. */
169
+ remove(ref: AccountReference, ctx?: ResolveContext): Promise<void>;
170
+ private removeUnlocked;
166
171
  /** Remove all stored sync linkages. */
167
172
  clear(): void;
168
173
  /**
@@ -22,6 +22,7 @@ __export(implicit_account_store_exports, {
22
22
  defaultImplicitKeyFn: () => defaultImplicitKeyFn
23
23
  });
24
24
  module.exports = __toCommonJS(implicit_account_store_exports);
25
+ var import_account_key = require('../core/account-key.js');
25
26
  function defaultImplicitKeyFn(authInfo) {
26
27
  const cred = authInfo.credential;
27
28
  if (!cred) return void 0;
@@ -35,11 +36,7 @@ function defaultImplicitKeyFn(authInfo) {
35
36
  }
36
37
  }
37
38
  function refNaturalKey(ref) {
38
- const r = ref;
39
- const domain = r["brand"]?.["domain"];
40
- const operator = r["operator"];
41
- const sandbox = Boolean(r["sandbox"]);
42
- return `${domain ?? ""}|${operator ?? ""}|${sandbox ? "1" : "0"}`;
39
+ return (0, import_account_key.accountReferenceKey)(ref);
43
40
  }
44
41
  function defaultBuildAccount(ref, _ctx) {
45
42
  const r = ref;
@@ -55,19 +52,25 @@ function defaultBuildAccount(ref, _ctx) {
55
52
  ...brand !== void 0 && { brand },
56
53
  ...operator !== "" && { operator },
57
54
  ...sandbox && { sandbox: true },
55
+ ..."operator_unit" in ref && { operator_unit: ref.operator_unit },
56
+ ..."currency" in ref && { currency: ref.currency },
57
+ ..."timezone" in ref && { timezone: ref.timezone },
58
58
  ctx_metadata: {}
59
59
  };
60
60
  }
61
61
  class InMemoryImplicitAccountStore {
62
62
  resolution = "implicit";
63
63
  _store = /* @__PURE__ */ new Map();
64
+ _writes = /* @__PURE__ */ new Map();
64
65
  _keyFn;
65
66
  _ttlMs;
67
+ _mergeOnUpsert;
66
68
  _buildAccount;
67
69
  constructor(options) {
68
70
  this._buildAccount = options?.buildAccount ?? defaultBuildAccount;
69
71
  this._keyFn = options?.keyFn ?? defaultImplicitKeyFn;
70
72
  this._ttlMs = options?.ttlMs ?? 864e5;
73
+ this._mergeOnUpsert = options?.mergeOnUpsert ?? false;
71
74
  }
72
75
  /**
73
76
  * Resolve the caller's account from the auth-principal→account mapping
@@ -79,14 +82,10 @@ class InMemoryImplicitAccountStore {
79
82
  * - The stored entry has exceeded `ttlMs`
80
83
  * - `ctx.authInfo` is absent or carries no extractable key
81
84
  *
82
- * **Multi-account note.** When a buyer synced multiple refs in one
83
- * `sync_accounts` call, this implementation returns the _first_ stored
84
- * account. If your platform requires per-request account disambiguation
85
- * (e.g., different brands on the same buyer), switch to `'explicit'` mode
86
- * so buyers pass `ext.account_ref` on each request, or override `keyFn`
87
- * to encode the brand into the key.
85
+ * Supplied references match the complete stored natural key. An omitted
86
+ * reference retains the historical first-account selection.
88
87
  */
89
- async resolve(_ref, ctx) {
88
+ async resolve(ref, ctx) {
90
89
  const authInfo = ctx?.authInfo;
91
90
  if (!authInfo) return null;
92
91
  const key = this._keyFn(authInfo);
@@ -97,7 +96,13 @@ class InMemoryImplicitAccountStore {
97
96
  this._store.delete(key);
98
97
  return null;
99
98
  }
100
- return entry.accounts[0] ?? null;
99
+ if (ref === void 0)
100
+ return entry.accounts.find(
101
+ (_account, index2) => Date.now() - (entry.storedAtByKey.get(refNaturalKey(entry.refs[index2])) ?? entry.storedAt) <= this._ttlMs
102
+ ) ?? null;
103
+ const index = entry.refs.findIndex((stored) => refNaturalKey(stored) === refNaturalKey(ref));
104
+ if (Date.now() - (entry.storedAtByKey.get(refNaturalKey(ref)) ?? entry.storedAt) > this._ttlMs) return null;
105
+ return index < 0 ? null : entry.accounts[index] ?? null;
101
106
  }
102
107
  /**
103
108
  * Process a `sync_accounts` payload: build accounts from refs and store
@@ -115,6 +120,25 @@ class InMemoryImplicitAccountStore {
115
120
  * reaching this method.
116
121
  */
117
122
  async upsert(refs, ctx) {
123
+ const snapshot = structuredClone(refs);
124
+ return this.withPrincipalLock(ctx, () => this.upsertUnlocked(snapshot, ctx));
125
+ }
126
+ withPrincipalLock(ctx, run) {
127
+ const key = ctx?.authInfo ? this._keyFn(ctx.authInfo) : void 0;
128
+ if (key === void 0) return run();
129
+ const next = (this._writes.get(key) ?? Promise.resolve()).then(run);
130
+ const tail = next.then(
131
+ () => {
132
+ },
133
+ () => {
134
+ }
135
+ );
136
+ this._writes.set(key, tail);
137
+ return next.finally(() => {
138
+ if (this._writes.get(key) === tail) this._writes.delete(key);
139
+ });
140
+ }
141
+ async upsertUnlocked(refs, ctx) {
118
142
  const authInfo = ctx?.authInfo;
119
143
  const key = authInfo !== void 0 ? this._keyFn(authInfo) : void 0;
120
144
  if (key === void 0) {
@@ -136,8 +160,10 @@ class InMemoryImplicitAccountStore {
136
160
  }
137
161
  const existing = this._store.get(key);
138
162
  const existingByNk = /* @__PURE__ */ new Map();
139
- if (existing) {
163
+ if (existing && Date.now() - existing.storedAt <= this._ttlMs) {
140
164
  for (let i = 0; i < existing.refs.length; i++) {
165
+ if (Date.now() - (existing.storedAtByKey.get(refNaturalKey(existing.refs[i])) ?? existing.storedAt) > this._ttlMs)
166
+ continue;
141
167
  existingByNk.set(refNaturalKey(existing.refs[i]), {
142
168
  account: existing.accounts[i],
143
169
  ref: existing.refs[i]
@@ -194,13 +220,41 @@ class InMemoryImplicitAccountStore {
194
220
  }
195
221
  }
196
222
  if (newAccounts.length > 0) {
197
- this._store.set(key, { accounts: newAccounts, refs: newRefs, storedAt: Date.now() });
223
+ const storedAt = Date.now();
224
+ const storedAtByKey = new Map(newRefs.map((ref) => [refNaturalKey(ref), storedAt]));
225
+ if (this._mergeOnUpsert && ctx?.input?.delete_missing !== true) {
226
+ const syncedKeys = new Set(newRefs.map(refNaturalKey));
227
+ for (const [nk, hit] of existingByNk) {
228
+ if (!syncedKeys.has(nk)) {
229
+ newAccounts.push(hit.account);
230
+ newRefs.push(hit.ref);
231
+ storedAtByKey.set(nk, existing.storedAtByKey.get(nk) ?? existing.storedAt);
232
+ }
233
+ }
234
+ }
235
+ this._store.set(key, { accounts: newAccounts, refs: newRefs, storedAt, storedAtByKey });
198
236
  }
199
237
  return rows;
200
238
  }
201
239
  // ---------------------------------------------------------------------------
202
240
  // Test helpers
203
241
  // ---------------------------------------------------------------------------
242
+ /** Revoke a single reference for this principal without clearing other brands. */
243
+ async remove(ref, ctx) {
244
+ return this.withPrincipalLock(ctx, () => this.removeUnlocked(ref, ctx));
245
+ }
246
+ async removeUnlocked(ref, ctx) {
247
+ const key = ctx?.authInfo ? this._keyFn(ctx.authInfo) : void 0;
248
+ if (key === void 0) return;
249
+ const entry = this._store.get(key);
250
+ if (!entry) return;
251
+ const index = entry.refs.findIndex((stored) => refNaturalKey(stored) === refNaturalKey(ref));
252
+ if (index < 0) return;
253
+ entry.refs.splice(index, 1);
254
+ entry.accounts.splice(index, 1);
255
+ entry.storedAtByKey.delete(refNaturalKey(ref));
256
+ if (!entry.accounts.length) this._store.delete(key);
257
+ }
204
258
  /** Remove all stored sync linkages. */
205
259
  clear() {
206
260
  this._store.clear();
@@ -1,3 +1,4 @@
1
+ import { accountReferenceKey } from "../core/account-key.mjs";
1
2
  function defaultImplicitKeyFn(authInfo) {
2
3
  const cred = authInfo.credential;
3
4
  if (!cred) return void 0;
@@ -11,11 +12,7 @@ function defaultImplicitKeyFn(authInfo) {
11
12
  }
12
13
  }
13
14
  function refNaturalKey(ref) {
14
- const r = ref;
15
- const domain = r["brand"]?.["domain"];
16
- const operator = r["operator"];
17
- const sandbox = Boolean(r["sandbox"]);
18
- return `${domain ?? ""}|${operator ?? ""}|${sandbox ? "1" : "0"}`;
15
+ return accountReferenceKey(ref);
19
16
  }
20
17
  function defaultBuildAccount(ref, _ctx) {
21
18
  const r = ref;
@@ -31,19 +28,25 @@ function defaultBuildAccount(ref, _ctx) {
31
28
  ...brand !== void 0 && { brand },
32
29
  ...operator !== "" && { operator },
33
30
  ...sandbox && { sandbox: true },
31
+ ..."operator_unit" in ref && { operator_unit: ref.operator_unit },
32
+ ..."currency" in ref && { currency: ref.currency },
33
+ ..."timezone" in ref && { timezone: ref.timezone },
34
34
  ctx_metadata: {}
35
35
  };
36
36
  }
37
37
  class InMemoryImplicitAccountStore {
38
38
  resolution = "implicit";
39
39
  _store = /* @__PURE__ */ new Map();
40
+ _writes = /* @__PURE__ */ new Map();
40
41
  _keyFn;
41
42
  _ttlMs;
43
+ _mergeOnUpsert;
42
44
  _buildAccount;
43
45
  constructor(options) {
44
46
  this._buildAccount = options?.buildAccount ?? defaultBuildAccount;
45
47
  this._keyFn = options?.keyFn ?? defaultImplicitKeyFn;
46
48
  this._ttlMs = options?.ttlMs ?? 864e5;
49
+ this._mergeOnUpsert = options?.mergeOnUpsert ?? false;
47
50
  }
48
51
  /**
49
52
  * Resolve the caller's account from the auth-principal→account mapping
@@ -55,14 +58,10 @@ class InMemoryImplicitAccountStore {
55
58
  * - The stored entry has exceeded `ttlMs`
56
59
  * - `ctx.authInfo` is absent or carries no extractable key
57
60
  *
58
- * **Multi-account note.** When a buyer synced multiple refs in one
59
- * `sync_accounts` call, this implementation returns the _first_ stored
60
- * account. If your platform requires per-request account disambiguation
61
- * (e.g., different brands on the same buyer), switch to `'explicit'` mode
62
- * so buyers pass `ext.account_ref` on each request, or override `keyFn`
63
- * to encode the brand into the key.
61
+ * Supplied references match the complete stored natural key. An omitted
62
+ * reference retains the historical first-account selection.
64
63
  */
65
- async resolve(_ref, ctx) {
64
+ async resolve(ref, ctx) {
66
65
  const authInfo = ctx?.authInfo;
67
66
  if (!authInfo) return null;
68
67
  const key = this._keyFn(authInfo);
@@ -73,7 +72,13 @@ class InMemoryImplicitAccountStore {
73
72
  this._store.delete(key);
74
73
  return null;
75
74
  }
76
- return entry.accounts[0] ?? null;
75
+ if (ref === void 0)
76
+ return entry.accounts.find(
77
+ (_account, index2) => Date.now() - (entry.storedAtByKey.get(refNaturalKey(entry.refs[index2])) ?? entry.storedAt) <= this._ttlMs
78
+ ) ?? null;
79
+ const index = entry.refs.findIndex((stored) => refNaturalKey(stored) === refNaturalKey(ref));
80
+ if (Date.now() - (entry.storedAtByKey.get(refNaturalKey(ref)) ?? entry.storedAt) > this._ttlMs) return null;
81
+ return index < 0 ? null : entry.accounts[index] ?? null;
77
82
  }
78
83
  /**
79
84
  * Process a `sync_accounts` payload: build accounts from refs and store
@@ -91,6 +96,25 @@ class InMemoryImplicitAccountStore {
91
96
  * reaching this method.
92
97
  */
93
98
  async upsert(refs, ctx) {
99
+ const snapshot = structuredClone(refs);
100
+ return this.withPrincipalLock(ctx, () => this.upsertUnlocked(snapshot, ctx));
101
+ }
102
+ withPrincipalLock(ctx, run) {
103
+ const key = ctx?.authInfo ? this._keyFn(ctx.authInfo) : void 0;
104
+ if (key === void 0) return run();
105
+ const next = (this._writes.get(key) ?? Promise.resolve()).then(run);
106
+ const tail = next.then(
107
+ () => {
108
+ },
109
+ () => {
110
+ }
111
+ );
112
+ this._writes.set(key, tail);
113
+ return next.finally(() => {
114
+ if (this._writes.get(key) === tail) this._writes.delete(key);
115
+ });
116
+ }
117
+ async upsertUnlocked(refs, ctx) {
94
118
  const authInfo = ctx?.authInfo;
95
119
  const key = authInfo !== void 0 ? this._keyFn(authInfo) : void 0;
96
120
  if (key === void 0) {
@@ -112,8 +136,10 @@ class InMemoryImplicitAccountStore {
112
136
  }
113
137
  const existing = this._store.get(key);
114
138
  const existingByNk = /* @__PURE__ */ new Map();
115
- if (existing) {
139
+ if (existing && Date.now() - existing.storedAt <= this._ttlMs) {
116
140
  for (let i = 0; i < existing.refs.length; i++) {
141
+ if (Date.now() - (existing.storedAtByKey.get(refNaturalKey(existing.refs[i])) ?? existing.storedAt) > this._ttlMs)
142
+ continue;
117
143
  existingByNk.set(refNaturalKey(existing.refs[i]), {
118
144
  account: existing.accounts[i],
119
145
  ref: existing.refs[i]
@@ -170,13 +196,41 @@ class InMemoryImplicitAccountStore {
170
196
  }
171
197
  }
172
198
  if (newAccounts.length > 0) {
173
- this._store.set(key, { accounts: newAccounts, refs: newRefs, storedAt: Date.now() });
199
+ const storedAt = Date.now();
200
+ const storedAtByKey = new Map(newRefs.map((ref) => [refNaturalKey(ref), storedAt]));
201
+ if (this._mergeOnUpsert && ctx?.input?.delete_missing !== true) {
202
+ const syncedKeys = new Set(newRefs.map(refNaturalKey));
203
+ for (const [nk, hit] of existingByNk) {
204
+ if (!syncedKeys.has(nk)) {
205
+ newAccounts.push(hit.account);
206
+ newRefs.push(hit.ref);
207
+ storedAtByKey.set(nk, existing.storedAtByKey.get(nk) ?? existing.storedAt);
208
+ }
209
+ }
210
+ }
211
+ this._store.set(key, { accounts: newAccounts, refs: newRefs, storedAt, storedAtByKey });
174
212
  }
175
213
  return rows;
176
214
  }
177
215
  // ---------------------------------------------------------------------------
178
216
  // Test helpers
179
217
  // ---------------------------------------------------------------------------
218
+ /** Revoke a single reference for this principal without clearing other brands. */
219
+ async remove(ref, ctx) {
220
+ return this.withPrincipalLock(ctx, () => this.removeUnlocked(ref, ctx));
221
+ }
222
+ async removeUnlocked(ref, ctx) {
223
+ const key = ctx?.authInfo ? this._keyFn(ctx.authInfo) : void 0;
224
+ if (key === void 0) return;
225
+ const entry = this._store.get(key);
226
+ if (!entry) return;
227
+ const index = entry.refs.findIndex((stored) => refNaturalKey(stored) === refNaturalKey(ref));
228
+ if (index < 0) return;
229
+ entry.refs.splice(index, 1);
230
+ entry.accounts.splice(index, 1);
231
+ entry.storedAtByKey.delete(refNaturalKey(ref));
232
+ if (!entry.accounts.length) this._store.delete(key);
233
+ }
180
234
  /** Remove all stored sync linkages. */
181
235
  clear() {
182
236
  this._store.clear();
@@ -191,6 +191,7 @@ export declare class CapabilityPreflightError extends Error {
191
191
  export declare class AgentClient {
192
192
  private agent;
193
193
  private config;
194
+ get accounts(): import("./buyer-account-registry.mjs").BuyerAccountRegistry;
194
195
  private client;
195
196
  private currentContextId?;
196
197
  private pendingTask?;
@@ -191,6 +191,7 @@ export declare class CapabilityPreflightError extends Error {
191
191
  export declare class AgentClient {
192
192
  private agent;
193
193
  private config;
194
+ get accounts(): import("./buyer-account-registry").BuyerAccountRegistry;
194
195
  private client;
195
196
  private currentContextId?;
196
197
  private pendingTask?;
@@ -67,6 +67,9 @@ class AgentClient {
67
67
  }
68
68
  agent;
69
69
  config;
70
+ get accounts() {
71
+ return this.client.accounts;
72
+ }
70
73
  client;
71
74
  currentContextId;
72
75
  pendingTask;
@@ -35,6 +35,9 @@ class AgentClient {
35
35
  }
36
36
  agent;
37
37
  config;
38
+ get accounts() {
39
+ return this.client.accounts;
40
+ }
38
41
  client;
39
42
  currentContextId;
40
43
  pendingTask;
@@ -11,9 +11,11 @@ import type { AdcpTaskName, TaskRequestFor, TaskResponseTypeMap } from './AgentC
11
11
  import type { DeferredTaskStorage } from '../storage/interfaces.mjs';
12
12
  import type { Activity, AsyncHandlerConfig } from './AsyncHandler.mjs';
13
13
  import { type WebhookHeaderValue, type WebhookHeadersLike } from '../webhooks/index.mjs';
14
- import { type WebhookRegistrationStore } from './webhook-registration.mjs';
14
+ import { type WebhookRegistration, type WebhookRegistrationStore } from './webhook-registration.mjs';
15
15
  import { type ReplayStore, type RevocationStore, type JwksResolver, type WebhookSignatureErrorCode, type ResolvedAgentJwksResolverOptions } from '../signing/server.mjs';
16
16
  import type { AdcpCapabilities, FeatureName } from '../utils/capabilities.mjs';
17
+ import { BuyerAccountRegistry, type AccountPolicy, type BuyerAccountStorage } from './buyer-account-registry.mjs';
18
+ import type { ProductCache } from './product-cache.mjs';
17
19
  import { type ResolveAccountOptions } from './account-resolution.mjs';
18
20
  import { type BuyerPropertyPolicy, type ProductPropertyPolicyMode } from '../media-buy/property-policy.mjs';
19
21
  import { type ResolveListOptions } from '../server/targeting-helpers.mjs';
@@ -93,6 +95,10 @@ export type WebhookParseErrorCode = WebhookSignatureErrorCode | 'webhook_signatu
93
95
  */
94
96
  | 'webhook_unverifiable' | 'webhook_registration_not_found' | 'webhook_registration_mismatch' | 'webhook_verification_context_missing' | 'webhook_registration_store_unavailable' | 'webhook_durable_settlement_unavailable' | 'webhook_publication_in_progress' | 'webhook_idempotency_conflict' | 'webhook_verification_unavailable';
95
97
  export interface WebhookVerificationConfig {
98
+ /** Applicable pins from persisted media-buy records. Reuse refresh callbacks bound to the seller/publisher/tenant across deliveries to share their cooldown. Never use webhook payload fields. */
99
+ publisherPins?: (registration: Readonly<WebhookRegistration>) => Promise<readonly import('../signing/publisher-pins.mjs').PublisherSigningKeyPin[]>;
100
+ /** Local diagnostics for request_signature_* discovery causes. */
101
+ onKeyResolutionError?: (error: unknown) => void;
96
102
  /** Deterministic/custom key source. Defaults to resolveAgent brand.json discovery. */
97
103
  jwks?: JwksResolver;
98
104
  /** Shared nonce replay store. Defaults to one process-local store per client. */
@@ -189,6 +195,13 @@ export declare class WebhookDispatchError extends Error {
189
195
  * Configuration for SingleAgentClient (and multi-agent client)
190
196
  */
191
197
  export interface SingleAgentClientConfig extends ConversationConfig {
198
+ /** Default off preserves existing wire requests; auto/strict use the provisioning registry. */
199
+ accountPolicy?: AccountPolicy;
200
+ accountStorage?: BuyerAccountStorage;
201
+ accountRegistryMaxEntries?: number;
202
+ /** Trusted stable caller identity for durable registry partitioning. Default is credential fingerprint. */
203
+ accountRegistryScope?: string;
204
+ productCache?: ProductCache;
192
205
  /** Durable storage for restart-safe A2A human continuations. */
193
206
  deferredStorage?: DeferredTaskStorage;
194
207
  /** Resolve current trusted agent configuration for a persisted continuation. */
@@ -460,6 +473,23 @@ export interface CapabilityEvidenceSnapshot {
460
473
  export declare class SingleAgentClient {
461
474
  private agent;
462
475
  private config;
476
+ private _accounts?;
477
+ private accountRegistryExplicitlyAccessed;
478
+ private initialOAuthGrantMaterial?;
479
+ private accountCredentialMaterial?;
480
+ private accountCredentialScope?;
481
+ private credentialScope;
482
+ private accountScope;
483
+ get accounts(): BuyerAccountRegistry;
484
+ private getAccountRegistry;
485
+ /**
486
+ * The memoized registry backs `resolveAccount()` only when the caller opts
487
+ * in with an account policy or registry configuration. Otherwise
488
+ * `resolveAccount()` keeps the SDK 14.0 sync-on-every-call behavior.
489
+ */
490
+ private accountRegistryOptedIn;
491
+ private accountRegistryContext;
492
+ private applyAccountPolicy;
463
493
  private executor;
464
494
  private asyncHandler?;
465
495
  private normalizedAgent;
@@ -11,9 +11,11 @@ import type { AdcpTaskName, TaskRequestFor, TaskResponseTypeMap } from './AgentC
11
11
  import type { DeferredTaskStorage } from '../storage/interfaces';
12
12
  import type { Activity, AsyncHandlerConfig } from './AsyncHandler';
13
13
  import { type WebhookHeaderValue, type WebhookHeadersLike } from '../webhooks';
14
- import { type WebhookRegistrationStore } from './webhook-registration';
14
+ import { type WebhookRegistration, type WebhookRegistrationStore } from './webhook-registration';
15
15
  import { type ReplayStore, type RevocationStore, type JwksResolver, type WebhookSignatureErrorCode, type ResolvedAgentJwksResolverOptions } from '../signing/server';
16
16
  import type { AdcpCapabilities, FeatureName } from '../utils/capabilities';
17
+ import { BuyerAccountRegistry, type AccountPolicy, type BuyerAccountStorage } from './buyer-account-registry';
18
+ import type { ProductCache } from './product-cache';
17
19
  import { type ResolveAccountOptions } from './account-resolution';
18
20
  import { type BuyerPropertyPolicy, type ProductPropertyPolicyMode } from '../media-buy/property-policy';
19
21
  import { type ResolveListOptions } from '../server/targeting-helpers';
@@ -93,6 +95,10 @@ export type WebhookParseErrorCode = WebhookSignatureErrorCode | 'webhook_signatu
93
95
  */
94
96
  | 'webhook_unverifiable' | 'webhook_registration_not_found' | 'webhook_registration_mismatch' | 'webhook_verification_context_missing' | 'webhook_registration_store_unavailable' | 'webhook_durable_settlement_unavailable' | 'webhook_publication_in_progress' | 'webhook_idempotency_conflict' | 'webhook_verification_unavailable';
95
97
  export interface WebhookVerificationConfig {
98
+ /** Applicable pins from persisted media-buy records. Reuse refresh callbacks bound to the seller/publisher/tenant across deliveries to share their cooldown. Never use webhook payload fields. */
99
+ publisherPins?: (registration: Readonly<WebhookRegistration>) => Promise<readonly import('../signing/publisher-pins').PublisherSigningKeyPin[]>;
100
+ /** Local diagnostics for request_signature_* discovery causes. */
101
+ onKeyResolutionError?: (error: unknown) => void;
96
102
  /** Deterministic/custom key source. Defaults to resolveAgent brand.json discovery. */
97
103
  jwks?: JwksResolver;
98
104
  /** Shared nonce replay store. Defaults to one process-local store per client. */
@@ -189,6 +195,13 @@ export declare class WebhookDispatchError extends Error {
189
195
  * Configuration for SingleAgentClient (and multi-agent client)
190
196
  */
191
197
  export interface SingleAgentClientConfig extends ConversationConfig {
198
+ /** Default off preserves existing wire requests; auto/strict use the provisioning registry. */
199
+ accountPolicy?: AccountPolicy;
200
+ accountStorage?: BuyerAccountStorage;
201
+ accountRegistryMaxEntries?: number;
202
+ /** Trusted stable caller identity for durable registry partitioning. Default is credential fingerprint. */
203
+ accountRegistryScope?: string;
204
+ productCache?: ProductCache;
192
205
  /** Durable storage for restart-safe A2A human continuations. */
193
206
  deferredStorage?: DeferredTaskStorage;
194
207
  /** Resolve current trusted agent configuration for a persisted continuation. */
@@ -460,6 +473,23 @@ export interface CapabilityEvidenceSnapshot {
460
473
  export declare class SingleAgentClient {
461
474
  private agent;
462
475
  private config;
476
+ private _accounts?;
477
+ private accountRegistryExplicitlyAccessed;
478
+ private initialOAuthGrantMaterial?;
479
+ private accountCredentialMaterial?;
480
+ private accountCredentialScope?;
481
+ private credentialScope;
482
+ private accountScope;
483
+ get accounts(): BuyerAccountRegistry;
484
+ private getAccountRegistry;
485
+ /**
486
+ * The memoized registry backs `resolveAccount()` only when the caller opts
487
+ * in with an account policy or registry configuration. Otherwise
488
+ * `resolveAccount()` keeps the SDK 14.0 sync-on-every-call behavior.
489
+ */
490
+ private accountRegistryOptedIn;
491
+ private accountRegistryContext;
492
+ private applyAccountPolicy;
463
493
  private executor;
464
494
  private asyncHandler?;
465
495
  private normalizedAgent;