@agentsbloom/sdk 0.2.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -1,124 +1,355 @@
1
- export type Protocol = 'WEBMCP' | 'UCP' | 'ACP' | 'AP2' | 'AGENTSBLOOM_REST';
2
-
3
- export type AgentActionHandler = (
4
- params: Record<string, unknown>,
5
- req: unknown,
6
- res: unknown
7
- ) => unknown | Promise<unknown>;
8
-
9
- export interface AgentAction {
10
- method?: string;
11
- description?: string;
12
- params?: Record<string, string>;
13
- handler: AgentActionHandler;
14
- }
15
-
16
- export interface AgentsBloomConfig {
17
- apiKey?: string | null;
18
- agentSecret?: string | null;
19
- name?: string;
20
- description?: string;
21
- actions?: Record<string, AgentAction>;
22
- llmsDoc?: string;
23
- baseUrl?: string;
24
- corsOrigin?: string;
25
- merchantJwks?: Record<string, unknown>;
26
- agentJwks?: {
27
- keys: Array<Record<string, unknown>>;
28
- };
29
- agentJwksUrl?: string;
30
- signature?: {
31
- maxAgeMs?: number;
32
- };
33
- ap2PublicKey?: unknown;
34
- ap2?: {
35
- expectedAudience?: string;
36
- maxMandateLifetimeSec?: number;
37
- requireJti?: boolean;
38
- requestedCategories?: string[];
39
- };
40
- rateLimit?: {
41
- max?: number;
42
- windowMs?: number;
43
- };
44
- idempotency?: {
45
- ttlMs?: number;
46
- };
47
- demoMode?: boolean;
48
- disableSignatureAuth?: boolean;
49
- disableHtmlInjection?: boolean;
50
- }
51
-
52
- export type AgentsBloomMiddleware = (
53
- req: unknown,
54
- res: unknown,
55
- next: (error?: unknown) => void
56
- ) => unknown;
57
-
58
- export interface TelemetryOptions {
59
- otlpEndpoint?: string;
60
- serviceName?: string;
61
- samplingRatio?: number;
62
- apiKey?: string;
63
- }
64
-
65
- export interface TelemetryConfig {
66
- otlpEndpoint: string;
67
- serviceName: string;
68
- samplingRatio: number;
69
- }
70
-
71
- export interface Ap2VerificationResult {
72
- valid: boolean;
73
- verified?: boolean;
74
- protocol: 'AP2';
75
- reason?: string;
76
- note?: string;
77
- selfCertifying?: boolean;
78
- mandates?: Record<string, unknown>;
79
- }
80
-
81
- export interface Ap2VerifyOptions {
82
- trustedPublicKey?: unknown;
83
- expectedAudience?: string;
84
- maxMandateLifetimeSec?: number;
85
- requireJti?: boolean;
86
- requestedCategories?: string[];
87
- }
88
-
89
- export interface CreateAp2MandateOptions {
90
- audience: string;
91
- maxBudget: number;
92
- currency?: string;
93
- allowedCategories?: string[];
94
- merchantScope?: string;
95
- lifetimeSec?: number;
96
- paymentMethod?: string;
97
- subject?: string;
98
- privateKey?: unknown;
99
- }
100
-
101
- export interface Ap2Mandate {
102
- token: string;
103
- did: string;
104
- publicKey: unknown;
105
- privateKey: unknown;
106
- }
107
-
108
- export function agentsbloom(config?: AgentsBloomConfig): AgentsBloomMiddleware;
109
- export function setupTelemetry(options?: TelemetryOptions): Promise<TelemetryConfig>;
110
- export function shutdown(): Promise<void>;
111
- export function resolveProtocol(req: {
112
- headers?: Record<string, string | undefined>;
113
- path?: string;
114
- url?: string;
115
- }): Protocol;
116
- export function verifyAP2Mandates(
117
- headers?: Record<string, string | undefined>,
118
- body?: Record<string, unknown>,
119
- publicKeyOrOptions?: unknown | Ap2VerifyOptions
120
- ): Ap2VerificationResult;
121
- export function createAp2Mandate(options: CreateAp2MandateOptions): Ap2Mandate;
122
- export function didKeyFromEd25519PublicKey(publicKey: unknown): string;
123
- export function ed25519PublicKeyFromDidKey(didKey: string): unknown | null;
124
- export function resetAp2ReplayCache(): void;
1
+ export type Protocol = 'WEBMCP' | 'UCP' | 'ACP' | 'AP2' | 'AGENTSBLOOM_REST';
2
+
3
+ export type AgentActionHandler = (
4
+ params: Record<string, unknown>,
5
+ req: unknown,
6
+ res: unknown
7
+ ) => unknown | Promise<unknown>;
8
+
9
+ export interface AgentAction {
10
+ method?: string;
11
+ description?: string;
12
+ params?: Record<string, string>;
13
+ handler: AgentActionHandler;
14
+ }
15
+
16
+ export interface AgentsBloomConfig {
17
+ apiKey?: string | null;
18
+ agentSecret?: string | null;
19
+ /**
20
+ * Previous shared secret, kept verify-live during AGENTSBLOOM_SECRET
21
+ * rotation so in-flight signed requests survive the window. Falls back
22
+ * to the AGENTSBLOOM_SECRET_PREVIOUS environment variable.
23
+ */
24
+ agentSecretPrevious?: string | null;
25
+ name?: string;
26
+ description?: string;
27
+ actions?: Record<string, AgentAction>;
28
+ llmsDoc?: string;
29
+ baseUrl?: string;
30
+ /**
31
+ * v4: when set, only requests whose Host header matches one of these
32
+ * values get Host-derived discovery URLs; any other Host falls back to
33
+ * `baseUrl`. Prevents Host-header poisoning of discovery documents.
34
+ */
35
+ allowedHosts?: string[];
36
+ /**
37
+ * v4 medium pass: string behaves as before; an ARRAY is an exact-match
38
+ * allow-list - matching Origins are reflected (Vary: Origin), everything
39
+ * else receives no Access-Control-Allow-Origin at all.
40
+ */
41
+ corsOrigin?: string | string[];
42
+ /** v4 medium pass (V10): body-size cap in bytes, enforced against the
43
+ * received bytes as well as Content-Length (default 1 MiB). */
44
+ maxBodyBytes?: number;
45
+ /**
46
+ * v4 medium pass (V11): per-agent legacy-HMAC secrets keyed by
47
+ * X-Agent-Identifier. Checked before the shared `agentSecret` fallback.
48
+ */
49
+ agentKeys?: Record<string, string>;
50
+ /** v4 medium pass (V11): identifiers rejected outright before verification. */
51
+ revokedIdentifiers?: string[];
52
+ /**
53
+ * v4 medium pass (V12): per-action authorization. 'authenticated'
54
+ * requires a verified RFC/legacy signature identity even for reads.
55
+ */
56
+ actionAccess?: Record<string, 'anonymous' | 'authenticated'>;
57
+ /**
58
+ * v4 medium pass (V12): per-action identity allow-lists. Entries match
59
+ * exactly (`rfc:key-1`) or by prefix when ending with ':' (`legacy:`).
60
+ */
61
+ actionIdentities?: Record<string, string[]>;
62
+ merchantJwks?: Record<string, unknown>;
63
+ /**
64
+ * v4: RFC 9421 verification now FAILS CLOSED - one of `agentJwks` or
65
+ * `agentJwksUrl` must be configured for Signature-header writes to verify.
66
+ */
67
+ agentJwks?: {
68
+ keys: Array<Record<string, unknown>>;
69
+ };
70
+ agentJwksUrl?: string;
71
+ signature?: {
72
+ maxAgeMs?: number;
73
+ /**
74
+ * Require the `@authority` covered component. DEFAULTS TO TRUE.
75
+ *
76
+ * Without it, a signature captured at store A is structurally valid at
77
+ * store B for the same path whenever both trust the same JWKS. Set
78
+ * `baseUrl` (or `allowedHosts`) too: `@authority` canonicalizes the
79
+ * request's own Host header, so binding is only effective when the store
80
+ * also knows which authorities are its own.
81
+ */
82
+ requireAuthority?: boolean;
83
+ /**
84
+ * Accept the pre-0.6 (non-conformant) signature base as a fallback:
85
+ * lowercased `@method`, query folded into `@path`, missing covered headers
86
+ * canonicalized to the empty string. Off by default; enable only for a
87
+ * migration window.
88
+ */
89
+ acceptLegacyProfile?: boolean;
90
+ /**
91
+ * Compute `Content-Digest` from a re-serialized body when `req.rawBody` is
92
+ * unavailable. Off by default, because a digest over a re-serialization
93
+ * does not prove what the client actually sent. Prefer
94
+ * `express.json({ verify: (req, _res, buf) => { req.rawBody = buf; } })`.
95
+ */
96
+ allowReserializedBody?: boolean;
97
+ };
98
+ /**
99
+ * Prefix for replay-cache and idempotency keys. Derived from `baseUrl` when
100
+ * omitted. Set it explicitly only when several distinct stores share one
101
+ * replay store (UPSTASH_REDIS_REST_URL).
102
+ */
103
+ cacheNamespace?: string;
104
+ /** Bounds the HTML-injection response buffer (default 2 MiB). */
105
+ htmlInjection?: {
106
+ maxBytes?: number;
107
+ };
108
+ ap2PublicKey?: unknown;
109
+ ap2?: {
110
+ expectedAudience?: string;
111
+ maxMandateLifetimeSec?: number;
112
+ requireJti?: boolean;
113
+ requestedCategories?: string[];
114
+ /**
115
+ * v4: reject self-certifying did:key mandates (anyone can mint those
116
+ * with an arbitrary maxBudget) and accept only mandates signed by the
117
+ * merchant-configured trusted key.
118
+ */
119
+ trustedIssuersOnly?: boolean;
120
+ /** v4 medium pass (V17): pin the currency budgets are denominated in. */
121
+ expectedCurrency?: string;
122
+ /**
123
+ * Reject a mandate that declares a `maxBudget` with no `currency`.
124
+ * DEFAULTS TO TRUE: an amount without a unit cannot be enforced, and
125
+ * every currency check is otherwise skipped.
126
+ */
127
+ requireCurrency?: boolean;
128
+ /** Tolerance in seconds for `iat`/`exp` clock drift (default 60). */
129
+ clockSkewSec?: number;
130
+ /**
131
+ * Require the checkout handler to return an authoritative `total` /
132
+ * `totalPrice`. DEFAULTS TO TRUE: without a store-computed total the
133
+ * mandate budget cannot be enforced at all, so checkout fails closed
134
+ * rather than issuing an unenforced payment link.
135
+ */
136
+ requireHandlerTotal?: boolean;
137
+ /**
138
+ * Include the detailed internal rejection reason in AP2 error responses.
139
+ * Off by default: reasons can reveal the store's expected audience and
140
+ * currency, which is exactly what a forged mandate needs.
141
+ */
142
+ exposeReasons?: boolean;
143
+ /** Allow a cart-bound mandate through when the session cart cannot be verified. */
144
+ allowUnverifiedCartMandates?: boolean;
145
+ /** AP2 Cart Mandate binding: derive expected cart hash from the session request. */
146
+ computeCartHash?: (req: unknown) => string | null;
147
+ };
148
+ rateLimit?: {
149
+ max?: number;
150
+ windowMs?: number;
151
+ };
152
+ mcp?: {
153
+ /** v4: cap on concurrent MCP SSE sessions (default 100). */
154
+ maxSessions?: number;
155
+ };
156
+ idempotency?: {
157
+ ttlMs?: number;
158
+ /** v4 medium pass (V18): bound on cached entries (default 10,000). */
159
+ maxEntries?: number;
160
+ };
161
+ demoMode?: boolean;
162
+ disableSignatureAuth?: boolean;
163
+ disableHtmlInjection?: boolean;
164
+ }
165
+
166
+ export type AgentsBloomMiddleware = (
167
+ req: unknown,
168
+ res: unknown,
169
+ next: (error?: unknown) => void
170
+ ) => unknown;
171
+
172
+ export interface TelemetryOptions {
173
+ otlpEndpoint?: string;
174
+ serviceName?: string;
175
+ samplingRatio?: number;
176
+ apiKey?: string;
177
+ }
178
+
179
+ export interface TelemetryConfig {
180
+ otlpEndpoint: string;
181
+ serviceName: string;
182
+ samplingRatio: number;
183
+ }
184
+
185
+ export type Ap2RejectionCode =
186
+ | 'mandate_malformed'
187
+ | 'sd_jwt_disclosures_unsupported'
188
+ | 'typ_unsupported'
189
+ | 'crit_unsupported'
190
+ | 'alg_missing'
191
+ | 'alg_unsupported'
192
+ | 'alg_key_mismatch'
193
+ | 'claim_missing'
194
+ | 'jti_invalid'
195
+ | 'expired'
196
+ | 'future'
197
+ | 'not_yet_valid'
198
+ | 'lifetime_exceeded'
199
+ | 'self_certifying_disabled'
200
+ | 'trusted_key_invalid'
201
+ | 'didkey_alg'
202
+ | 'didkey_invalid'
203
+ | 'unverifiable'
204
+ | 'signature_invalid'
205
+ | 'signature_error'
206
+ | 'audience_mismatch'
207
+ | 'merchant_scope_mismatch'
208
+ | 'cart_binding_mismatch'
209
+ | 'category_denied'
210
+ | 'currency_invalid'
211
+ | 'currency_missing'
212
+ | 'store_currency_invalid'
213
+ | 'currency_mismatch_store'
214
+ | 'order_currency_invalid'
215
+ | 'currency_mismatch_order'
216
+ | 'budget_invalid'
217
+ | 'order_total_invalid'
218
+ | 'budget_exceeded'
219
+ | 'replay';
220
+
221
+ export interface Ap2VerificationResult {
222
+ valid: boolean;
223
+ /** Stable machine-readable rejection identifier. */
224
+ code?: Ap2RejectionCode;
225
+ /**
226
+ * True when `reason` is safe to return to the caller. False for rejections
227
+ * whose detail would reveal store configuration (expected audience,
228
+ * expected currency) or verifier internals (raw crypto error text) — use
229
+ * `publicReason` in that case.
230
+ */
231
+ disclose?: boolean;
232
+ /** Caller-safe rendering of `reason`. */
233
+ publicReason?: string;
234
+ /** Mandate currency, normalized to an uppercase ISO 4217 code. */
235
+ currency?: string | null;
236
+ /** The JWS algorithm the mandate was verified with. */
237
+ algorithm?: string;
238
+ cartBinding?: 'verified' | 'unverified' | 'unbound';
239
+ /**
240
+ * True only when a presented mandate cryptographically verified. When a
241
+ * shared replay store (UPSTASH_REDIS_REST_URL/TOKEN) is configured,
242
+ * verification may resolve asynchronously - the return type becomes
243
+ * Promise<Ap2VerificationResult>; callers should await it either way.
244
+ */
245
+ verified?: boolean;
246
+ protocol: 'AP2';
247
+ reason?: string;
248
+ note?: string;
249
+ selfCertifying?: boolean;
250
+ mandates?: Record<string, unknown>;
251
+ }
252
+
253
+ export interface Ap2VerifyOptions {
254
+ trustedPublicKey?: unknown;
255
+ expectedAudience?: string;
256
+ maxMandateLifetimeSec?: number;
257
+ requireJti?: boolean;
258
+ requestedCategories?: string[];
259
+ /** v4: when false, self-certifying did:key issuers are rejected. */
260
+ allowSelfCertifying?: boolean;
261
+ expectedCartHash?: string;
262
+ expectedCurrency?: string;
263
+ /** Reject a `maxBudget` with no `currency` (default true). */
264
+ requireCurrency?: boolean;
265
+ /** Tolerance in seconds for `iat`/`exp` clock drift (default 60). */
266
+ clockSkewSec?: number;
267
+ /** When false, a valid mandate is verified WITHOUT consuming its jti. */
268
+ consumeJti?: boolean;
269
+ /** Injectable clock for tests. */
270
+ nowMs?: number;
271
+ }
272
+
273
+ export interface CreateAp2MandateOptions {
274
+ audience: string;
275
+ maxBudget: number;
276
+ currency?: string;
277
+ allowedCategories?: string[];
278
+ merchantScope?: string;
279
+ lifetimeSec?: number;
280
+ paymentMethod?: string;
281
+ subject?: string;
282
+ privateKey?: unknown;
283
+ agentInitiated?: boolean;
284
+ cart?: { items: Array<unknown> };
285
+ cartHash?: string;
286
+ }
287
+
288
+ export interface Ap2Mandate {
289
+ token: string;
290
+ did: string;
291
+ publicKey: unknown;
292
+ privateKey: unknown;
293
+ cartHash?: string | null;
294
+ }
295
+
296
+ export function agentsbloom(config?: AgentsBloomConfig): AgentsBloomMiddleware;
297
+ export function setupTelemetry(options?: TelemetryOptions): Promise<TelemetryConfig>;
298
+ export function shutdown(): Promise<void>;
299
+ export function resolveProtocol(req: {
300
+ headers?: Record<string, string | undefined>;
301
+ path?: string;
302
+ url?: string;
303
+ }): Protocol;
304
+ export function verifyAP2Mandates(
305
+ headers?: Record<string, string | undefined>,
306
+ body?: Record<string, unknown>,
307
+ publicKeyOrOptions?: unknown | Ap2VerifyOptions
308
+ ): Ap2VerificationResult | Promise<Ap2VerificationResult>;
309
+ export function createAp2Mandate(options: CreateAp2MandateOptions): Ap2Mandate;
310
+ export function didKeyFromEd25519PublicKey(publicKey: unknown): string;
311
+ export function ed25519PublicKeyFromDidKey(didKey: string): unknown | null;
312
+ export function resetAp2ReplayCache(): void;
313
+ export function canonicalCartHash(cart: unknown): { cartHash: string; [key: string]: unknown };
314
+ export function stableStringify(value: unknown): string;
315
+ /**
316
+ * Normalizes an audience/merchantScope value (URL-parsed: lowercased host,
317
+ * trailing slashes stripped) so mandates built via new URL().href compare
318
+ * equal to slash-free configured audiences.
319
+ */
320
+ export function normalizeAudience(value: string): string;
321
+
322
+ /** Marks quota exceeded process-wide until the given epoch-ms timestamp. */
323
+ export function setQuotaExceededUntil(untilEpochMs: number): void;
324
+ /** Clears the process-wide quota-exceeded state immediately. */
325
+ export function clearQuotaExceeded(): void;
326
+
327
+ /**
328
+ * RFC 9421 `alg` values this SDK verifies. Both the RFC 9421 registry names
329
+ * (`ecdsa-p256-sha256`) and the JOSE spellings (`ES256`) are accepted.
330
+ */
331
+ export const SUPPORTED_SIGNATURE_ALGORITHMS: readonly string[];
332
+ /** JWS algorithms accepted for AP2 mandates signed by a merchant-trusted key. */
333
+ export const SUPPORTED_MANDATE_ALGORITHMS: readonly string[];
334
+
335
+ export interface OutcomeReport {
336
+ orderRef: string;
337
+ status: 'paid' | 'declined' | 'refunded' | 'chargeback' | 'disputed' | 'canceled';
338
+ gateway?: 'stripe' | 'razorpay' | 'paddle' | 'other';
339
+ reason?: string | null;
340
+ amount?: number | null;
341
+ currency?: string | null;
342
+ identityKey?: string | null;
343
+ occurredAt?: string;
344
+ }
345
+
346
+ export interface OutcomeReporter {
347
+ report(body: OutcomeReport): Promise<{ ok: boolean; recorded: string; orderRef: string }>;
348
+ captureStripeOutcome(event: unknown): Promise<{ skipped: boolean } | { ok: boolean }>;
349
+ }
350
+ export function stripeEventToOutcome(event: unknown): OutcomeReport | null;
351
+ export function createOutcomeReporter(options: {
352
+ apiKey: string;
353
+ collectorUrl?: string;
354
+ fetchImpl?: (url: string, init: object) => Promise<Response>;
355
+ }): OutcomeReporter;