@reinconsole/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,351 @@
1
+ import { AsyncLocalStorage } from 'async_hooks';
2
+ import { Decision, PaymentIntent, Agent, Policy, Asset, AgentId, Receipt, newId } from '@reinconsole/core';
3
+ import { z } from 'zod';
4
+
5
+ // src/guard.ts
6
+
7
+ // src/errors.ts
8
+ var ReinError = class extends Error {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = new.target.name;
12
+ }
13
+ };
14
+ var EngineError = class extends ReinError {
15
+ constructor(status, body, message = `policy engine responded ${status}`) {
16
+ super(message);
17
+ this.status = status;
18
+ this.body = body;
19
+ }
20
+ status;
21
+ body;
22
+ };
23
+ var PaymentBlockedError = class extends ReinError {
24
+ constructor(intent, decision, receipt) {
25
+ super(
26
+ `rein blocked payment of ${intent.amount} ${intent.asset} to ${intent.vendor.host}: ${decision.outcome}${decision.reason ? ` (${decision.reason})` : ""}`
27
+ );
28
+ this.intent = intent;
29
+ this.decision = decision;
30
+ this.receipt = receipt;
31
+ }
32
+ intent;
33
+ decision;
34
+ receipt;
35
+ };
36
+ var UnsupportedRequirementError = class extends ReinError {
37
+ constructor(url) {
38
+ super(`no supported x402 payment requirement in 402 from ${url}; failing closed`);
39
+ this.url = url;
40
+ }
41
+ url;
42
+ };
43
+
44
+ // src/client.ts
45
+ var Health = z.object({ status: z.string(), publicKey: z.string() });
46
+ var EvaluateResponse = z.object({ intent: PaymentIntent, decision: Decision });
47
+ var EngineClient = class {
48
+ baseUrl;
49
+ fetchImpl;
50
+ constructor(options) {
51
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
52
+ const f = options.fetch ?? globalThis.fetch;
53
+ this.fetchImpl = (input, init) => f(input, init);
54
+ }
55
+ async request(method, path, schema, body) {
56
+ const res = await this.fetchImpl(`${this.baseUrl}${path}`, {
57
+ method,
58
+ headers: body === void 0 ? void 0 : { "content-type": "application/json" },
59
+ body: body === void 0 ? void 0 : JSON.stringify(body)
60
+ });
61
+ if (!res.ok) {
62
+ const payload = await res.clone().json().catch(() => res.text().catch(() => void 0));
63
+ throw new EngineError(res.status, payload);
64
+ }
65
+ if (res.status === 204) return schema.parse(void 0);
66
+ return schema.parse(await res.json());
67
+ }
68
+ health() {
69
+ return this.request("GET", "/health", Health);
70
+ }
71
+ registerAgent(input) {
72
+ return this.request("POST", "/v1/agents", Agent, input);
73
+ }
74
+ listAgents() {
75
+ return this.request("GET", "/v1/agents", z.array(Agent));
76
+ }
77
+ freeze(agentId) {
78
+ return this.request("POST", `/v1/agents/${agentId}/freeze`, z.void());
79
+ }
80
+ unfreeze(agentId) {
81
+ return this.request("POST", `/v1/agents/${agentId}/unfreeze`, z.void());
82
+ }
83
+ addPolicy(policy) {
84
+ return this.request("POST", "/v1/policies", Policy, policy);
85
+ }
86
+ listPolicies() {
87
+ return this.request("GET", "/v1/policies", z.array(Policy));
88
+ }
89
+ /** The hot path: submit an intent, get the normalized intent + signed decision. */
90
+ evaluate(submission) {
91
+ return this.request("POST", "/v1/evaluate", EvaluateResponse, submission);
92
+ }
93
+ decisions() {
94
+ return this.request("GET", "/v1/decisions", z.array(Decision));
95
+ }
96
+ };
97
+ var PaymentRequirement = z.object({
98
+ scheme: z.string(),
99
+ network: z.string(),
100
+ /** Amount in the asset's atomic units (e.g. "10000" = 0.01 USDC at 6 decimals). */
101
+ maxAmountRequired: z.string().regex(/^\d+$/, "atomic amount must be an integer string"),
102
+ resource: z.string().optional(),
103
+ description: z.string().optional(),
104
+ mimeType: z.string().optional(),
105
+ payTo: z.string().min(1),
106
+ maxTimeoutSeconds: z.number().optional(),
107
+ /** Token contract address — or, mock-first, a plain symbol like "USDC". */
108
+ asset: z.string().min(1),
109
+ extra: z.record(z.unknown()).optional()
110
+ });
111
+ var PaymentRequired = z.object({
112
+ x402Version: z.number(),
113
+ accepts: z.array(PaymentRequirement).min(1),
114
+ error: z.string().optional()
115
+ });
116
+ var NETWORK_TO_CHAIN = {
117
+ base: "base",
118
+ "base-sepolia": "base",
119
+ "eip155:8453": "base",
120
+ "eip155:84532": "base",
121
+ solana: "solana",
122
+ "solana-devnet": "solana",
123
+ polygon: "polygon",
124
+ "polygon-amoy": "polygon",
125
+ bnb: "bnb",
126
+ bsc: "bnb"
127
+ };
128
+ var KNOWN_ASSET_ADDRESSES = {
129
+ "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913": "USDC",
130
+ // USDC on Base
131
+ "0x036cbd53842c5426634e7929541ec2318f3dcf7e": "USDC",
132
+ // USDC on Base Sepolia
133
+ EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v: "USDC"
134
+ // USDC on Solana
135
+ };
136
+ function networkToChain(network) {
137
+ return NETWORK_TO_CHAIN[network.toLowerCase()];
138
+ }
139
+ function resolveAsset(requirement, extraAddresses = {}) {
140
+ const direct = Asset.safeParse(requirement.asset.toUpperCase());
141
+ if (direct.success) return direct.data;
142
+ const symbol = requirement.extra?.["symbol"];
143
+ if (typeof symbol === "string") {
144
+ const fromExtra = Asset.safeParse(symbol.toUpperCase());
145
+ if (fromExtra.success) return fromExtra.data;
146
+ }
147
+ return extraAddresses[requirement.asset] ?? extraAddresses[requirement.asset.toLowerCase()] ?? KNOWN_ASSET_ADDRESSES[requirement.asset] ?? KNOWN_ASSET_ADDRESSES[requirement.asset.toLowerCase()];
148
+ }
149
+ function atomicToDecimal(atomic, decimals) {
150
+ if (!/^\d+$/.test(atomic)) throw new TypeError(`invalid atomic amount: ${atomic}`);
151
+ if (decimals === 0) return atomic.replace(/^0+(?=\d)/, "");
152
+ const digits = atomic.padStart(decimals + 1, "0");
153
+ const intPart = digits.slice(0, digits.length - decimals).replace(/^0+(?=\d)/, "");
154
+ const fracPart = digits.slice(digits.length - decimals).replace(/0+$/, "");
155
+ return fracPart.length > 0 ? `${intPart}.${fracPart}` : intPart;
156
+ }
157
+ function decimalToAtomic(decimal, decimals) {
158
+ if (!/^\d+(\.\d+)?$/.test(decimal)) throw new TypeError(`invalid decimal amount: ${decimal}`);
159
+ const dot = decimal.indexOf(".");
160
+ const intPart = dot === -1 ? decimal : decimal.slice(0, dot);
161
+ const fracPart = dot === -1 ? "" : decimal.slice(dot + 1);
162
+ if (fracPart.length > decimals) {
163
+ throw new TypeError(`amount ${decimal} has more than ${decimals} fraction digits`);
164
+ }
165
+ const atomic = intPart + fracPart.padEnd(decimals, "0");
166
+ return atomic.replace(/^0+(?=\d)/, "");
167
+ }
168
+ function requirementDecimals(requirement) {
169
+ const decimals = requirement.extra?.["decimals"];
170
+ return typeof decimals === "number" && Number.isInteger(decimals) && decimals >= 0 ? decimals : 6;
171
+ }
172
+ function selectRequirement(accepts, extraAddresses = {}) {
173
+ for (const requirement of accepts) {
174
+ if (requirement.scheme.toLowerCase() !== "exact") continue;
175
+ const chain = networkToChain(requirement.network);
176
+ if (!chain) continue;
177
+ const asset = resolveAsset(requirement, extraAddresses);
178
+ if (!asset) continue;
179
+ const amount = atomicToDecimal(requirement.maxAmountRequired, requirementDecimals(requirement));
180
+ return { requirement, chain, asset, amount };
181
+ }
182
+ return void 0;
183
+ }
184
+ function toIntentSubmission(resolved, url, agentId, taskContext) {
185
+ const parsed = new URL(url);
186
+ return {
187
+ agentId,
188
+ vendor: {
189
+ host: parsed.host,
190
+ address: resolved.requirement.payTo
191
+ },
192
+ resource: resolved.requirement.resource ?? parsed.pathname,
193
+ amount: resolved.amount,
194
+ asset: resolved.asset,
195
+ chain: resolved.chain,
196
+ taskContext
197
+ };
198
+ }
199
+
200
+ // src/guard.ts
201
+ var Guard = class {
202
+ client;
203
+ options;
204
+ baseFetch;
205
+ task = new AsyncLocalStorage();
206
+ log = [];
207
+ /** Allowed-but-unsettled receipts awaiting the payment layer's retry, by URL. */
208
+ pendingByUrl = /* @__PURE__ */ new Map();
209
+ constructor(options) {
210
+ AgentId.parse(options.agentId);
211
+ this.options = options;
212
+ const f = options.fetch ?? globalThis.fetch;
213
+ this.baseFetch = (input, init) => f(input, init);
214
+ this.client = new EngineClient({ baseUrl: options.engineUrl, fetch: options.engineFetch });
215
+ }
216
+ /** Every receipt this guard has recorded, oldest first. */
217
+ receipts() {
218
+ return this.log;
219
+ }
220
+ /** Run `fn` with a task context attached to every intent submitted inside it. */
221
+ withTask(context, fn) {
222
+ return this.task.run({ ...this.options.taskContext, ...context }, fn);
223
+ }
224
+ /**
225
+ * The one-liner: returns a fetch-compatible function that enforces policy on
226
+ * every x402 paywall. Wraps the guard's base fetch unless one is passed.
227
+ */
228
+ wrap(fetchImpl) {
229
+ const inner = fetchImpl ? (input, init) => fetchImpl(input, init) : this.baseFetch;
230
+ return async (input, init) => {
231
+ const url = requestUrl(input);
232
+ if (readHeader(input, init, "X-PAYMENT") !== null) {
233
+ const res2 = await inner(input, init);
234
+ this.settlePending(url, res2);
235
+ return res2;
236
+ }
237
+ const res = await inner(input, init);
238
+ if (res.status !== 402) return res;
239
+ const body = await res.clone().json().catch(() => void 0);
240
+ const parsed = PaymentRequired.safeParse(body);
241
+ if (!parsed.success) return res;
242
+ const resolved = selectRequirement(parsed.data.accepts, this.options.assetAddresses);
243
+ if (!resolved) throw new UnsupportedRequirementError(url);
244
+ const taskContext = this.task.getStore() ?? this.options.taskContext;
245
+ const submission = toIntentSubmission(resolved, url, this.options.agentId, taskContext);
246
+ const { intent, decision } = await this.client.evaluate(submission);
247
+ if (decision.outcome !== "allow") {
248
+ const receipt = this.record(intent, decision, url, init);
249
+ if (this.options.onBlocked === "respond") return blockedResponse(receipt);
250
+ throw new PaymentBlockedError(intent, decision, receipt);
251
+ }
252
+ if (!this.options.payer) {
253
+ const receipt = this.record(intent, decision, url, init);
254
+ this.pendingByUrl.set(url, receipt);
255
+ return res;
256
+ }
257
+ const paymentHeader = await this.options.payer(resolved.requirement, intent, decision);
258
+ const retry = await inner(input, withHeader(input, init, "X-PAYMENT", paymentHeader));
259
+ this.record(intent, decision, url, init, parseSettlement(retry));
260
+ return retry;
261
+ };
262
+ }
263
+ record(intent, decision, url, init, settlement) {
264
+ const receipt = Receipt.parse({
265
+ id: newId("rcp"),
266
+ agentId: intent.agentId,
267
+ intentId: intent.id,
268
+ decisionId: decision.id,
269
+ outcome: decision.outcome,
270
+ url,
271
+ method: init?.method ?? "GET",
272
+ vendorHost: intent.vendor.host,
273
+ amount: intent.amount,
274
+ asset: intent.asset,
275
+ chain: intent.chain,
276
+ taskContext: intent.taskContext,
277
+ reason: decision.reason,
278
+ settlement,
279
+ createdAt: /* @__PURE__ */ new Date()
280
+ });
281
+ this.log.push(receipt);
282
+ this.options.onReceipt?.(receipt);
283
+ return receipt;
284
+ }
285
+ settlePending(url, res) {
286
+ const receipt = this.pendingByUrl.get(url);
287
+ if (!receipt || !res.ok) return;
288
+ const settlement = parseSettlement(res);
289
+ if (settlement) receipt.settlement = settlement;
290
+ this.pendingByUrl.delete(url);
291
+ }
292
+ };
293
+ function createGuard(options) {
294
+ return new Guard(options);
295
+ }
296
+ function requestUrl(input) {
297
+ if (typeof input === "string") return input;
298
+ if (input instanceof URL) return input.toString();
299
+ return input.url;
300
+ }
301
+ function readHeader(input, init, name) {
302
+ const source = init?.headers ?? (input instanceof Request ? input.headers : void 0);
303
+ return source === void 0 ? null : new Headers(source).get(name);
304
+ }
305
+ function withHeader(input, init, name, value) {
306
+ const headers = new Headers(
307
+ init?.headers ?? (input instanceof Request ? input.headers : void 0)
308
+ );
309
+ headers.set(name, value);
310
+ return { ...init, headers };
311
+ }
312
+ function parseSettlement(res) {
313
+ const raw = res.headers.get("X-PAYMENT-RESPONSE");
314
+ if (raw === null) return void 0;
315
+ let payload;
316
+ try {
317
+ payload = JSON.parse(raw);
318
+ } catch {
319
+ try {
320
+ payload = JSON.parse(Buffer.from(raw, "base64").toString("utf8"));
321
+ } catch {
322
+ return { raw };
323
+ }
324
+ }
325
+ const record = typeof payload === "object" && payload !== null ? payload : {};
326
+ return {
327
+ txHash: typeof record["transaction"] === "string" ? record["transaction"] : void 0,
328
+ networkId: typeof record["network"] === "string" ? record["network"] : void 0,
329
+ raw
330
+ };
331
+ }
332
+ function blockedResponse(receipt) {
333
+ return new Response(
334
+ JSON.stringify({
335
+ error: "payment_blocked_by_rein",
336
+ outcome: receipt.outcome,
337
+ reason: receipt.reason,
338
+ intentId: receipt.intentId,
339
+ decisionId: receipt.decisionId,
340
+ receiptId: receipt.id
341
+ }),
342
+ {
343
+ status: 402,
344
+ headers: { "content-type": "application/json", "x-rein-outcome": receipt.outcome }
345
+ }
346
+ );
347
+ }
348
+
349
+ export { EngineClient, EngineError, Guard, PaymentBlockedError, PaymentRequired, PaymentRequirement, ReinError, UnsupportedRequirementError, atomicToDecimal, createGuard, decimalToAtomic, networkToChain, requirementDecimals, resolveAsset, selectRequirement, toIntentSubmission };
350
+ //# sourceMappingURL=index.js.map
351
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/x402.ts","../src/guard.ts"],"names":["z","res"],"mappings":";;;;;;;AAGO,IAAM,SAAA,GAAN,cAAwB,KAAA,CAAM;AAAA,EACnC,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,OAAO,GAAA,CAAA,MAAA,CAAW,IAAA;AAAA,EACzB;AACF;AAGO,IAAM,WAAA,GAAN,cAA0B,SAAA,CAAU;AAAA,EACzC,YACW,MAAA,EACA,IAAA,EACT,OAAA,GAAU,CAAA,wBAAA,EAA2B,MAAM,CAAA,CAAA,EAC3C;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AAJJ,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAIX;AAAA,EALW,MAAA;AAAA,EACA,IAAA;AAKb;AAOO,IAAM,mBAAA,GAAN,cAAkC,SAAA,CAAU;AAAA,EACjD,WAAA,CACW,MAAA,EACA,QAAA,EACA,OAAA,EACT;AACA,IAAA,KAAA;AAAA,MACE,CAAA,wBAAA,EAA2B,OAAO,MAAM,CAAA,CAAA,EAAI,OAAO,KAAK,CAAA,IAAA,EAAO,OAAO,MAAA,CAAO,IAAI,KAC5E,QAAA,CAAS,OAAO,GAAG,QAAA,CAAS,MAAA,GAAS,KAAK,QAAA,CAAS,MAAM,MAAM,EAAE,CAAA;AAAA,KACxE;AAPS,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AACA,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAMX;AAAA,EARW,MAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAOb;AAOO,IAAM,2BAAA,GAAN,cAA0C,SAAA,CAAU;AAAA,EACzD,YAAqB,GAAA,EAAa;AAChC,IAAA,KAAA,CAAM,CAAA,kDAAA,EAAqD,GAAG,CAAA,gBAAA,CAAkB,CAAA;AAD7D,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AAAA,EAErB;AAAA,EAFqB,GAAA;AAGvB;;;ACxCA,IAAM,MAAA,GAAS,CAAA,CAAE,MAAA,CAAO,EAAE,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO,EAAG,SAAA,EAAW,CAAA,CAAE,MAAA,EAAO,EAAG,CAAA;AACrE,IAAM,gBAAA,GAAmB,EAAE,MAAA,CAAO,EAAE,QAAQ,aAAA,EAAe,QAAA,EAAU,UAAU,CAAA;AAcxE,IAAM,eAAN,MAAmB;AAAA,EACP,OAAA;AAAA,EACA,SAAA;AAAA,EAEjB,YAAY,OAAA,EAA8B;AACxC,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA,CAAQ,OAAA,CAAQ,OAAA,CAAQ,QAAQ,EAAE,CAAA;AACjD,IAAA,MAAM,CAAA,GAAI,OAAA,CAAQ,KAAA,IAAS,UAAA,CAAW,KAAA;AACtC,IAAA,IAAA,CAAK,YAAY,CAAC,KAAA,EAAO,IAAA,KAAS,CAAA,CAAE,OAAO,IAAI,CAAA;AAAA,EACjD;AAAA,EAEA,MAAc,OAAA,CACZ,MAAA,EACA,IAAA,EACA,QACA,IAAA,EACY;AACZ,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,SAAA,CAAU,GAAG,IAAA,CAAK,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI;AAAA,MACzD,MAAA;AAAA,MACA,SAAS,IAAA,KAAS,MAAA,GAAY,MAAA,GAAY,EAAE,gBAAgB,kBAAA,EAAmB;AAAA,MAC/E,MAAM,IAAA,KAAS,MAAA,GAAY,MAAA,GAAY,IAAA,CAAK,UAAU,IAAI;AAAA,KAC3D,CAAA;AACD,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAM,OAAA,GAAU,MAAM,GAAA,CACnB,KAAA,GACA,IAAA,EAAK,CACL,KAAA,CAAM,MAAM,IAAI,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM,MAAS,CAAC,CAAA;AAChD,MAAA,MAAM,IAAI,WAAA,CAAY,GAAA,CAAI,MAAA,EAAQ,OAAO,CAAA;AAAA,IAC3C;AACA,IAAA,IAAI,IAAI,MAAA,KAAW,GAAA,EAAK,OAAO,MAAA,CAAO,MAAM,MAAS,CAAA;AACrD,IAAA,OAAO,MAAA,CAAO,KAAA,CAAM,MAAM,GAAA,CAAI,MAAM,CAAA;AAAA,EACtC;AAAA,EAEA,MAAA,GAA0C;AACxC,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,SAAA,EAAW,MAAM,CAAA;AAAA,EAC9C;AAAA,EAEA,cAAc,KAAA,EAKK;AACjB,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAQ,YAAA,EAAc,OAAO,KAAK,CAAA;AAAA,EACxD;AAAA,EAEA,UAAA,GAA+B;AAC7B,IAAA,OAAO,KAAK,OAAA,CAAQ,KAAA,EAAO,cAAc,CAAA,CAAE,KAAA,CAAM,KAAK,CAAC,CAAA;AAAA,EACzD;AAAA,EAEA,OAAO,OAAA,EAAgC;AACrC,IAAA,OAAO,IAAA,CAAK,QAAQ,MAAA,EAAQ,CAAA,WAAA,EAAc,OAAO,CAAA,OAAA,CAAA,EAAW,CAAA,CAAE,MAAM,CAAA;AAAA,EACtE;AAAA,EAEA,SAAS,OAAA,EAAgC;AACvC,IAAA,OAAO,IAAA,CAAK,QAAQ,MAAA,EAAQ,CAAA,WAAA,EAAc,OAAO,CAAA,SAAA,CAAA,EAAa,CAAA,CAAE,MAAM,CAAA;AAAA,EACxE;AAAA,EAEA,UAAU,MAAA,EAAiD;AACzD,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAQ,cAAA,EAAgB,QAAQ,MAAM,CAAA;AAAA,EAC5D;AAAA,EAEA,YAAA,GAAkC;AAChC,IAAA,OAAO,KAAK,OAAA,CAAQ,KAAA,EAAO,gBAAgB,CAAA,CAAE,KAAA,CAAM,MAAM,CAAC,CAAA;AAAA,EAC5D;AAAA;AAAA,EAGA,SAAS,UAAA,EAAyD;AAChE,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAQ,cAAA,EAAgB,kBAAkB,UAAU,CAAA;AAAA,EAC1E;AAAA,EAEA,SAAA,GAAiC;AAC/B,IAAA,OAAO,KAAK,OAAA,CAAQ,KAAA,EAAO,iBAAiB,CAAA,CAAE,KAAA,CAAM,QAAQ,CAAC,CAAA;AAAA,EAC/D;AACF;ACtFO,IAAM,kBAAA,GAAqBA,EAAE,MAAA,CAAO;AAAA,EACzC,MAAA,EAAQA,EAAE,MAAA,EAAO;AAAA,EACjB,OAAA,EAASA,EAAE,MAAA,EAAO;AAAA;AAAA,EAElB,mBAAmBA,CAAAA,CAAE,MAAA,EAAO,CAAE,KAAA,CAAM,SAAS,yCAAyC,CAAA;AAAA,EACtF,QAAA,EAAUA,CAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC9B,WAAA,EAAaA,CAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACjC,QAAA,EAAUA,CAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC9B,KAAA,EAAOA,CAAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,EACvB,iBAAA,EAAmBA,CAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA;AAAA,EAEvC,KAAA,EAAOA,CAAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,EACvB,OAAOA,CAAAA,CAAE,MAAA,CAAOA,EAAE,OAAA,EAAS,EAAE,QAAA;AAC/B,CAAC;AAIM,IAAM,eAAA,GAAkBA,EAAE,MAAA,CAAO;AAAA,EACtC,WAAA,EAAaA,EAAE,MAAA,EAAO;AAAA,EACtB,SAASA,CAAAA,CAAE,KAAA,CAAM,kBAAkB,CAAA,CAAE,IAAI,CAAC,CAAA;AAAA,EAC1C,KAAA,EAAOA,CAAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AACpB,CAAC;AAQD,IAAM,gBAAA,GAA0C;AAAA,EAC9C,IAAA,EAAM,MAAA;AAAA,EACN,cAAA,EAAgB,MAAA;AAAA,EAChB,aAAA,EAAe,MAAA;AAAA,EACf,cAAA,EAAgB,MAAA;AAAA,EAChB,MAAA,EAAQ,QAAA;AAAA,EACR,eAAA,EAAiB,QAAA;AAAA,EACjB,OAAA,EAAS,SAAA;AAAA,EACT,cAAA,EAAgB,SAAA;AAAA,EAChB,GAAA,EAAK,KAAA;AAAA,EACL,GAAA,EAAK;AACP,CAAA;AAGA,IAAM,qBAAA,GAA+C;AAAA,EACnD,4CAAA,EAA8C,MAAA;AAAA;AAAA,EAC9C,4CAAA,EAA8C,MAAA;AAAA;AAAA,EAC9C,4CAAA,EAA8C;AAAA;AAChD,CAAA;AAEO,SAAS,eAAe,OAAA,EAAoC;AACjE,EAAA,OAAO,gBAAA,CAAiB,OAAA,CAAQ,WAAA,EAAa,CAAA;AAC/C;AAQO,SAAS,YAAA,CACd,WAAA,EACA,cAAA,GAAwC,EAAC,EACtB;AACnB,EAAA,MAAM,SAAS,KAAA,CAAM,SAAA,CAAU,WAAA,CAAY,KAAA,CAAM,aAAa,CAAA;AAC9D,EAAA,IAAI,MAAA,CAAO,OAAA,EAAS,OAAO,MAAA,CAAO,IAAA;AAElC,EAAA,MAAM,MAAA,GAAS,WAAA,CAAY,KAAA,GAAQ,QAAQ,CAAA;AAC3C,EAAA,IAAI,OAAO,WAAW,QAAA,EAAU;AAC9B,IAAA,MAAM,SAAA,GAAY,KAAA,CAAM,SAAA,CAAU,MAAA,CAAO,aAAa,CAAA;AACtD,IAAA,IAAI,SAAA,CAAU,OAAA,EAAS,OAAO,SAAA,CAAU,IAAA;AAAA,EAC1C;AAEA,EAAA,OACE,eAAe,WAAA,CAAY,KAAK,KAChC,cAAA,CAAe,WAAA,CAAY,MAAM,WAAA,EAAa,CAAA,IAC9C,qBAAA,CAAsB,YAAY,KAAK,CAAA,IACvC,sBAAsB,WAAA,CAAY,KAAA,CAAM,aAAa,CAAA;AAEzD;AAOO,SAAS,eAAA,CAAgB,QAAgB,QAAA,EAA0B;AACxE,EAAA,IAAI,CAAC,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAA,QAAS,IAAI,SAAA,CAAU,CAAA,uBAAA,EAA0B,MAAM,CAAA,CAAE,CAAA;AACjF,EAAA,IAAI,aAAa,CAAA,EAAG,OAAO,MAAA,CAAO,OAAA,CAAQ,aAAa,EAAE,CAAA;AACzD,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,QAAA,CAAS,QAAA,GAAW,GAAG,GAAG,CAAA;AAChD,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,MAAA,CAAO,SAAS,QAAQ,CAAA,CAAE,OAAA,CAAQ,WAAA,EAAa,EAAE,CAAA;AACjF,EAAA,MAAM,QAAA,GAAW,OAAO,KAAA,CAAM,MAAA,CAAO,SAAS,QAAQ,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AACzE,EAAA,OAAO,SAAS,MAAA,GAAS,CAAA,GAAI,GAAG,OAAO,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAA,GAAK,OAAA;AAC1D;AAQO,SAAS,eAAA,CAAgB,SAAiB,QAAA,EAA0B;AACzE,EAAA,IAAI,CAAC,eAAA,CAAgB,IAAA,CAAK,OAAO,CAAA,QAAS,IAAI,SAAA,CAAU,CAAA,wBAAA,EAA2B,OAAO,CAAA,CAAE,CAAA;AAC5F,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAA;AAC/B,EAAA,MAAM,UAAU,GAAA,KAAQ,EAAA,GAAK,UAAU,OAAA,CAAQ,KAAA,CAAM,GAAG,GAAG,CAAA;AAC3D,EAAA,MAAM,WAAW,GAAA,KAAQ,EAAA,GAAK,KAAK,OAAA,CAAQ,KAAA,CAAM,MAAM,CAAC,CAAA;AACxD,EAAA,IAAI,QAAA,CAAS,SAAS,QAAA,EAAU;AAC9B,IAAA,MAAM,IAAI,SAAA,CAAU,CAAA,OAAA,EAAU,OAAO,CAAA,eAAA,EAAkB,QAAQ,CAAA,gBAAA,CAAkB,CAAA;AAAA,EACnF;AACA,EAAA,MAAM,MAAA,GAAS,OAAA,GAAU,QAAA,CAAS,MAAA,CAAO,UAAU,GAAG,CAAA;AACtD,EAAA,OAAO,MAAA,CAAO,OAAA,CAAQ,WAAA,EAAa,EAAE,CAAA;AACvC;AAEO,SAAS,oBAAoB,WAAA,EAAyC;AAC3E,EAAA,MAAM,QAAA,GAAW,WAAA,CAAY,KAAA,GAAQ,UAAU,CAAA;AAC/C,EAAA,OAAO,OAAO,aAAa,QAAA,IAAY,MAAA,CAAO,UAAU,QAAQ,CAAA,IAAK,QAAA,IAAY,CAAA,GAAI,QAAA,GAAW,CAAA;AAClG;AAgBO,SAAS,iBAAA,CACd,OAAA,EACA,cAAA,GAAwC,EAAC,EACR;AACjC,EAAA,KAAA,MAAW,eAAe,OAAA,EAAS;AACjC,IAAA,IAAI,WAAA,CAAY,MAAA,CAAO,WAAA,EAAY,KAAM,OAAA,EAAS;AAClD,IAAA,MAAM,KAAA,GAAQ,cAAA,CAAe,WAAA,CAAY,OAAO,CAAA;AAChD,IAAA,IAAI,CAAC,KAAA,EAAO;AACZ,IAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,WAAA,EAAa,cAAc,CAAA;AACtD,IAAA,IAAI,CAAC,KAAA,EAAO;AACZ,IAAA,MAAM,SAAS,eAAA,CAAgB,WAAA,CAAY,iBAAA,EAAmB,mBAAA,CAAoB,WAAW,CAAC,CAAA;AAC9F,IAAA,OAAO,EAAE,WAAA,EAAa,KAAA,EAAO,KAAA,EAAO,MAAA,EAAO;AAAA,EAC7C;AACA,EAAA,OAAO,MAAA;AACT;AAcO,SAAS,kBAAA,CACd,QAAA,EACA,GAAA,EACA,OAAA,EACA,WAAA,EACkB;AAClB,EAAA,MAAM,MAAA,GAAS,IAAI,GAAA,CAAI,GAAG,CAAA;AAC1B,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,MAAM,MAAA,CAAO,IAAA;AAAA,MACb,OAAA,EAAS,SAAS,WAAA,CAAY;AAAA,KAChC;AAAA,IACA,QAAA,EAAU,QAAA,CAAS,WAAA,CAAY,QAAA,IAAY,MAAA,CAAO,QAAA;AAAA,IAClD,QAAQ,QAAA,CAAS,MAAA;AAAA,IACjB,OAAO,QAAA,CAAS,KAAA;AAAA,IAChB,OAAO,QAAA,CAAS,KAAA;AAAA,IAChB;AAAA,GACF;AACF;;;ACxHO,IAAM,QAAN,MAAY;AAAA,EACR,MAAA;AAAA,EACQ,OAAA;AAAA,EACA,SAAA;AAAA,EACA,IAAA,GAAO,IAAI,iBAAA,EAA+B;AAAA,EAC1C,MAAiB,EAAC;AAAA;AAAA,EAElB,YAAA,uBAAmB,GAAA,EAAqB;AAAA,EAEzD,YAAY,OAAA,EAAuB;AACjC,IAAA,OAAA,CAAQ,KAAA,CAAM,QAAQ,OAAO,CAAA;AAC7B,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AACf,IAAA,MAAM,CAAA,GAAI,OAAA,CAAQ,KAAA,IAAS,UAAA,CAAW,KAAA;AACtC,IAAA,IAAA,CAAK,YAAY,CAAC,KAAA,EAAO,IAAA,KAAS,CAAA,CAAE,OAAO,IAAI,CAAA;AAC/C,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,YAAA,CAAa,EAAE,OAAA,EAAS,QAAQ,SAAA,EAAW,KAAA,EAAO,OAAA,CAAQ,WAAA,EAAa,CAAA;AAAA,EAC3F;AAAA;AAAA,EAGA,QAAA,GAA+B;AAC7B,IAAA,OAAO,IAAA,CAAK,GAAA;AAAA,EACd;AAAA;AAAA,EAGA,QAAA,CAAY,SAAsB,EAAA,EAAgB;AAChD,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,EAAE,GAAG,IAAA,CAAK,OAAA,CAAQ,WAAA,EAAa,GAAG,OAAA,EAAQ,EAAG,EAAE,CAAA;AAAA,EACtE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAK,SAAA,EAAkC;AACrC,IAAA,MAAM,KAAA,GAAmB,YAAY,CAAC,KAAA,EAAO,SAAS,SAAA,CAAU,KAAA,EAAO,IAAI,CAAA,GAAI,IAAA,CAAK,SAAA;AAEpF,IAAA,OAAO,OAAO,OAAO,IAAA,KAAS;AAC5B,MAAA,MAAM,GAAA,GAAM,WAAW,KAAK,CAAA;AAG5B,MAAA,IAAI,UAAA,CAAW,KAAA,EAAO,IAAA,EAAM,WAAW,MAAM,IAAA,EAAM;AACjD,QAAA,MAAMC,IAAAA,GAAM,MAAM,KAAA,CAAM,KAAA,EAAO,IAAI,CAAA;AACnC,QAAA,IAAA,CAAK,aAAA,CAAc,KAAKA,IAAG,CAAA;AAC3B,QAAA,OAAOA,IAAAA;AAAA,MACT;AAEA,MAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,KAAA,EAAO,IAAI,CAAA;AACnC,MAAA,IAAI,GAAA,CAAI,MAAA,KAAW,GAAA,EAAK,OAAO,GAAA;AAE/B,MAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAChB,KAAA,GACA,IAAA,EAAK,CACL,KAAA,CAAM,MAAM,MAAS,CAAA;AACxB,MAAA,MAAM,MAAA,GAAS,eAAA,CAAgB,SAAA,CAAU,IAAI,CAAA;AAE7C,MAAA,IAAI,CAAC,MAAA,CAAO,OAAA,EAAS,OAAO,GAAA;AAE5B,MAAA,MAAM,WAAW,iBAAA,CAAkB,MAAA,CAAO,KAAK,OAAA,EAAS,IAAA,CAAK,QAAQ,cAAc,CAAA;AACnF,MAAA,IAAI,CAAC,QAAA,EAAU,MAAM,IAAI,4BAA4B,GAAG,CAAA;AAExD,MAAA,MAAM,cAAc,IAAA,CAAK,IAAA,CAAK,QAAA,EAAS,IAAK,KAAK,OAAA,CAAQ,WAAA;AACzD,MAAA,MAAM,aAAa,kBAAA,CAAmB,QAAA,EAAU,KAAK,IAAA,CAAK,OAAA,CAAQ,SAAS,WAAW,CAAA;AACtF,MAAA,MAAM,EAAE,QAAQ,QAAA,EAAS,GAAI,MAAM,IAAA,CAAK,MAAA,CAAO,SAAS,UAAU,CAAA;AAElE,MAAA,IAAI,QAAA,CAAS,YAAY,OAAA,EAAS;AAChC,QAAA,MAAM,UAAU,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,QAAA,EAAU,KAAK,IAAI,CAAA;AACvD,QAAA,IAAI,KAAK,OAAA,CAAQ,SAAA,KAAc,SAAA,EAAW,OAAO,gBAAgB,OAAO,CAAA;AACxE,QAAA,MAAM,IAAI,mBAAA,CAAoB,MAAA,EAAQ,QAAA,EAAU,OAAO,CAAA;AAAA,MACzD;AAEA,MAAA,IAAI,CAAC,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO;AAGvB,QAAA,MAAM,UAAU,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,QAAA,EAAU,KAAK,IAAI,CAAA;AACvD,QAAA,IAAA,CAAK,YAAA,CAAa,GAAA,CAAI,GAAA,EAAK,OAAO,CAAA;AAClC,QAAA,OAAO,GAAA;AAAA,MACT;AAEA,MAAA,MAAM,aAAA,GAAgB,MAAM,IAAA,CAAK,OAAA,CAAQ,MAAM,QAAA,CAAS,WAAA,EAAa,QAAQ,QAAQ,CAAA;AACrF,MAAA,MAAM,KAAA,GAAQ,MAAM,KAAA,CAAM,KAAA,EAAO,WAAW,KAAA,EAAO,IAAA,EAAM,WAAA,EAAa,aAAa,CAAC,CAAA;AACpF,MAAA,IAAA,CAAK,OAAO,MAAA,EAAQ,QAAA,EAAU,KAAK,IAAA,EAAM,eAAA,CAAgB,KAAK,CAAC,CAAA;AAC/D,MAAA,OAAO,KAAA;AAAA,IACT,CAAA;AAAA,EACF;AAAA,EAEQ,MAAA,CACN,MAAA,EACA,QAAA,EACA,GAAA,EACA,MACA,UAAA,EACS;AACT,IAAA,MAAM,OAAA,GAAU,QAAQ,KAAA,CAAM;AAAA,MAC5B,EAAA,EAAI,MAAM,KAAK,CAAA;AAAA,MACf,SAAS,MAAA,CAAO,OAAA;AAAA,MAChB,UAAU,MAAA,CAAO,EAAA;AAAA,MACjB,YAAY,QAAA,CAAS,EAAA;AAAA,MACrB,SAAS,QAAA,CAAS,OAAA;AAAA,MAClB,GAAA;AAAA,MACA,MAAA,EAAQ,MAAM,MAAA,IAAU,KAAA;AAAA,MACxB,UAAA,EAAY,OAAO,MAAA,CAAO,IAAA;AAAA,MAC1B,QAAQ,MAAA,CAAO,MAAA;AAAA,MACf,OAAO,MAAA,CAAO,KAAA;AAAA,MACd,OAAO,MAAA,CAAO,KAAA;AAAA,MACd,aAAa,MAAA,CAAO,WAAA;AAAA,MACpB,QAAQ,QAAA,CAAS,MAAA;AAAA,MACjB,UAAA;AAAA,MACA,SAAA,sBAAe,IAAA;AAAK,KACrB,CAAA;AACD,IAAA,IAAA,CAAK,GAAA,CAAI,KAAK,OAAO,CAAA;AACrB,IAAA,IAAA,CAAK,OAAA,CAAQ,YAAY,OAAO,CAAA;AAChC,IAAA,OAAO,OAAA;AAAA,EACT;AAAA,EAEQ,aAAA,CAAc,KAAa,GAAA,EAAqB;AACtD,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,YAAA,CAAa,GAAA,CAAI,GAAG,CAAA;AACzC,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,GAAA,CAAI,EAAA,EAAI;AACzB,IAAA,MAAM,UAAA,GAAa,gBAAgB,GAAG,CAAA;AACtC,IAAA,IAAI,UAAA,UAAoB,UAAA,GAAa,UAAA;AACrC,IAAA,IAAA,CAAK,YAAA,CAAa,OAAO,GAAG,CAAA;AAAA,EAC9B;AACF;AAGO,SAAS,YAAY,OAAA,EAA8B;AACxD,EAAA,OAAO,IAAI,MAAM,OAAO,CAAA;AAC1B;AAEA,SAAS,WAAW,KAAA,EAAuC;AACzD,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,EAAU,OAAO,KAAA;AACtC,EAAA,IAAI,KAAA,YAAiB,GAAA,EAAK,OAAO,KAAA,CAAM,QAAA,EAAS;AAChD,EAAA,OAAO,KAAA,CAAM,GAAA;AACf;AAEA,SAAS,UAAA,CACP,KAAA,EACA,IAAA,EACA,IAAA,EACe;AACf,EAAA,MAAM,SAAS,IAAA,EAAM,OAAA,KAAY,KAAA,YAAiB,OAAA,GAAU,MAAM,OAAA,GAAU,MAAA,CAAA;AAC5E,EAAA,OAAO,MAAA,KAAW,SAAY,IAAA,GAAO,IAAI,QAAQ,MAAM,CAAA,CAAE,IAAI,IAAI,CAAA;AACnE;AAEA,SAAS,UAAA,CACP,KAAA,EACA,IAAA,EACA,IAAA,EACA,KAAA,EACa;AACb,EAAA,MAAM,UAAU,IAAI,OAAA;AAAA,IAClB,IAAA,EAAM,OAAA,KAAY,KAAA,YAAiB,OAAA,GAAU,MAAM,OAAA,GAAU,MAAA;AAAA,GAC/D;AACA,EAAA,OAAA,CAAQ,GAAA,CAAI,MAAM,KAAK,CAAA;AACvB,EAAA,OAAO,EAAE,GAAG,IAAA,EAAM,OAAA,EAAQ;AAC5B;AAMA,SAAS,gBAAgB,GAAA,EAA8C;AACrE,EAAA,MAAM,GAAA,GAAM,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,oBAAoB,CAAA;AAChD,EAAA,IAAI,GAAA,KAAQ,MAAM,OAAO,MAAA;AACzB,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI;AACF,IAAA,OAAA,GAAU,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA,EAC1B,CAAA,CAAA,MAAQ;AACN,IAAA,IAAI;AACF,MAAA,OAAA,GAAU,IAAA,CAAK,MAAM,MAAA,CAAO,IAAA,CAAK,KAAK,QAAQ,CAAA,CAAE,QAAA,CAAS,MAAM,CAAC,CAAA;AAAA,IAClE,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,EAAE,GAAA,EAAI;AAAA,IACf;AAAA,EACF;AACA,EAAA,MAAM,SACJ,OAAO,OAAA,KAAY,YAAY,OAAA,KAAY,IAAA,GAAQ,UAAsC,EAAC;AAC5F,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,OAAO,MAAA,CAAO,aAAa,MAAM,QAAA,GAAW,MAAA,CAAO,aAAa,CAAA,GAAI,MAAA;AAAA,IAC5E,SAAA,EAAW,OAAO,MAAA,CAAO,SAAS,MAAM,QAAA,GAAW,MAAA,CAAO,SAAS,CAAA,GAAI,MAAA;AAAA,IACvE;AAAA,GACF;AACF;AAEA,SAAS,gBAAgB,OAAA,EAA4B;AACnD,EAAA,OAAO,IAAI,QAAA;AAAA,IACT,KAAK,SAAA,CAAU;AAAA,MACb,KAAA,EAAO,yBAAA;AAAA,MACP,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,UAAU,OAAA,CAAQ,QAAA;AAAA,MAClB,YAAY,OAAA,CAAQ,UAAA;AAAA,MACpB,WAAW,OAAA,CAAQ;AAAA,KACpB,CAAA;AAAA,IACD;AAAA,MACE,MAAA,EAAQ,GAAA;AAAA,MACR,SAAS,EAAE,cAAA,EAAgB,kBAAA,EAAoB,gBAAA,EAAkB,QAAQ,OAAA;AAAQ;AACnF,GACF;AACF","file":"index.js","sourcesContent":["import type { Decision, PaymentIntent, Receipt } from '@reinconsole/core';\n\n/** Base class for everything the SDK throws, so callers can catch broadly. */\nexport class ReinError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\n/** The policy engine answered with a non-2xx status. */\nexport class EngineError extends ReinError {\n constructor(\n readonly status: number,\n readonly body: unknown,\n message = `policy engine responded ${status}`,\n ) {\n super(message);\n }\n}\n\n/**\n * The engine evaluated the intent and did NOT allow it (deny or escalate —\n * v0.1 blocks both; escalation approval flows land later). The payment was\n * never constructed, so no funds moved. Carries the full evidence trail.\n */\nexport class PaymentBlockedError extends ReinError {\n constructor(\n readonly intent: PaymentIntent,\n readonly decision: Decision,\n readonly receipt: Receipt,\n ) {\n super(\n `rein blocked payment of ${intent.amount} ${intent.asset} to ${intent.vendor.host}: ` +\n `${decision.outcome}${decision.reason ? ` (${decision.reason})` : ''}`,\n );\n }\n}\n\n/**\n * The vendor's 402 was x402-shaped but offered no requirement the guard can\n * govern (unknown scheme/network/asset). The guard fails closed rather than\n * letting an ungoverned payment through.\n */\nexport class UnsupportedRequirementError extends ReinError {\n constructor(readonly url: string) {\n super(`no supported x402 payment requirement in 402 from ${url}; failing closed`);\n }\n}\n","import { z } from 'zod';\nimport { Agent, Decision, PaymentIntent, Policy } from '@reinconsole/core';\nimport { EngineError } from './errors.js';\nimport type { IntentSubmission } from './x402.js';\n\n/** Any fetch-compatible function (global fetch, undici, or a test double). */\nexport type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;\n\nconst Health = z.object({ status: z.string(), publicKey: z.string() });\nconst EvaluateResponse = z.object({ intent: PaymentIntent, decision: Decision });\nexport type EvaluateResponse = z.infer<typeof EvaluateResponse>;\n\nexport interface EngineClientOptions {\n /** Base URL of the policy engine, e.g. \"http://localhost:8787\". */\n baseUrl: string;\n /** Override the transport (tests, custom agents). Defaults to global fetch. */\n fetch?: FetchLike;\n}\n\n/**\n * Thin typed client for the policy-engine HTTP API. Every response is parsed\n * through the @reinconsole/core schemas, so wire drift fails loudly at the boundary.\n */\nexport class EngineClient {\n private readonly baseUrl: string;\n private readonly fetchImpl: FetchLike;\n\n constructor(options: EngineClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, '');\n const f = options.fetch ?? globalThis.fetch;\n this.fetchImpl = (input, init) => f(input, init);\n }\n\n private async request<T>(\n method: string,\n path: string,\n schema: z.ZodType<T, z.ZodTypeDef, unknown>,\n body?: unknown,\n ): Promise<T> {\n const res = await this.fetchImpl(`${this.baseUrl}${path}`, {\n method,\n headers: body === undefined ? undefined : { 'content-type': 'application/json' },\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n if (!res.ok) {\n const payload = await res\n .clone()\n .json()\n .catch(() => res.text().catch(() => undefined));\n throw new EngineError(res.status, payload);\n }\n if (res.status === 204) return schema.parse(undefined);\n return schema.parse(await res.json());\n }\n\n health(): Promise<z.infer<typeof Health>> {\n return this.request('GET', '/health', Health);\n }\n\n registerAgent(input: {\n orgId: string;\n name: string;\n erc8004Id?: string;\n wallets?: Agent['wallets'];\n }): Promise<Agent> {\n return this.request('POST', '/v1/agents', Agent, input);\n }\n\n listAgents(): Promise<Agent[]> {\n return this.request('GET', '/v1/agents', z.array(Agent));\n }\n\n freeze(agentId: string): Promise<void> {\n return this.request('POST', `/v1/agents/${agentId}/freeze`, z.void());\n }\n\n unfreeze(agentId: string): Promise<void> {\n return this.request('POST', `/v1/agents/${agentId}/unfreeze`, z.void());\n }\n\n addPolicy(policy: z.input<typeof Policy>): Promise<Policy> {\n return this.request('POST', '/v1/policies', Policy, policy);\n }\n\n listPolicies(): Promise<Policy[]> {\n return this.request('GET', '/v1/policies', z.array(Policy));\n }\n\n /** The hot path: submit an intent, get the normalized intent + signed decision. */\n evaluate(submission: IntentSubmission): Promise<EvaluateResponse> {\n return this.request('POST', '/v1/evaluate', EvaluateResponse, submission);\n }\n\n decisions(): Promise<Decision[]> {\n return this.request('GET', '/v1/decisions', z.array(Decision));\n }\n}\n","import { z } from 'zod';\nimport { Chain, Asset, type TaskContext, type Vendor } from '@reinconsole/core';\n\n/**\n * x402 wire shapes (spec v1), mock-first: these schemas are what Rein's mock\n * facilitator and tests speak today, and they track the published x402 spec so\n * real vendors parse identically when the live integration lands.\n */\n\n/** One way to pay, as offered inside a 402 response body. */\nexport const PaymentRequirement = z.object({\n scheme: z.string(),\n network: z.string(),\n /** Amount in the asset's atomic units (e.g. \"10000\" = 0.01 USDC at 6 decimals). */\n maxAmountRequired: z.string().regex(/^\\d+$/, 'atomic amount must be an integer string'),\n resource: z.string().optional(),\n description: z.string().optional(),\n mimeType: z.string().optional(),\n payTo: z.string().min(1),\n maxTimeoutSeconds: z.number().optional(),\n /** Token contract address — or, mock-first, a plain symbol like \"USDC\". */\n asset: z.string().min(1),\n extra: z.record(z.unknown()).optional(),\n});\nexport type PaymentRequirement = z.infer<typeof PaymentRequirement>;\n\n/** The full 402 body a paywalled vendor returns. */\nexport const PaymentRequired = z.object({\n x402Version: z.number(),\n accepts: z.array(PaymentRequirement).min(1),\n error: z.string().optional(),\n});\nexport type PaymentRequired = z.infer<typeof PaymentRequired>;\n\n/**\n * x402 network ids → the chains Rein governs. Testnets map to their mainnet.\n * Both v1 names (\"base-sepolia\") and v2 CAIP-2 ids (\"eip155:84532\") appear in\n * the wild, so the guard accepts either.\n */\nconst NETWORK_TO_CHAIN: Record<string, Chain> = {\n base: 'base',\n 'base-sepolia': 'base',\n 'eip155:8453': 'base',\n 'eip155:84532': 'base',\n solana: 'solana',\n 'solana-devnet': 'solana',\n polygon: 'polygon',\n 'polygon-amoy': 'polygon',\n bnb: 'bnb',\n bsc: 'bnb',\n};\n\n/** Canonical stablecoin contract addresses (EVM keys lowercased). */\nconst KNOWN_ASSET_ADDRESSES: Record<string, Asset> = {\n '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913': 'USDC', // USDC on Base\n '0x036cbd53842c5426634e7929541ec2318f3dcf7e': 'USDC', // USDC on Base Sepolia\n EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v: 'USDC', // USDC on Solana\n};\n\nexport function networkToChain(network: string): Chain | undefined {\n return NETWORK_TO_CHAIN[network.toLowerCase()];\n}\n\n/**\n * Resolve a requirement's asset to a symbol Rein knows. Tries, in order: the\n * asset field as a literal symbol (mock-first), `extra.symbol`, then known\n * contract addresses (exact case for Solana, lowercased for EVM), then any\n * caller-supplied address map.\n */\nexport function resolveAsset(\n requirement: PaymentRequirement,\n extraAddresses: Record<string, Asset> = {},\n): Asset | undefined {\n const direct = Asset.safeParse(requirement.asset.toUpperCase());\n if (direct.success) return direct.data;\n\n const symbol = requirement.extra?.['symbol'];\n if (typeof symbol === 'string') {\n const fromExtra = Asset.safeParse(symbol.toUpperCase());\n if (fromExtra.success) return fromExtra.data;\n }\n\n return (\n extraAddresses[requirement.asset] ??\n extraAddresses[requirement.asset.toLowerCase()] ??\n KNOWN_ASSET_ADDRESSES[requirement.asset] ??\n KNOWN_ASSET_ADDRESSES[requirement.asset.toLowerCase()]\n );\n}\n\n/**\n * Convert an atomic-unit amount to a normalized decimal string without floats,\n * e.g. (\"10000\", 6) -> \"0.01\". Stablecoins Rein supports all use 6 decimals;\n * a requirement can override via `extra.decimals`.\n */\nexport function atomicToDecimal(atomic: string, decimals: number): string {\n if (!/^\\d+$/.test(atomic)) throw new TypeError(`invalid atomic amount: ${atomic}`);\n if (decimals === 0) return atomic.replace(/^0+(?=\\d)/, '');\n const digits = atomic.padStart(decimals + 1, '0');\n const intPart = digits.slice(0, digits.length - decimals).replace(/^0+(?=\\d)/, '');\n const fracPart = digits.slice(digits.length - decimals).replace(/0+$/, '');\n return fracPart.length > 0 ? `${intPart}.${fracPart}` : intPart;\n}\n\n/**\n * The inverse of {@link atomicToDecimal}: a human-unit decimal string to atomic\n * units without floats, e.g. (\"0.05\", 6) -> \"50000\". Throws if the value has\n * more fraction digits than the asset carries (sub-atomic precision is a\n * pricing bug, not something to round silently).\n */\nexport function decimalToAtomic(decimal: string, decimals: number): string {\n if (!/^\\d+(\\.\\d+)?$/.test(decimal)) throw new TypeError(`invalid decimal amount: ${decimal}`);\n const dot = decimal.indexOf('.');\n const intPart = dot === -1 ? decimal : decimal.slice(0, dot);\n const fracPart = dot === -1 ? '' : decimal.slice(dot + 1);\n if (fracPart.length > decimals) {\n throw new TypeError(`amount ${decimal} has more than ${decimals} fraction digits`);\n }\n const atomic = intPart + fracPart.padEnd(decimals, '0');\n return atomic.replace(/^0+(?=\\d)/, '');\n}\n\nexport function requirementDecimals(requirement: PaymentRequirement): number {\n const decimals = requirement.extra?.['decimals'];\n return typeof decimals === 'number' && Number.isInteger(decimals) && decimals >= 0 ? decimals : 6;\n}\n\n/** A requirement the guard fully understood, mapped into Rein's domain. */\nexport interface ResolvedRequirement {\n requirement: PaymentRequirement;\n chain: Chain;\n asset: Asset;\n /** Human-unit decimal amount, e.g. \"0.01\". */\n amount: string;\n}\n\n/**\n * Pick the first offer the guard can govern: scheme `exact`, a network that\n * maps to a supported chain, and a resolvable asset. Returns undefined when\n * no offer qualifies — callers must treat that as fail-closed.\n */\nexport function selectRequirement(\n accepts: readonly PaymentRequirement[],\n extraAddresses: Record<string, Asset> = {},\n): ResolvedRequirement | undefined {\n for (const requirement of accepts) {\n if (requirement.scheme.toLowerCase() !== 'exact') continue;\n const chain = networkToChain(requirement.network);\n if (!chain) continue;\n const asset = resolveAsset(requirement, extraAddresses);\n if (!asset) continue;\n const amount = atomicToDecimal(requirement.maxAmountRequired, requirementDecimals(requirement));\n return { requirement, chain, asset, amount };\n }\n return undefined;\n}\n\n/** What the guard submits to the policy engine (the `/v1/evaluate` shape). */\nexport interface IntentSubmission {\n agentId: string;\n vendor: Vendor;\n resource: string;\n amount: string;\n asset: Asset;\n chain: Chain;\n taskContext?: TaskContext;\n}\n\n/** Build the evaluate payload for a resolved offer on a given request URL. */\nexport function toIntentSubmission(\n resolved: ResolvedRequirement,\n url: string,\n agentId: string,\n taskContext: TaskContext | undefined,\n): IntentSubmission {\n const parsed = new URL(url);\n return {\n agentId,\n vendor: {\n host: parsed.host,\n address: resolved.requirement.payTo,\n },\n resource: resolved.requirement.resource ?? parsed.pathname,\n amount: resolved.amount,\n asset: resolved.asset,\n chain: resolved.chain,\n taskContext,\n };\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport {\n AgentId,\n Receipt,\n newId,\n type Asset,\n type Decision,\n type PaymentIntent,\n type ReceiptSettlement,\n type TaskContext,\n} from '@reinconsole/core';\nimport { EngineClient, type FetchLike } from './client.js';\nimport { PaymentBlockedError, UnsupportedRequirementError } from './errors.js';\nimport {\n PaymentRequired,\n selectRequirement,\n toIntentSubmission,\n type PaymentRequirement,\n} from './x402.js';\n\n/**\n * Builds the `X-PAYMENT` header for an allowed intent. Mock payers and the\n * local EIP-3009 payer ignore the decision; the session-key signer tier\n * requires it — the {intent, decision} pair is the engine-signed voucher the\n * signer verifies before any key is put to work.\n */\nexport type Payer = (\n requirement: PaymentRequirement,\n intent: PaymentIntent,\n decision: Decision,\n) => string | Promise<string>;\n\nexport interface GuardOptions {\n /** Policy engine base URL, e.g. \"http://localhost:8787\". */\n engineUrl: string;\n /** The agent this guard speaks for (must be registered with the engine). */\n agentId: string;\n /** Base fetch the guard wraps by default (vendor-facing). Defaults to global fetch. */\n fetch?: FetchLike;\n /** Transport for talking to the policy engine. Defaults to global fetch. */\n engineFetch?: FetchLike;\n /** Task context attached to every intent unless overridden via withTask(). */\n taskContext?: TaskContext;\n /**\n * What a blocked payment looks like to the caller: 'throw' (default) raises\n * PaymentBlockedError; 'respond' returns a synthetic 402 JSON response, for\n * agent loops that prefer inspecting responses over catching.\n */\n onBlocked?: 'throw' | 'respond';\n /** If set, the guard settles allowed payments itself (pay + retry). */\n payer?: Payer;\n /** Extra token-address -> symbol mappings for asset resolution. */\n assetAddresses?: Record<string, Asset>;\n /** Called once per receipt, as it is recorded. */\n onReceipt?: (receipt: Receipt) => void;\n}\n\n/**\n * The Rein guard: wrap an agent's fetch once, and every x402 paywall it hits\n * is policy-checked before any payment exists, with a receipt either way.\n *\n * Layering: the guard wraps the BASE fetch, underneath any x402 payment\n * library. The first (unpaid) request surfaces the 402; the guard evaluates\n * it and either blocks — so the payment layer never sees the paywall — or\n * releases the 402 upward. The payment layer's `X-PAYMENT` retry then flows\n * back through the guard, which attaches the settlement to the receipt.\n * Alternatively, pass a `payer` and the guard completes the payment itself.\n */\nexport class Guard {\n readonly client: EngineClient;\n private readonly options: GuardOptions;\n private readonly baseFetch: FetchLike;\n private readonly task = new AsyncLocalStorage<TaskContext>();\n private readonly log: Receipt[] = [];\n /** Allowed-but-unsettled receipts awaiting the payment layer's retry, by URL. */\n private readonly pendingByUrl = new Map<string, Receipt>();\n\n constructor(options: GuardOptions) {\n AgentId.parse(options.agentId);\n this.options = options;\n const f = options.fetch ?? globalThis.fetch;\n this.baseFetch = (input, init) => f(input, init);\n this.client = new EngineClient({ baseUrl: options.engineUrl, fetch: options.engineFetch });\n }\n\n /** Every receipt this guard has recorded, oldest first. */\n receipts(): readonly Receipt[] {\n return this.log;\n }\n\n /** Run `fn` with a task context attached to every intent submitted inside it. */\n withTask<T>(context: TaskContext, fn: () => T): T {\n return this.task.run({ ...this.options.taskContext, ...context }, fn);\n }\n\n /**\n * The one-liner: returns a fetch-compatible function that enforces policy on\n * every x402 paywall. Wraps the guard's base fetch unless one is passed.\n */\n wrap(fetchImpl?: FetchLike): FetchLike {\n const inner: FetchLike = fetchImpl ? (input, init) => fetchImpl(input, init) : this.baseFetch;\n\n return async (input, init) => {\n const url = requestUrl(input);\n // A request that already carries a payment is the release leg of an\n // intent this guard allowed — pass it through and capture settlement.\n if (readHeader(input, init, 'X-PAYMENT') !== null) {\n const res = await inner(input, init);\n this.settlePending(url, res);\n return res;\n }\n\n const res = await inner(input, init);\n if (res.status !== 402) return res;\n\n const body = await res\n .clone()\n .json()\n .catch(() => undefined);\n const parsed = PaymentRequired.safeParse(body);\n // Not an x402 paywall — nothing to govern, hand it back untouched.\n if (!parsed.success) return res;\n\n const resolved = selectRequirement(parsed.data.accepts, this.options.assetAddresses);\n if (!resolved) throw new UnsupportedRequirementError(url);\n\n const taskContext = this.task.getStore() ?? this.options.taskContext;\n const submission = toIntentSubmission(resolved, url, this.options.agentId, taskContext);\n const { intent, decision } = await this.client.evaluate(submission);\n\n if (decision.outcome !== 'allow') {\n const receipt = this.record(intent, decision, url, init);\n if (this.options.onBlocked === 'respond') return blockedResponse(receipt);\n throw new PaymentBlockedError(intent, decision, receipt);\n }\n\n if (!this.options.payer) {\n // Advisory mode: release the 402 to the payment layer above us; its\n // X-PAYMENT retry will come back through and settle the receipt.\n const receipt = this.record(intent, decision, url, init);\n this.pendingByUrl.set(url, receipt);\n return res;\n }\n\n const paymentHeader = await this.options.payer(resolved.requirement, intent, decision);\n const retry = await inner(input, withHeader(input, init, 'X-PAYMENT', paymentHeader));\n this.record(intent, decision, url, init, parseSettlement(retry));\n return retry;\n };\n }\n\n private record(\n intent: PaymentIntent,\n decision: Decision,\n url: string,\n init: RequestInit | undefined,\n settlement?: ReceiptSettlement,\n ): Receipt {\n const receipt = Receipt.parse({\n id: newId('rcp'),\n agentId: intent.agentId,\n intentId: intent.id,\n decisionId: decision.id,\n outcome: decision.outcome,\n url,\n method: init?.method ?? 'GET',\n vendorHost: intent.vendor.host,\n amount: intent.amount,\n asset: intent.asset,\n chain: intent.chain,\n taskContext: intent.taskContext,\n reason: decision.reason,\n settlement,\n createdAt: new Date(),\n });\n this.log.push(receipt);\n this.options.onReceipt?.(receipt);\n return receipt;\n }\n\n private settlePending(url: string, res: Response): void {\n const receipt = this.pendingByUrl.get(url);\n if (!receipt || !res.ok) return;\n const settlement = parseSettlement(res);\n if (settlement) receipt.settlement = settlement;\n this.pendingByUrl.delete(url);\n }\n}\n\n/** Convenience factory mirroring the docs: `const guard = createGuard({...})`. */\nexport function createGuard(options: GuardOptions): Guard {\n return new Guard(options);\n}\n\nfunction requestUrl(input: string | URL | Request): string {\n if (typeof input === 'string') return input;\n if (input instanceof URL) return input.toString();\n return input.url;\n}\n\nfunction readHeader(\n input: string | URL | Request,\n init: RequestInit | undefined,\n name: string,\n): string | null {\n const source = init?.headers ?? (input instanceof Request ? input.headers : undefined);\n return source === undefined ? null : new Headers(source).get(name);\n}\n\nfunction withHeader(\n input: string | URL | Request,\n init: RequestInit | undefined,\n name: string,\n value: string,\n): RequestInit {\n const headers = new Headers(\n init?.headers ?? (input instanceof Request ? input.headers : undefined),\n );\n headers.set(name, value);\n return { ...init, headers };\n}\n\n/**\n * Decode the vendor's `X-PAYMENT-RESPONSE` header (base64 JSON per the x402\n * spec; plain JSON tolerated mock-first). Absent or undecodable -> undefined.\n */\nfunction parseSettlement(res: Response): ReceiptSettlement | undefined {\n const raw = res.headers.get('X-PAYMENT-RESPONSE');\n if (raw === null) return undefined;\n let payload: unknown;\n try {\n payload = JSON.parse(raw);\n } catch {\n try {\n payload = JSON.parse(Buffer.from(raw, 'base64').toString('utf8'));\n } catch {\n return { raw };\n }\n }\n const record =\n typeof payload === 'object' && payload !== null ? (payload as Record<string, unknown>) : {};\n return {\n txHash: typeof record['transaction'] === 'string' ? record['transaction'] : undefined,\n networkId: typeof record['network'] === 'string' ? record['network'] : undefined,\n raw,\n };\n}\n\nfunction blockedResponse(receipt: Receipt): Response {\n return new Response(\n JSON.stringify({\n error: 'payment_blocked_by_rein',\n outcome: receipt.outcome,\n reason: receipt.reason,\n intentId: receipt.intentId,\n decisionId: receipt.decisionId,\n receiptId: receipt.id,\n }),\n {\n status: 402,\n headers: { 'content-type': 'application/json', 'x-rein-outcome': receipt.outcome },\n },\n );\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@reinconsole/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Rein guard SDK — wrap your agent's fetch once and every x402 payment is policy-checked, receipted, and observable before a cent moves.",
5
+ "license": "MIT",
6
+ "homepage": "https://github.com/bugiiiii11/rein#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/bugiiiii11/rein.git",
10
+ "directory": "packages/sdk"
11
+ },
12
+ "bugs": "https://github.com/bugiiiii11/rein/issues",
13
+ "keywords": [
14
+ "rein",
15
+ "x402",
16
+ "402",
17
+ "ai-agents",
18
+ "agent-payments",
19
+ "spend-control",
20
+ "usdc",
21
+ "non-custodial"
22
+ ],
23
+ "engines": {
24
+ "node": ">=22"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "type": "module",
30
+ "sideEffects": false,
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/index.js",
35
+ "require": "./dist/index.cjs"
36
+ }
37
+ },
38
+ "main": "./dist/index.cjs",
39
+ "module": "./dist/index.js",
40
+ "types": "./dist/index.d.ts",
41
+ "files": [
42
+ "dist"
43
+ ],
44
+ "dependencies": {
45
+ "zod": "^3.24.1",
46
+ "@reinconsole/core": "^0.1.0"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^22.10.0",
50
+ "tsup": "^8.3.5",
51
+ "typescript": "^5.7.2",
52
+ "vitest": "^2.1.8",
53
+ "@reinconsole/policy-engine": "0.1.0"
54
+ },
55
+ "scripts": {
56
+ "build": "tsup",
57
+ "dev": "tsup --watch",
58
+ "typecheck": "tsc --noEmit",
59
+ "test": "vitest run",
60
+ "test:watch": "vitest",
61
+ "clean": "rimraf dist .turbo"
62
+ }
63
+ }