@belticlabs/agent-risk-sdk 0.7.0 → 0.8.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.
@@ -1,6 +1,6 @@
1
1
  import { LanguageModelMiddleware, ToolSet } from 'ai';
2
- import { S as Session } from '../session-DAN29XoB.js';
3
- import '../index-DAVKGJs5.js';
2
+ import { S as Session } from '../session-DTIzd1EQ.js';
3
+ import '../index-CBUSXzgG.js';
4
4
  import 'zod';
5
5
 
6
6
  declare function middleware(session: Session): LanguageModelMiddleware;
package/dist/ai/index.js CHANGED
@@ -3,12 +3,10 @@ import {
3
3
  } from "../chunk-JGVVQUXQ.js";
4
4
  import {
5
5
  toJson,
6
- toJsonObject
7
- } from "../chunk-FQDHFTVR.js";
8
- import {
6
+ toJsonObject,
9
7
  uuidv7
10
- } from "../chunk-UXXSE643.js";
11
- import "../chunk-FNU4CRJJ.js";
8
+ } from "../chunk-MWXXX35V.js";
9
+ import "../chunk-PDE55ZZV.js";
12
10
 
13
11
  // src/ai/index.ts
14
12
  var PARAM_KEYS = [
@@ -1,15 +1,9 @@
1
- import {
2
- summaryOf
3
- } from "./chunk-M4I3FGZG.js";
4
1
  import {
5
2
  toJsonObject
6
- } from "./chunk-FQDHFTVR.js";
7
- import {
8
- payloadDigest
9
- } from "./chunk-UXXSE643.js";
3
+ } from "./chunk-MWXXX35V.js";
10
4
  import {
11
5
  Money
12
- } from "./chunk-FNU4CRJJ.js";
6
+ } from "./chunk-PDE55ZZV.js";
13
7
 
14
8
  // src/x402/binding.ts
15
9
  var SESSION_EXTENSION = "beltic.sessionId";
@@ -19,9 +13,19 @@ function sessionIdOf(extensions) {
19
13
  return typeof v === "string" && v.length > 0 ? v : null;
20
14
  }
21
15
 
16
+ // src/core/payment-moment.ts
17
+ function summaryOf(m) {
18
+ return {
19
+ protocol: m.protocol,
20
+ payee: m.payee,
21
+ amount: { value: m.amount.value, currency: m.amount.currency },
22
+ ...m.payer ? { payer: m.payer } : {}
23
+ };
24
+ }
25
+
22
26
  // src/x402/moments.ts
23
27
  import { z } from "zod";
24
- function x402Summary(accepts, opts = {}) {
28
+ function summary(accepts, opts = {}) {
25
29
  const payer = opts.payer?.toLowerCase();
26
30
  return {
27
31
  protocol: "x402",
@@ -34,7 +38,7 @@ function x402Summary(accepts, opts = {}) {
34
38
  };
35
39
  }
36
40
  function fromAccepts(a, artifact, raw, payer) {
37
- return { ...x402Summary(a, { payer }), artifact, raw };
41
+ return { ...summary(a, { payer }), artifact, raw };
38
42
  }
39
43
  function payerOf(payload) {
40
44
  const p = payload.payload ?? {};
@@ -50,6 +54,7 @@ function payerOf(payload) {
50
54
  return typeof hit === "string" ? hit.toLowerCase() : void 0;
51
55
  }
52
56
  var x402Moments = {
57
+ summary,
53
58
  /** The 402 challenge as the buyer saw it, from the `PAYMENT-REQUIRED` header. */
54
59
  required(required) {
55
60
  return fromAccepts(required.accepts?.[0], "http-402", toJsonObject(required));
@@ -89,54 +94,10 @@ var PriceSchema = z.union([
89
94
  z.object({ amount: z.string() }).transform((p) => p.amount)
90
95
  ]).catch("0");
91
96
 
92
- // src/x402/adapter.ts
93
- function attachX402(beltic, server, http) {
94
- const inFlight = /* @__PURE__ */ new Map();
95
- http?.onProtectedRequest(async (ctx, route) => {
96
- if (ctx.paymentHeader) return;
97
- const bound = ctx.adapter.getHeader(SESSION_HEADER) ?? ctx.adapter.getHeader(SESSION_HEADER.toLowerCase());
98
- if (!bound) return;
99
- const stream = await beltic.streams.ensure(bound);
100
- const accepts = Array.isArray(route.accepts) ? route.accepts[0] : route.accepts;
101
- await stream.emit(
102
- "payment.requested",
103
- x402Moments.route(accepts, {
104
- path: ctx.path,
105
- method: ctx.method,
106
- route: JSON.parse(JSON.stringify(route))
107
- })
108
- );
109
- });
110
- server.onBeforeVerify(async (ctx) => {
111
- const payload = ctx.paymentPayload;
112
- const requirements = ctx.requirements;
113
- const stream = await beltic.streams.ensure(sessionIdOf(payload.extensions));
114
- if (stream.born === "seller")
115
- await stream.emit("payment.requested", x402Moments.requirements(requirements));
116
- const presented = x402Moments.payload(payload);
117
- await stream.emit("payment.presented", presented);
118
- inFlight.set(payloadDigest(payload), stream);
119
- const decision = await beltic.evaluate(stream.id, summaryOf(presented));
120
- if (decision.absent || decision.allowed) return;
121
- return {
122
- abort: true,
123
- reason: `BELTIC_${decision.value}`,
124
- message: decision.reasonCodes.join(",")
125
- };
126
- });
127
- server.onAfterSettle(async (ctx) => {
128
- const key = payloadDigest(ctx.paymentPayload);
129
- const stream = inFlight.get(key);
130
- if (!stream) return;
131
- inFlight.delete(key);
132
- if (ctx.result.success) await stream.close("settled", { transaction: ctx.result.transaction });
133
- });
134
- }
135
-
136
97
  export {
98
+ summaryOf,
137
99
  SESSION_EXTENSION,
138
100
  SESSION_HEADER,
139
- x402Summary,
140
- x402Moments,
141
- attachX402
101
+ sessionIdOf,
102
+ x402Moments
142
103
  };
@@ -304,6 +304,31 @@ function didKeyFromEd25519(publicKey) {
304
304
  // ../canon/src/session-id.ts
305
305
  import { randomBytes as randomBytes2 } from "crypto";
306
306
 
307
+ // src/core/json.ts
308
+ function toJson(value) {
309
+ if (value === null || value === void 0) return null;
310
+ if (typeof value === "string" || typeof value === "boolean") return value;
311
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
312
+ if (typeof value === "bigint") return value.toString();
313
+ if (value instanceof Uint8Array) return { $bytes: Buffer.from(value).toString("base64") };
314
+ if (value instanceof Date) return value.toISOString();
315
+ if (value instanceof URL) return value.toString();
316
+ if (Array.isArray(value)) return value.map(toJson);
317
+ if (typeof value === "object") {
318
+ const out = {};
319
+ for (const [k, v] of Object.entries(value)) {
320
+ if (v === void 0 || typeof v === "function") continue;
321
+ out[k] = toJson(v);
322
+ }
323
+ return out;
324
+ }
325
+ return null;
326
+ }
327
+ function toJsonObject(value) {
328
+ const j = toJson(value);
329
+ return j && typeof j === "object" && !Array.isArray(j) ? j : { value: j };
330
+ }
331
+
307
332
  export {
308
333
  toHex,
309
334
  fromHex,
@@ -313,5 +338,7 @@ export {
313
338
  memorySigner,
314
339
  Chain,
315
340
  didKeyFromEd25519,
316
- uuidv7
341
+ uuidv7,
342
+ toJson,
343
+ toJsonObject
317
344
  };
@@ -0,0 +1,57 @@
1
+ import {
2
+ SESSION_HEADER,
3
+ sessionIdOf,
4
+ summaryOf,
5
+ x402Moments
6
+ } from "./chunk-DI3LZR65.js";
7
+ import {
8
+ payloadDigest
9
+ } from "./chunk-MWXXX35V.js";
10
+
11
+ // src/x402/adapter.ts
12
+ function attachX402(beltic, server, http) {
13
+ const inFlight = /* @__PURE__ */ new Map();
14
+ http?.onProtectedRequest(async (ctx, route) => {
15
+ if (ctx.paymentHeader) return;
16
+ const bound = ctx.adapter.getHeader(SESSION_HEADER) ?? ctx.adapter.getHeader(SESSION_HEADER.toLowerCase());
17
+ if (!bound) return;
18
+ const stream = await beltic.streams.ensure(bound);
19
+ const accepts = Array.isArray(route.accepts) ? route.accepts[0] : route.accepts;
20
+ await stream.emit(
21
+ "payment.requested",
22
+ x402Moments.route(accepts, {
23
+ path: ctx.path,
24
+ method: ctx.method,
25
+ route: JSON.parse(JSON.stringify(route))
26
+ })
27
+ );
28
+ });
29
+ server.onBeforeVerify(async (ctx) => {
30
+ const payload = ctx.paymentPayload;
31
+ const requirements = ctx.requirements;
32
+ const stream = await beltic.streams.ensure(sessionIdOf(payload.extensions));
33
+ if (stream.born === "seller")
34
+ await stream.emit("payment.requested", x402Moments.requirements(requirements));
35
+ const presented = x402Moments.payload(payload);
36
+ await stream.emit("payment.presented", presented);
37
+ inFlight.set(payloadDigest(payload), stream);
38
+ const decision = await beltic.evaluate(stream.id, summaryOf(presented));
39
+ if (decision.absent || decision.allowed) return;
40
+ return {
41
+ abort: true,
42
+ reason: `BELTIC_${decision.value}`,
43
+ message: decision.reasonCodes.join(",")
44
+ };
45
+ });
46
+ server.onAfterSettle(async (ctx) => {
47
+ const key = payloadDigest(ctx.paymentPayload);
48
+ const stream = inFlight.get(key);
49
+ if (!stream) return;
50
+ inFlight.delete(key);
51
+ if (ctx.result.success) await stream.close("settled", { transaction: ctx.result.transaction });
52
+ });
53
+ }
54
+
55
+ export {
56
+ attachX402
57
+ };
@@ -350,6 +350,13 @@ var EvaluateInputSchema = z5.strictObject({
350
350
  callId: z5.string().min(1).max(256).optional()
351
351
  }).describe("evaluate input");
352
352
  var CreatePolicyInputSchema = z5.strictObject({ rules: PolicyRulesSchema }).describe("policy input");
353
+ var MAX_CATALOG_RESULTS = 20;
354
+ var CatalogQuerySchema = z5.strictObject({
355
+ q: z5.string().trim().min(1).max(200),
356
+ /** Only endpoints that accept payment on this CAIP-2 network, e.g. `eip155:8453`. */
357
+ network: z5.string().min(1).max(128).optional(),
358
+ limit: z5.coerce.number().int().min(1).max(MAX_CATALOG_RESULTS).default(8)
359
+ }).describe("catalog query");
353
360
  var ApiErrorSchema = z5.strictObject({
354
361
  error: z5.strictObject({
355
362
  code: z5.string().min(1),
@@ -541,6 +548,8 @@ export {
541
548
  PaymentSummarySchema,
542
549
  EvaluateInputSchema,
543
550
  CreatePolicyInputSchema,
551
+ MAX_CATALOG_RESULTS,
552
+ CatalogQuerySchema,
544
553
  ApiErrorSchema,
545
554
  payloadSchemaFor,
546
555
  Verdict
@@ -0,0 +1,298 @@
1
+ import { J as JsonValue, e as CatalogQuery, d as CatalogOutput, N as JsonObject, W as PaymentSummary, o as DeclaredIntent } from './index-CBUSXzgG.js';
2
+ import { A as ApiClient, S as Session, b as SessionOptions, a as Decision } from './session-DTIzd1EQ.js';
3
+ import { z } from 'zod';
4
+
5
+ /**
6
+ * One way to pay, as the seller wrote it: the fields a wallet needs are
7
+ * checked, everything else (`extra`, the scheme's own parts) is kept so the
8
+ * chosen entry can be echoed back as `accepted`.
9
+ */
10
+ declare const Requirement: z.ZodObject<{
11
+ scheme: z.ZodString;
12
+ network: z.ZodString;
13
+ asset: z.ZodString;
14
+ amount: z.ZodString;
15
+ payTo: z.ZodString;
16
+ maxTimeoutSeconds: z.ZodOptional<z.ZodNumber>;
17
+ }, z.core.$loose>;
18
+ type X402Requirement = z.infer<typeof Requirement>;
19
+ /** The whole challenge a quote answers: payment options, the resource, and the request the seller expects (`bazaar` extension). */
20
+ declare const Challenge: z.ZodPipe<z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<unknown, string>>, z.ZodObject<{
21
+ x402Version: z.ZodLiteral<2>;
22
+ accepts: z.ZodArray<z.ZodUnknown>;
23
+ resource: z.ZodCatch<z.ZodOptional<z.ZodObject<{
24
+ url: z.ZodString;
25
+ description: z.ZodCatch<z.ZodOptional<z.ZodString>>;
26
+ }, z.core.$loose>>>;
27
+ extensions: z.ZodCatch<z.ZodOptional<z.ZodObject<{
28
+ bazaar: z.ZodCatch<z.ZodOptional<z.ZodObject<{
29
+ info: z.ZodCatch<z.ZodOptional<z.ZodObject<{
30
+ input: z.ZodCatch<z.ZodOptional<z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>>>;
31
+ }, z.core.$loose>>>;
32
+ schema: z.ZodCatch<z.ZodOptional<z.ZodObject<{
33
+ properties: z.ZodCatch<z.ZodOptional<z.ZodObject<{
34
+ input: z.ZodCatch<z.ZodOptional<z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>>>;
35
+ }, z.core.$loose>>>;
36
+ }, z.core.$loose>>>;
37
+ }, z.core.$loose>>>;
38
+ }, z.core.$loose>>>;
39
+ }, z.core.$loose>>, z.ZodTransform<{
40
+ resource: {
41
+ url: string;
42
+ description: string | undefined;
43
+ } | null;
44
+ accepts: {
45
+ [x: string]: unknown;
46
+ scheme: string;
47
+ network: string;
48
+ asset: string;
49
+ amount: string;
50
+ payTo: string;
51
+ maxTimeoutSeconds?: number | undefined;
52
+ }[];
53
+ input: string | number | boolean | JsonValue[] | {
54
+ [key: string]: JsonValue;
55
+ } | null;
56
+ inputSchema: string | number | boolean | JsonValue[] | {
57
+ [key: string]: JsonValue;
58
+ } | null;
59
+ }, {
60
+ [x: string]: unknown;
61
+ x402Version: 2;
62
+ accepts: unknown[];
63
+ resource?: {
64
+ [x: string]: unknown;
65
+ url: string;
66
+ description?: string | undefined;
67
+ } | undefined;
68
+ extensions?: {
69
+ [x: string]: unknown;
70
+ bazaar?: {
71
+ [x: string]: unknown;
72
+ info?: {
73
+ [x: string]: unknown;
74
+ input?: JsonValue | undefined;
75
+ } | undefined;
76
+ schema?: {
77
+ [x: string]: unknown;
78
+ properties?: {
79
+ [x: string]: unknown;
80
+ input?: JsonValue | undefined;
81
+ } | undefined;
82
+ } | undefined;
83
+ } | undefined;
84
+ } | undefined;
85
+ }>>;
86
+ type X402Challenge = z.output<typeof Challenge>;
87
+ /** The seller's settlement of a paid request (`PAYMENT-RESPONSE`). */
88
+ declare const Settlement: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<unknown, string>>, z.ZodObject<{
89
+ success: z.ZodBoolean;
90
+ transaction: z.ZodCatch<z.ZodOptional<z.ZodString>>;
91
+ network: z.ZodCatch<z.ZodOptional<z.ZodString>>;
92
+ payer: z.ZodCatch<z.ZodOptional<z.ZodString>>;
93
+ errorReason: z.ZodCatch<z.ZodOptional<z.ZodString>>;
94
+ errorMessage: z.ZodCatch<z.ZodOptional<z.ZodString>>;
95
+ }, z.core.$loose>>;
96
+ type X402Settlement = z.output<typeof Settlement>;
97
+
98
+ /**
99
+ * x402 artifacts → protocol moments (Fraud SDK RFC › Protocol Adapter —
100
+ * x402). The moment is normalized (payee, amount, payer) so both sides of
101
+ * a purchase compare; the artifact travels whole in `raw`. For x402 the
102
+ * currency is `<network>/<asset>` (GAP-49) and the value is the atomic
103
+ * amount as the protocol carries it — `beltic.x402.summary` gives a buyer
104
+ * the same normalization for the payment it is about to evaluate, so what
105
+ * it asks about and what the fetch records are one and the same.
106
+ */
107
+
108
+ /**
109
+ * The minimum an `accepts` entry needs to become a moment; unknown parts
110
+ * are named, never dropped.
111
+ */
112
+ interface AcceptsLike {
113
+ payTo?: string | undefined;
114
+ amount?: string | undefined;
115
+ network?: string | undefined;
116
+ asset?: string | undefined;
117
+ }
118
+
119
+ /**
120
+ * x402 on the buyer half, as `beltic.x402` (Fraud SDK RFC › Protocol
121
+ * Adapter — x402):
122
+ *
123
+ * catalog where to buy: the platform's live search of public x402
124
+ * catalogs (GAP-90)
125
+ * fetch the paying fetch, observed. It sits *inside* the agent's
126
+ * paying fetch, so it sees the 402 → `payment.requested` and the
127
+ * retry carrying the payment → `payment.presented`, into which
128
+ * it injects the session binding (GAP-30). Before a request that
129
+ * presents payment leaves, the session's evidence is flushed: the
130
+ * seller evaluates as soon as it sees the payment (GAP-66). It
131
+ * never pays and never decides.
132
+ * quote the price of a request before paying it: the same request
133
+ * through `fetch` (so the 402 is on record), its challenge read —
134
+ * payment options, resource, the request the seller expects
135
+ * pay the same request again, carrying the payment the wallet
136
+ * signed for the chosen option, and the settlement it answered.
137
+ * Signing is the wallet's: the SDK never holds a key
138
+ * summary an `accepts` entry as the payment `session.decide` takes
139
+ * intent a mandate for x402 spend, its cap in the rail's currency
140
+ *
141
+ * Only x402 v2 is read (GAP-72), and nothing here loads `@x402/*`: a buyer
142
+ * is observed without installing it. The seller half is `attachX402` in
143
+ * `/x402` and the middleware in `/hono`.
144
+ */
145
+
146
+ /** What `catalog` searches for: `q` is required, `limit` defaults to 8 on the platform (1..20). */
147
+ type CatalogSearch = Omit<CatalogQuery, 'limit'> & {
148
+ limit?: number | undefined;
149
+ };
150
+ /** The request a paid resource is called with; `quote` and the payment send the same one. */
151
+ interface X402Request {
152
+ /** `GET` without a body, `POST` with one, unless given. */
153
+ method?: string | undefined;
154
+ /** Sent as JSON. */
155
+ body?: JsonValue | undefined;
156
+ }
157
+ interface X402Quote {
158
+ /** The status the resource answered the unpaid request with. */
159
+ status: number;
160
+ /** Its v2 challenge when it answered 402 with one; null when it asked for nothing or in a form this does not read. */
161
+ challenge: X402Challenge | null;
162
+ }
163
+ /** A payment the wallet signed: the option it accepted, as the challenge listed it, and the scheme's payload. */
164
+ interface X402Signed {
165
+ accepted: X402Requirement;
166
+ /** Scheme-specific, e.g. `exact` on EVM: `{ authorization, signature }` (EIP-3009). */
167
+ payload: JsonObject;
168
+ }
169
+ interface X402Payment {
170
+ /** The resource's answer to the paid request; its body is unread. */
171
+ response: Response;
172
+ /** The seller's `PAYMENT-RESPONSE`, when it sent one. */
173
+ settlement: X402Settlement | null;
174
+ }
175
+ interface X402IntentInput {
176
+ mandate: string;
177
+ network: string;
178
+ asset: string;
179
+ /** Atomic units of `asset`, as x402 carries amounts — a decimal string or a bigint, never a float. */
180
+ maxAmount: string | bigint;
181
+ validUntil: string | Date;
182
+ merchantAllowlist?: string[] | undefined;
183
+ }
184
+ declare class X402 {
185
+ private readonly api;
186
+ constructor(api: ApiClient);
187
+ /**
188
+ * x402-payable APIs matching `q`, grouped by vendor, as the platform finds
189
+ * them live in public catalogs (GAP-90); with `network`, only what can be
190
+ * paid there. `sources` says which catalogs answered. Null when the
191
+ * platform could not be reached (GAP-70); a rejected query throws.
192
+ */
193
+ catalog(search: CatalogSearch): Promise<CatalogOutput | null>;
194
+ /**
195
+ * A fetch bound to `session` that records what x402 crosses it. A request
196
+ * made while the session has no stream (the platform could not open one,
197
+ * GAP-70) goes through unrecorded.
198
+ */
199
+ fetch(session: Session): typeof globalThis.fetch;
200
+ /** The unpaid request, sent as it will be paid, and the challenge it answered. */
201
+ quote(session: Session, url: string, request?: X402Request): Promise<X402Quote>;
202
+ /**
203
+ * The request, sent as it was quoted, carrying `signed` as an x402 v2
204
+ * `PAYMENT-SIGNATURE` through `fetch` — so the presentation is recorded,
205
+ * bound to the session and flushed before it leaves (GAP-66). What the
206
+ * seller settled is read, not recorded (GAP-77).
207
+ */
208
+ pay(session: Session, url: string, signed: X402Signed, request?: X402Request): Promise<X402Payment>;
209
+ /** An `accepts` entry as the payment `session.decide` takes — the normalization the recorded moments use. */
210
+ summary(accepts: AcceptsLike | undefined, opts?: {
211
+ payer?: string | undefined;
212
+ }): PaymentSummary;
213
+ /**
214
+ * The declared intent for an x402 mandate (Fraud SDK RFC › Session ›
215
+ * `intent.declared`). The cap must be in the currency the rail's moments
216
+ * carry — `<network>/<asset>`, atomic units (GAP-49) — or the platform's
217
+ * spend detectors compare two currencies and never meet.
218
+ */
219
+ intent(input: X402IntentInput): DeclaredIntent;
220
+ /** The request as `quote` and `pay` send it: `GET` bare, `POST` with a JSON body, unless told otherwise. */
221
+ private static init;
222
+ /** An x402 header value: base64 of the JSON. */
223
+ private static encode;
224
+ }
225
+
226
+ /**
227
+ * One SDK, two halves (Fraud SDK RFC › Summary). `Beltic` is the single
228
+ * client: `session` for the buyer half, `evaluate` for whichever half is
229
+ * about to let a payment through, and `x402` for what the buyer does on
230
+ * that rail — find an API (`catalog`, GAP-90), observe the paying fetch
231
+ * (`fetch`), read a price and pay it (`quote`, `pay`), shape what it asks
232
+ * about (`summary`, `intent`). Integrations that pull an optional peer are plain functions
233
+ * behind subpath exports:
234
+ *
235
+ * @belticlabs/agent-risk-sdk/ai → middleware(session), wrapTools(session, …)
236
+ * @belticlabs/agent-risk-sdk/x402 → attachX402(beltic, …) (seller)
237
+ * @belticlabs/agent-risk-sdk/hono → belticPaymentMiddleware(beltic, …) (seller)
238
+ *
239
+ * Neither half decides risk locally: verdicts are platform-side.
240
+ *
241
+ * A client is configured or it is not constructed — `Beltic.fromEnv()`
242
+ * reads `BELTIC_*`, and there is no disabled client (GAP-78). Evidence is
243
+ * a side channel of the work it observes, so a platform outage never
244
+ * throws into that work (GAP-70): the transport waits it out, `session`
245
+ * runs without a stream, `evaluate` answers `Decision.absent()` — never an
246
+ * invented verdict — and `x402.catalog` answers `null`. What the platform
247
+ * *rejects* (a 4xx, a forked chain) is the client's fault and throws.
248
+ */
249
+
250
+ declare const SDK_VERSION = "0.8.0";
251
+ interface BelticOptions {
252
+ apiKey: string;
253
+ baseUrl: string;
254
+ /** The agent's 32-byte Ed25519 seed as 64 hex — the buyer half. Without it, `session` is unavailable; the seller half works. */
255
+ agentSeed?: string | undefined;
256
+ }
257
+ declare class Beltic {
258
+ /** x402 for the buyer: `catalog`, `fetch`, `quote`, `pay`, `summary`, `intent`. */
259
+ readonly x402: X402;
260
+ private readonly api;
261
+ private readonly transport;
262
+ /** Open handles by conversation id (GAP-84). */
263
+ private readonly sessions;
264
+ /**
265
+ * The client the environment describes: `BELTIC_API_KEY` and
266
+ * `BELTIC_BASE_URL`, both required, and `BELTIC_AGENT_SEED` (64 hex) for
267
+ * the buyer half. A missing required variable is a configuration error,
268
+ * thrown (GAP-78).
269
+ */
270
+ static fromEnv(env?: Record<string, string | undefined>): Beltic;
271
+ constructor(opts: BelticOptions);
272
+ /**
273
+ * A conversation: new without an id, resumed with one — the id `session.id()`
274
+ * answered in this or any earlier process (GAP-84). One handle per
275
+ * conversation in-process until it closes; the options count when a
276
+ * session is opened. See `Session`.
277
+ */
278
+ session(id?: string, opts?: SessionOptions): Session;
279
+ /**
280
+ * The platform's verdict on a payment — the seller's before it verifies,
281
+ * the buyer's before it presents (Fraud SDK RFC › Evaluation Client).
282
+ * Read-your-writes: the session's buffered evidence is flushed first so
283
+ * the platform judges what the caller already saw (GAP-16/88). With a `callId`
284
+ * the platform answers the decision already taken for that call anywhere
285
+ * in the conversation (GAP-84). Absent when the platform could not be
286
+ * reached (GAP-70).
287
+ */
288
+ evaluate(sessionId: string, payment: PaymentSummary, opts?: {
289
+ callId?: string | undefined;
290
+ }): Promise<Decision>;
291
+ /** Send everything buffered now — every session's — and wait for that attempt. */
292
+ flush(): Promise<void>;
293
+ shutdown(): Promise<void>;
294
+ /** `process.env` where there is a `process` (Node); `{}` on workerd, where the shell passes its `env`. */
295
+ private static processEnv;
296
+ }
297
+
298
+ export { type AcceptsLike as A, Beltic as B, type CatalogSearch as C, SDK_VERSION as S, X402 as X, type BelticOptions as a, type X402Challenge as b, type X402IntentInput as c, type X402Payment as d, type X402Quote as e, type X402Request as f, type X402Requirement as g, type X402Settlement as h, type X402Signed as i };
@@ -189,6 +189,11 @@ interface ChainHead {
189
189
  digest: Hex64;
190
190
  }
191
191
 
192
+ /**
193
+ * Request / response contracts of the Anti-Fraud API (Fraud Engine RFC › API).
194
+ * Additive fields beyond the RFC are marked with their GAP.
195
+ */
196
+
192
197
  declare const DeclaredIntentSchema: z.ZodObject<{
193
198
  mandate: z.ZodString;
194
199
  maxAmount: z.ZodOptional<z.ZodObject<{
@@ -316,6 +321,50 @@ interface CreatePolicyOutput {
316
321
  version: number;
317
322
  status: 'ACTIVE' | 'SUPERSEDED' | 'DRAFT';
318
323
  }
324
+ /** The most a catalog search answers: the upstream search caps a page at 20 (GAP-90). */
325
+ declare const MAX_CATALOG_RESULTS = 20;
326
+ /** Non-RFC surface (GAP-90): x402-payable APIs matching a query, searched live in public catalogs. */
327
+ declare const CatalogQuerySchema: z.ZodObject<{
328
+ q: z.ZodString;
329
+ network: z.ZodOptional<z.ZodString>;
330
+ limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
331
+ }, z.core.$strict>;
332
+ type CatalogQuery = z.infer<typeof CatalogQuerySchema>;
333
+ /** One way to pay an endpoint. `amount` is atomic and null when the price depends on the request. */
334
+ interface CatalogOffer {
335
+ network: string;
336
+ asset: string;
337
+ amount: string | null;
338
+ /** Null when the catalog does not name the payee; the endpoint's 402 does. */
339
+ payTo: string | null;
340
+ }
341
+ interface CatalogEndpoint {
342
+ url: string;
343
+ method: string;
344
+ description: string;
345
+ source: string;
346
+ pricing: 'fixed' | 'dynamic';
347
+ accepts: CatalogOffer[];
348
+ /** The request shape the catalog publishes (method, body type, an example), when it does. */
349
+ input?: JsonValue;
350
+ /** The JSON Schema of that input, when the catalog publishes one. */
351
+ inputSchema?: JsonValue;
352
+ }
353
+ interface CatalogVendor {
354
+ vendor: string;
355
+ endpoints: CatalogEndpoint[];
356
+ }
357
+ /** How each catalog answered; one that failed does not fail the search. */
358
+ interface CatalogSourceReport {
359
+ name: string;
360
+ ok: boolean;
361
+ count: number;
362
+ error?: string;
363
+ }
364
+ interface CatalogOutput {
365
+ vendors: CatalogVendor[];
366
+ sources: CatalogSourceReport[];
367
+ }
319
368
  /** Error envelope (GAP-20). */
320
369
  declare const ApiErrorSchema: z.ZodObject<{
321
370
  error: z.ZodObject<{
@@ -519,4 +568,4 @@ interface PayloadByKind {
519
568
  'platform.anomaly': PlatformAnomalyPayload;
520
569
  }
521
570
 
522
- export { type Sig as $, ALL_SOURCES as A, type LlmCallParams as B, type ChainHead as C, type Decision as D, type EvidenceSourceAll as E, type LlmCallStartPayload as F, type GatewayDecisionPayload as G, type Hex64 as H, type IntentDeclaredPayload as I, type JsonValue as J, type PaymentMomentPayload as K, type LlmCallEndPayload as L, MAX_BATCH_EVENTS as M, type PaymentSummary as N, PaymentSummarySchema as O, type PayloadByKind as P, type PlatformAnomalyPayload as Q, type PlatformEvidenceKind as R, type PlatformObservationPayload as S, type PromptBase as T, type PromptDelta as U, SESSION_ID_CHARS as V, SESSION_ID_PREFIX as W, SOURCE_ORDER as X, type SessionClosePayload as Y, SessionIdSchema as Z, type SessionOpenPayload as _, type ApiError as a, TimestampSchema as a0, type ToolCallEndPayload as a1, type ToolCallStartPayload as a2, type TransportGapPayload as a3, type WireEvidenceKind as a4, chainKey as a5, compareByArrival as a6, compareBySessionSource as a7, compareBySourceSeq as a8, compareRootFirst as a9, payloadSchemaFor as aa, withinHeads as ab, ApiErrorSchema as b, CreatePolicyInputSchema as c, type CreatePolicyOutput as d, type CreateSessionInput as e, CreateSessionInputSchema as f, type CreateSessionOutput as g, type DeclaredIntent as h, DeclaredIntentSchema as i, type DigestedEnvelope as j, type EvaluateInput as k, EvaluateInputSchema as l, type EvaluateOutput as m, type EventResult as n, type EvidenceAck as o, EvidenceBatchInputSchema as p, type EvidenceEnvelope as q, type EvidenceEvent as r, EvidenceEventSchema as s, type EvidenceKind as t, type EvidenceSource as u, EvidenceSourceAllSchema as v, EvidenceSourceSchema as w, Hex64Schema as x, type JsonObject as y, JsonValueSchema as z };
571
+ export { type PromptBase as $, ALL_SOURCES as A, type EvidenceKind as B, type CatalogEndpoint as C, type Decision as D, type EvidenceSourceAll as E, type EvidenceSource as F, EvidenceSourceAllSchema as G, EvidenceSourceSchema as H, type GatewayDecisionPayload as I, type JsonValue as J, type Hex64 as K, Hex64Schema as L, type IntentDeclaredPayload as M, type JsonObject as N, JsonValueSchema as O, type LlmCallEndPayload as P, type LlmCallParams as Q, type LlmCallStartPayload as R, MAX_BATCH_EVENTS as S, MAX_CATALOG_RESULTS as T, type PayloadByKind as U, type PaymentMomentPayload as V, type PaymentSummary as W, PaymentSummarySchema as X, type PlatformAnomalyPayload as Y, type PlatformEvidenceKind as Z, type PlatformObservationPayload as _, type ApiError as a, type PromptDelta as a0, SESSION_ID_CHARS as a1, SESSION_ID_PREFIX as a2, SOURCE_ORDER as a3, type SessionClosePayload as a4, SessionIdSchema as a5, type SessionOpenPayload as a6, type Sig as a7, TimestampSchema as a8, type ToolCallEndPayload as a9, type ToolCallStartPayload as aa, type TransportGapPayload as ab, type WireEvidenceKind as ac, chainKey as ad, compareByArrival as ae, compareBySessionSource as af, compareBySourceSeq as ag, compareRootFirst as ah, payloadSchemaFor as ai, withinHeads as aj, ApiErrorSchema as b, type CatalogOffer as c, type CatalogOutput as d, type CatalogQuery as e, CatalogQuerySchema as f, type CatalogSourceReport as g, type CatalogVendor as h, type ChainHead as i, CreatePolicyInputSchema as j, type CreatePolicyOutput as k, type CreateSessionInput as l, CreateSessionInputSchema as m, type CreateSessionOutput as n, type DeclaredIntent as o, DeclaredIntentSchema as p, type DigestedEnvelope as q, type EvaluateInput as r, EvaluateInputSchema as s, type EvaluateOutput as t, type EventResult as u, type EvidenceAck as v, EvidenceBatchInputSchema as w, type EvidenceEnvelope as x, type EvidenceEvent as y, EvidenceEventSchema as z };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { B as Beltic, a as BelticOptions, S as SDK_VERSION } from './client-Xn9M58OJ.js';
2
- export { B as BelticApiError, C as ChainRejectedError, D as DecideOptions, a as Decision, H as HumanDecisionInput, S as Session, b as SessionOptions } from './session-DAN29XoB.js';
3
- import './index-DAVKGJs5.js';
1
+ export { A as AcceptsLike, B as Beltic, a as BelticOptions, C as CatalogSearch, S as SDK_VERSION, X as X402, b as X402Challenge, c as X402IntentInput, d as X402Payment, e as X402Quote, f as X402Request, g as X402Requirement, h as X402Settlement, i as X402Signed } from './client-DFZL4Pt3.js';
2
+ export { B as BelticApiError, C as ChainRejectedError, D as DecideOptions, a as Decision, H as HumanDecisionInput, S as Session, b as SessionOptions } from './session-DTIzd1EQ.js';
3
+ import './index-CBUSXzgG.js';
4
4
  import 'zod';
5
5
 
6
6
  /**
package/dist/index.js CHANGED
@@ -2,8 +2,11 @@ import {
2
2
  openCall
3
3
  } from "./chunk-JGVVQUXQ.js";
4
4
  import {
5
- summaryOf
6
- } from "./chunk-M4I3FGZG.js";
5
+ SESSION_EXTENSION,
6
+ SESSION_HEADER,
7
+ summaryOf,
8
+ x402Moments
9
+ } from "./chunk-DI3LZR65.js";
7
10
  import {
8
11
  Chain,
9
12
  canonicalBytes,
@@ -12,11 +15,13 @@ import {
12
15
  memorySigner,
13
16
  sha256,
14
17
  toHex
15
- } from "./chunk-UXXSE643.js";
18
+ } from "./chunk-MWXXX35V.js";
16
19
  import {
17
20
  ApiErrorSchema,
18
- Hex64Schema
19
- } from "./chunk-FNU4CRJJ.js";
21
+ Hex64Schema,
22
+ JsonValueSchema,
23
+ Money
24
+ } from "./chunk-PDE55ZZV.js";
20
25
 
21
26
  // src/core/api-client.ts
22
27
  var TIMEOUT_MS = 1e4;
@@ -45,15 +50,26 @@ var ApiClient = class {
45
50
  "user-agent": userAgent
46
51
  };
47
52
  }
48
- async post(path, body) {
53
+ post(path, body) {
54
+ return this.request("POST", path, JSON.stringify(body));
55
+ }
56
+ /** A read; `query` entries left undefined are not sent. */
57
+ get(path, query = {}) {
58
+ const params = new URLSearchParams();
59
+ for (const [key, value] of Object.entries(query))
60
+ if (value !== void 0) params.set(key, String(value));
61
+ const search = params.toString();
62
+ return this.request("GET", search ? `${path}?${search}` : path);
63
+ }
64
+ async request(method, path, body) {
49
65
  const controller = new AbortController();
50
66
  const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
51
67
  let res;
52
68
  try {
53
69
  res = await globalThis.fetch(`${this.baseUrl}${path}`, {
54
- method: "POST",
70
+ method,
55
71
  headers: this.headers,
56
- body: JSON.stringify(body),
72
+ ...body !== void 0 ? { body } : {},
57
73
  signal: controller.signal
58
74
  });
59
75
  } catch (err) {
@@ -806,9 +822,196 @@ var Streams = class {
806
822
  }
807
823
  };
808
824
 
825
+ // src/x402/headers.ts
826
+ import { z } from "zod";
827
+ var Base64Json = z.string().regex(/^[A-Za-z0-9+/]*={0,2}$/).transform((value, ctx) => {
828
+ try {
829
+ return JSON.parse(Buffer.from(value, "base64").toString("utf8"));
830
+ } catch {
831
+ ctx.addIssue({ code: "custom", message: "not base64 JSON" });
832
+ return z.NEVER;
833
+ }
834
+ });
835
+ var Accepts = z.looseObject({
836
+ payTo: z.string().optional().catch(void 0),
837
+ amount: z.string().optional().catch(void 0),
838
+ network: z.string().optional().catch(void 0),
839
+ asset: z.string().optional().catch(void 0)
840
+ });
841
+ var PaymentRequired = Base64Json.pipe(
842
+ z.looseObject({ accepts: z.array(Accepts).optional().catch(void 0) })
843
+ );
844
+ var PaymentPayload = Base64Json.pipe(
845
+ z.looseObject({
846
+ accepted: Accepts.optional().catch(void 0),
847
+ payload: z.record(z.string(), z.unknown()).optional().catch(void 0),
848
+ extensions: z.record(z.string(), z.unknown()).optional().catch(void 0)
849
+ })
850
+ );
851
+ var Requirement = z.looseObject({
852
+ scheme: z.string().min(1),
853
+ network: z.string().min(1),
854
+ asset: z.string().min(1),
855
+ amount: z.string().regex(/^\d+$/),
856
+ payTo: z.string().min(1),
857
+ maxTimeoutSeconds: z.number().int().positive().optional()
858
+ });
859
+ var Shape = JsonValueSchema.optional().catch(void 0);
860
+ var Challenge = Base64Json.pipe(
861
+ z.looseObject({
862
+ x402Version: z.literal(2),
863
+ accepts: z.array(z.unknown()),
864
+ resource: z.looseObject({ url: z.string(), description: z.string().optional().catch(void 0) }).optional().catch(void 0),
865
+ extensions: z.looseObject({
866
+ bazaar: z.looseObject({
867
+ info: z.looseObject({ input: Shape }).optional().catch(void 0),
868
+ schema: z.looseObject({
869
+ properties: z.looseObject({ input: Shape }).optional().catch(void 0)
870
+ }).optional().catch(void 0)
871
+ }).optional().catch(void 0)
872
+ }).optional().catch(void 0)
873
+ })
874
+ ).transform((c) => ({
875
+ resource: c.resource ? { url: c.resource.url, description: c.resource.description } : null,
876
+ accepts: c.accepts.flatMap((a) => {
877
+ const r = Requirement.safeParse(a);
878
+ return r.success ? [r.data] : [];
879
+ }),
880
+ input: c.extensions?.bazaar?.info?.input ?? null,
881
+ inputSchema: c.extensions?.bazaar?.schema?.properties?.input ?? null
882
+ }));
883
+ var Settlement = Base64Json.pipe(
884
+ z.looseObject({
885
+ success: z.boolean(),
886
+ transaction: z.string().optional().catch(void 0),
887
+ network: z.string().optional().catch(void 0),
888
+ payer: z.string().optional().catch(void 0),
889
+ errorReason: z.string().optional().catch(void 0),
890
+ errorMessage: z.string().optional().catch(void 0)
891
+ })
892
+ );
893
+
894
+ // src/x402/x402.ts
895
+ var X402 = class _X402 {
896
+ constructor(api) {
897
+ this.api = api;
898
+ }
899
+ /**
900
+ * x402-payable APIs matching `q`, grouped by vendor, as the platform finds
901
+ * them live in public catalogs (GAP-90); with `network`, only what can be
902
+ * paid there. `sources` says which catalogs answered. Null when the
903
+ * platform could not be reached (GAP-70); a rejected query throws.
904
+ */
905
+ async catalog(search) {
906
+ try {
907
+ return await this.api.get("/v1/x402/catalog", {
908
+ q: search.q,
909
+ network: search.network,
910
+ limit: search.limit
911
+ });
912
+ } catch (err) {
913
+ if (!Transport.outage(err)) throw err;
914
+ console.error("[beltic]", err);
915
+ return null;
916
+ }
917
+ }
918
+ /**
919
+ * A fetch bound to `session` that records what x402 crosses it. A request
920
+ * made while the session has no stream (the platform could not open one,
921
+ * GAP-70) goes through unrecorded.
922
+ */
923
+ fetch(session) {
924
+ return async (input, init) => {
925
+ const stream = await session.stream();
926
+ if (!stream) return globalThis.fetch(input, init);
927
+ const headers = new Headers(
928
+ init?.headers ?? (input instanceof Request ? input.headers : void 0)
929
+ );
930
+ headers.set(SESSION_HEADER, stream.id);
931
+ const payload = PaymentPayload.safeParse(headers.get("PAYMENT-SIGNATURE")).data;
932
+ if (payload) {
933
+ const bound = {
934
+ ...payload,
935
+ extensions: { ...payload.extensions, [SESSION_EXTENSION]: stream.id }
936
+ };
937
+ headers.set("PAYMENT-SIGNATURE", _X402.encode(bound));
938
+ await stream.emit("payment.presented", x402Moments.payload(bound));
939
+ await stream.flush();
940
+ }
941
+ const res = await globalThis.fetch(input, { ...init, headers });
942
+ if (res.status !== 402) return res;
943
+ const required = PaymentRequired.safeParse(res.headers.get("PAYMENT-REQUIRED")).data;
944
+ if (required) await stream.emit("payment.requested", x402Moments.required(required));
945
+ return res;
946
+ };
947
+ }
948
+ /** The unpaid request, sent as it will be paid, and the challenge it answered. */
949
+ async quote(session, url, request = {}) {
950
+ const res = await this.fetch(session)(url, _X402.init(request));
951
+ await res.body?.cancel();
952
+ if (res.status !== 402) return { status: res.status, challenge: null };
953
+ const challenge = Challenge.safeParse(res.headers.get("PAYMENT-REQUIRED"));
954
+ return { status: 402, challenge: challenge.success ? challenge.data : null };
955
+ }
956
+ /**
957
+ * The request, sent as it was quoted, carrying `signed` as an x402 v2
958
+ * `PAYMENT-SIGNATURE` through `fetch` — so the presentation is recorded,
959
+ * bound to the session and flushed before it leaves (GAP-66). What the
960
+ * seller settled is read, not recorded (GAP-77).
961
+ */
962
+ async pay(session, url, signed, request = {}) {
963
+ const payment = {
964
+ x402Version: 2,
965
+ resource: { url },
966
+ accepted: signed.accepted,
967
+ payload: signed.payload
968
+ };
969
+ const response = await this.fetch(session)(
970
+ url,
971
+ _X402.init(request, { "PAYMENT-SIGNATURE": _X402.encode(payment) })
972
+ );
973
+ const settlement = Settlement.safeParse(response.headers.get("PAYMENT-RESPONSE"));
974
+ return { response, settlement: settlement.success ? settlement.data : null };
975
+ }
976
+ /** An `accepts` entry as the payment `session.decide` takes — the normalization the recorded moments use. */
977
+ summary(accepts, opts = {}) {
978
+ return x402Moments.summary(accepts, opts);
979
+ }
980
+ /**
981
+ * The declared intent for an x402 mandate (Fraud SDK RFC › Session ›
982
+ * `intent.declared`). The cap must be in the currency the rail's moments
983
+ * carry — `<network>/<asset>`, atomic units (GAP-49) — or the platform's
984
+ * spend detectors compare two currencies and never meet.
985
+ */
986
+ intent(input) {
987
+ return {
988
+ mandate: input.mandate,
989
+ maxAmount: Money.x402(input.network, input.asset, input.maxAmount).toAmount(),
990
+ validUntil: new Date(input.validUntil).toISOString(),
991
+ ...input.merchantAllowlist ? { merchantAllowlist: input.merchantAllowlist } : {}
992
+ };
993
+ }
994
+ /** The request as `quote` and `pay` send it: `GET` bare, `POST` with a JSON body, unless told otherwise. */
995
+ static init(request, extra = {}) {
996
+ const headers = { accept: "*/*", ...extra };
997
+ if (request.body === void 0) return { method: request.method ?? "GET", headers };
998
+ return {
999
+ method: request.method ?? "POST",
1000
+ headers: { ...headers, "content-type": "application/json" },
1001
+ body: JSON.stringify(request.body)
1002
+ };
1003
+ }
1004
+ /** An x402 header value: base64 of the JSON. */
1005
+ static encode(value) {
1006
+ return Buffer.from(JSON.stringify(value), "utf8").toString("base64");
1007
+ }
1008
+ };
1009
+
809
1010
  // src/client.ts
810
- var SDK_VERSION = "0.7.0";
1011
+ var SDK_VERSION = "0.8.0";
811
1012
  var Beltic = class _Beltic {
1013
+ /** x402 for the buyer: `catalog`, `fetch`, `quote`, `pay`, `summary`, `intent`. */
1014
+ x402;
812
1015
  /** The stream registry, for the protocol adapters. @internal */
813
1016
  streams;
814
1017
  api;
@@ -841,6 +1044,7 @@ var Beltic = class _Beltic {
841
1044
  `@belticlabs/agent-risk-sdk/${SDK_VERSION}`
842
1045
  );
843
1046
  this.transport = new Transport(this.api);
1047
+ this.x402 = new X402(this.api);
844
1048
  this.streams = new Streams({
845
1049
  api: this.api,
846
1050
  transport: this.transport,
@@ -917,5 +1121,6 @@ export {
917
1121
  ChainRejectedError,
918
1122
  Decision,
919
1123
  SDK_VERSION,
920
- Session
1124
+ Session,
1125
+ X402
921
1126
  };
@@ -1,5 +1,5 @@
1
- import { J as JsonValue, E as EvidenceSourceAll, D as Decision } from '../index-DAVKGJs5.js';
2
- export { A as ALL_SOURCES, a as ApiError, b as ApiErrorSchema, C as ChainHead, c as CreatePolicyInputSchema, d as CreatePolicyOutput, e as CreateSessionInput, f as CreateSessionInputSchema, g as CreateSessionOutput, h as DeclaredIntent, i as DeclaredIntentSchema, j as DigestedEnvelope, k as EvaluateInput, l as EvaluateInputSchema, m as EvaluateOutput, n as EventResult, o as EvidenceAck, p as EvidenceBatchInputSchema, q as EvidenceEnvelope, r as EvidenceEvent, s as EvidenceEventSchema, t as EvidenceKind, u as EvidenceSource, v as EvidenceSourceAllSchema, w as EvidenceSourceSchema, G as GatewayDecisionPayload, H as Hex64, x as Hex64Schema, I as IntentDeclaredPayload, y as JsonObject, z as JsonValueSchema, L as LlmCallEndPayload, B as LlmCallParams, F as LlmCallStartPayload, M as MAX_BATCH_EVENTS, P as PayloadByKind, K as PaymentMomentPayload, N as PaymentSummary, O as PaymentSummarySchema, Q as PlatformAnomalyPayload, R as PlatformEvidenceKind, S as PlatformObservationPayload, T as PromptBase, U as PromptDelta, V as SESSION_ID_CHARS, W as SESSION_ID_PREFIX, X as SOURCE_ORDER, Y as SessionClosePayload, Z as SessionIdSchema, _ as SessionOpenPayload, $ as Sig, a0 as TimestampSchema, a1 as ToolCallEndPayload, a2 as ToolCallStartPayload, a3 as TransportGapPayload, a4 as WireEvidenceKind, a5 as chainKey, a6 as compareByArrival, a7 as compareBySessionSource, a8 as compareBySourceSeq, a9 as compareRootFirst, aa as payloadSchemaFor, ab as withinHeads } from '../index-DAVKGJs5.js';
1
+ import { J as JsonValue, E as EvidenceSourceAll, D as Decision } from '../index-CBUSXzgG.js';
2
+ export { A as ALL_SOURCES, a as ApiError, b as ApiErrorSchema, C as CatalogEndpoint, c as CatalogOffer, d as CatalogOutput, e as CatalogQuery, f as CatalogQuerySchema, g as CatalogSourceReport, h as CatalogVendor, i as ChainHead, j as CreatePolicyInputSchema, k as CreatePolicyOutput, l as CreateSessionInput, m as CreateSessionInputSchema, n as CreateSessionOutput, o as DeclaredIntent, p as DeclaredIntentSchema, q as DigestedEnvelope, r as EvaluateInput, s as EvaluateInputSchema, t as EvaluateOutput, u as EventResult, v as EvidenceAck, w as EvidenceBatchInputSchema, x as EvidenceEnvelope, y as EvidenceEvent, z as EvidenceEventSchema, B as EvidenceKind, F as EvidenceSource, G as EvidenceSourceAllSchema, H as EvidenceSourceSchema, I as GatewayDecisionPayload, K as Hex64, L as Hex64Schema, M as IntentDeclaredPayload, N as JsonObject, O as JsonValueSchema, P as LlmCallEndPayload, Q as LlmCallParams, R as LlmCallStartPayload, S as MAX_BATCH_EVENTS, T as MAX_CATALOG_RESULTS, U as PayloadByKind, V as PaymentMomentPayload, W as PaymentSummary, X as PaymentSummarySchema, Y as PlatformAnomalyPayload, Z as PlatformEvidenceKind, _ as PlatformObservationPayload, $ as PromptBase, a0 as PromptDelta, a1 as SESSION_ID_CHARS, a2 as SESSION_ID_PREFIX, a3 as SOURCE_ORDER, a4 as SessionClosePayload, a5 as SessionIdSchema, a6 as SessionOpenPayload, a7 as Sig, a8 as TimestampSchema, a9 as ToolCallEndPayload, aa as ToolCallStartPayload, ab as TransportGapPayload, ac as WireEvidenceKind, ad as chainKey, ae as compareByArrival, af as compareBySessionSource, ag as compareBySourceSeq, ah as compareRootFirst, ai as payloadSchemaFor, aj as withinHeads } from '../index-CBUSXzgG.js';
3
3
  import { z } from 'zod';
4
4
 
5
5
  /**
@@ -3,6 +3,7 @@ import {
3
3
  ANNOTATION_CODES,
4
4
  AmountSchema,
5
5
  ApiErrorSchema,
6
+ CatalogQuerySchema,
6
7
  CreatePolicyInputSchema,
7
8
  CreateSessionInputSchema,
8
9
  DecimalStringSchema,
@@ -16,6 +17,7 @@ import {
16
17
  INDICATOR_CODES,
17
18
  JsonValueSchema,
18
19
  MAX_BATCH_EVENTS,
20
+ MAX_CATALOG_RESULTS,
19
21
  Money,
20
22
  PaymentSummarySchema,
21
23
  PolicyRulesSchema,
@@ -38,12 +40,13 @@ import {
38
40
  payloadSchemaFor,
39
41
  walkPath,
40
42
  withinHeads
41
- } from "../chunk-FNU4CRJJ.js";
43
+ } from "../chunk-PDE55ZZV.js";
42
44
  export {
43
45
  ALL_SOURCES,
44
46
  ANNOTATION_CODES,
45
47
  AmountSchema,
46
48
  ApiErrorSchema,
49
+ CatalogQuerySchema,
47
50
  CreatePolicyInputSchema,
48
51
  CreateSessionInputSchema,
49
52
  DecimalStringSchema,
@@ -57,6 +60,7 @@ export {
57
60
  INDICATOR_CODES,
58
61
  JsonValueSchema,
59
62
  MAX_BATCH_EVENTS,
63
+ MAX_CATALOG_RESULTS,
60
64
  Money,
61
65
  PaymentSummarySchema,
62
66
  PolicyRulesSchema,
@@ -1,4 +1,4 @@
1
- import { m as EvaluateOutput, D as Decision$1, y as JsonObject, n as EventResult, r as EvidenceEvent, u as EvidenceSource, T as PromptBase, a4 as WireEvidenceKind, P as PayloadByKind, J as JsonValue, a2 as ToolCallStartPayload, Y as SessionClosePayload, h as DeclaredIntent, N as PaymentSummary, K as PaymentMomentPayload } from './index-DAVKGJs5.js';
1
+ import { t as EvaluateOutput, D as Decision$1, N as JsonObject, u as EventResult, y as EvidenceEvent, F as EvidenceSource, $ as PromptBase, ac as WireEvidenceKind, U as PayloadByKind, J as JsonValue, aa as ToolCallStartPayload, a4 as SessionClosePayload, o as DeclaredIntent, W as PaymentSummary, V as PaymentMomentPayload } from './index-CBUSXzgG.js';
2
2
 
3
3
  /**
4
4
  * The platform's verdict on one payment as a value the host can ask
@@ -40,6 +40,9 @@ declare class ApiClient {
40
40
  private readonly headers;
41
41
  constructor(baseUrl: string, apiKey: string, userAgent: string);
42
42
  post<T>(path: string, body: unknown): Promise<T>;
43
+ /** A read; `query` entries left undefined are not sent. */
44
+ get<T>(path: string, query?: Record<string, string | number | undefined>): Promise<T>;
45
+ private request;
43
46
  }
44
47
 
45
48
  /**
@@ -377,4 +380,4 @@ declare class Session {
377
380
  private static callOf;
378
381
  }
379
382
 
380
- export { BelticApiError as B, ChainRejectedError as C, type DecideOptions as D, type HumanDecisionInput as H, Session as S, Decision as a, type SessionOptions as b };
383
+ export { ApiClient as A, BelticApiError as B, ChainRejectedError as C, type DecideOptions as D, type HumanDecisionInput as H, Session as S, Decision as a, type SessionOptions as b };
@@ -1,9 +1,9 @@
1
1
  import { RoutesConfig, x402ResourceServer } from '@x402/core/server';
2
2
  import { MiddlewareHandler } from 'hono';
3
- import { B as Beltic } from '../client-Xn9M58OJ.js';
4
- import '../index-DAVKGJs5.js';
3
+ import { B as Beltic } from '../client-DFZL4Pt3.js';
4
+ import '../index-CBUSXzgG.js';
5
5
  import 'zod';
6
- import '../session-DAN29XoB.js';
6
+ import '../session-DTIzd1EQ.js';
7
7
 
8
8
  /** Seller half for hono: `@x402/hono`'s payment middleware with the Beltic hooks attached. */
9
9
 
package/dist/x402/hono.js CHANGED
@@ -1,10 +1,9 @@
1
1
  import {
2
2
  attachX402
3
- } from "../chunk-HI656UWG.js";
4
- import "../chunk-M4I3FGZG.js";
5
- import "../chunk-FQDHFTVR.js";
6
- import "../chunk-UXXSE643.js";
7
- import "../chunk-FNU4CRJJ.js";
3
+ } from "../chunk-P3VZNH5W.js";
4
+ import "../chunk-DI3LZR65.js";
5
+ import "../chunk-MWXXX35V.js";
6
+ import "../chunk-PDE55ZZV.js";
8
7
 
9
8
  // src/x402/hono.ts
10
9
  import {
@@ -1,8 +1,8 @@
1
1
  import { x402ResourceServer, x402HTTPResourceServer } from '@x402/core/server';
2
- import { B as Beltic } from '../client-Xn9M58OJ.js';
3
- import { S as Session } from '../session-DAN29XoB.js';
4
- import { h as DeclaredIntent, N as PaymentSummary } from '../index-DAVKGJs5.js';
2
+ import { B as Beltic } from '../client-DFZL4Pt3.js';
3
+ import '../index-CBUSXzgG.js';
5
4
  import 'zod';
5
+ import '../session-DTIzd1EQ.js';
6
6
 
7
7
  declare function attachX402(beltic: Beltic, server: x402ResourceServer, http?: x402HTTPResourceServer): void;
8
8
 
@@ -14,59 +14,4 @@ declare function attachX402(beltic: Beltic, server: x402ResourceServer, http?: x
14
14
  declare const SESSION_EXTENSION = "beltic.sessionId";
15
15
  declare const SESSION_HEADER = "Beltic-Session-Id";
16
16
 
17
- /**
18
- * The paying fetch, observed. A request made while the session has no
19
- * stream (the platform could not open one, GAP-70) goes through the inner
20
- * fetch unrecorded.
21
- */
22
- declare function belticFetch(session: Session, inner?: typeof globalThis.fetch): typeof globalThis.fetch;
23
-
24
- /**
25
- * The declared intent for an x402 mandate (Fraud SDK RFC › Session ›
26
- * `intent.declared`). The cap must be in the currency the rail's moments
27
- * carry — `<network>/<asset>`, atomic units (GAP-49) — or the platform's
28
- * spend detectors compare two currencies and never meet; this is the one
29
- * place a buyer spells that. Everything else is the protocol's own shape.
30
- */
31
-
32
- interface X402IntentInput {
33
- mandate: string;
34
- network: string;
35
- asset: string;
36
- /** Atomic units of `asset`, as x402 carries amounts — a decimal string or a bigint, never a float. */
37
- maxAmount: string | bigint;
38
- validUntil: string | Date;
39
- merchantAllowlist?: string[] | undefined;
40
- }
41
- declare function x402Intent(input: X402IntentInput): DeclaredIntent;
42
-
43
- /**
44
- * x402 artifacts → protocol moments (Fraud SDK RFC › Protocol Adapter —
45
- * x402). The moment is normalized (payee, amount, payer) so both sides of
46
- * a purchase compare; the artifact travels whole in `raw`. For x402 the
47
- * currency is `<network>/<asset>` (GAP-49) and the value is the atomic
48
- * amount as the protocol carries it — `x402Summary` gives a buyer the same
49
- * normalization for the payment it is about to evaluate, so what it asks
50
- * about and what the wrapper records are one and the same.
51
- */
52
-
53
- /**
54
- * The minimum an `accepts` entry needs to become a moment; unknown parts
55
- * are named, never dropped.
56
- */
57
- interface AcceptsLike {
58
- payTo?: string | undefined;
59
- amount?: string | undefined;
60
- network?: string | undefined;
61
- asset?: string | undefined;
62
- }
63
- /**
64
- * The one normalization of an x402 `accepts` entry: the payment in the
65
- * shape `evaluate` takes, with the payer as `payerOf` would read it. Every
66
- * moment below is this plus its artifact and `raw`.
67
- */
68
- declare function x402Summary(accepts: AcceptsLike | undefined, opts?: {
69
- payer?: string | undefined;
70
- }): PaymentSummary;
71
-
72
- export { type AcceptsLike, SESSION_EXTENSION, SESSION_HEADER, type X402IntentInput, attachX402, belticFetch, x402Intent, x402Summary };
17
+ export { SESSION_EXTENSION, SESSION_HEADER, attachX402 };
@@ -1,86 +1,14 @@
1
1
  import {
2
- SESSION_EXTENSION,
3
- SESSION_HEADER,
4
- attachX402,
5
- x402Moments,
6
- x402Summary
7
- } from "../chunk-HI656UWG.js";
8
- import "../chunk-M4I3FGZG.js";
9
- import "../chunk-FQDHFTVR.js";
10
- import "../chunk-UXXSE643.js";
2
+ attachX402
3
+ } from "../chunk-P3VZNH5W.js";
11
4
  import {
12
- Money
13
- } from "../chunk-FNU4CRJJ.js";
14
-
15
- // src/x402/fetch.ts
16
- import { z } from "zod";
17
- var Base64Json = z.string().regex(/^[A-Za-z0-9+/]*={0,2}$/).transform((value, ctx) => {
18
- try {
19
- return JSON.parse(Buffer.from(value, "base64").toString("utf8"));
20
- } catch {
21
- ctx.addIssue({ code: "custom", message: "not base64 JSON" });
22
- return z.NEVER;
23
- }
24
- });
25
- var Accepts = z.looseObject({
26
- payTo: z.string().optional().catch(void 0),
27
- amount: z.string().optional().catch(void 0),
28
- network: z.string().optional().catch(void 0),
29
- asset: z.string().optional().catch(void 0)
30
- });
31
- var PaymentRequired = Base64Json.pipe(
32
- z.looseObject({ accepts: z.array(Accepts).optional().catch(void 0) })
33
- );
34
- var PaymentPayload = Base64Json.pipe(
35
- z.looseObject({
36
- accepted: Accepts.optional().catch(void 0),
37
- payload: z.record(z.string(), z.unknown()).optional().catch(void 0),
38
- extensions: z.record(z.string(), z.unknown()).optional().catch(void 0)
39
- })
40
- );
41
- function belticFetch(session, inner = globalThis.fetch) {
42
- return async (input, init) => {
43
- const stream = await session.stream();
44
- if (!stream) return inner(input, init);
45
- const headers = new Headers(
46
- init?.headers ?? (input instanceof Request ? input.headers : void 0)
47
- );
48
- headers.set(SESSION_HEADER, stream.id);
49
- const payload = PaymentPayload.safeParse(headers.get("PAYMENT-SIGNATURE")).data;
50
- if (payload) {
51
- const bound = {
52
- ...payload,
53
- extensions: { ...payload.extensions, [SESSION_EXTENSION]: stream.id }
54
- };
55
- headers.set("PAYMENT-SIGNATURE", encodeHeader(bound));
56
- await stream.emit("payment.presented", x402Moments.payload(bound));
57
- await stream.flush();
58
- }
59
- const res = await inner(input, { ...init, headers });
60
- if (res.status !== 402) return res;
61
- const required = PaymentRequired.safeParse(res.headers.get("PAYMENT-REQUIRED")).data;
62
- if (required) await stream.emit("payment.requested", x402Moments.required(required));
63
- return res;
64
- };
65
- }
66
- function encodeHeader(value) {
67
- return Buffer.from(JSON.stringify(value), "utf8").toString("base64");
68
- }
69
-
70
- // src/x402/intent.ts
71
- function x402Intent(input) {
72
- return {
73
- mandate: input.mandate,
74
- maxAmount: Money.x402(input.network, input.asset, input.maxAmount).toAmount(),
75
- validUntil: new Date(input.validUntil).toISOString(),
76
- ...input.merchantAllowlist ? { merchantAllowlist: input.merchantAllowlist } : {}
77
- };
78
- }
5
+ SESSION_EXTENSION,
6
+ SESSION_HEADER
7
+ } from "../chunk-DI3LZR65.js";
8
+ import "../chunk-MWXXX35V.js";
9
+ import "../chunk-PDE55ZZV.js";
79
10
  export {
80
11
  SESSION_EXTENSION,
81
12
  SESSION_HEADER,
82
- attachX402,
83
- belticFetch,
84
- x402Intent,
85
- x402Summary
13
+ attachX402
86
14
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@belticlabs/agent-risk-sdk",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "The Beltic Agent Risk SDK — one client, two halves: instrument the buyer agent (AI SDK, x402 fetch) and guard the seller boundary (x402 over hono, evaluate).",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -1,29 +0,0 @@
1
- // src/core/json.ts
2
- function toJson(value) {
3
- if (value === null || value === void 0) return null;
4
- if (typeof value === "string" || typeof value === "boolean") return value;
5
- if (typeof value === "number") return Number.isFinite(value) ? value : null;
6
- if (typeof value === "bigint") return value.toString();
7
- if (value instanceof Uint8Array) return { $bytes: Buffer.from(value).toString("base64") };
8
- if (value instanceof Date) return value.toISOString();
9
- if (value instanceof URL) return value.toString();
10
- if (Array.isArray(value)) return value.map(toJson);
11
- if (typeof value === "object") {
12
- const out = {};
13
- for (const [k, v] of Object.entries(value)) {
14
- if (v === void 0 || typeof v === "function") continue;
15
- out[k] = toJson(v);
16
- }
17
- return out;
18
- }
19
- return null;
20
- }
21
- function toJsonObject(value) {
22
- const j = toJson(value);
23
- return j && typeof j === "object" && !Array.isArray(j) ? j : { value: j };
24
- }
25
-
26
- export {
27
- toJson,
28
- toJsonObject
29
- };
@@ -1,13 +0,0 @@
1
- // src/core/payment-moment.ts
2
- function summaryOf(m) {
3
- return {
4
- protocol: m.protocol,
5
- payee: m.payee,
6
- amount: { value: m.amount.value, currency: m.amount.currency },
7
- ...m.payer ? { payer: m.payer } : {}
8
- };
9
- }
10
-
11
- export {
12
- summaryOf
13
- };
@@ -1,71 +0,0 @@
1
- import { N as PaymentSummary } from './index-DAVKGJs5.js';
2
- import { b as SessionOptions, S as Session, a as Decision } from './session-DAN29XoB.js';
3
-
4
- /**
5
- * One SDK, two halves (Fraud SDK RFC › Summary). `Beltic` is the single
6
- * client: `session` for the buyer half, `evaluate` for whichever half is
7
- * about to let a payment through. Protocol integrations are plain
8
- * functions behind subpath exports, each pulling exactly one optional peer:
9
- *
10
- * @belticlabs/agent-risk-sdk/ai → middleware(session), wrapTools(session, …)
11
- * @belticlabs/agent-risk-sdk/x402 → belticFetch(session), x402Summary(…), attachX402(beltic, …)
12
- * @belticlabs/agent-risk-sdk/hono → belticPaymentMiddleware(beltic, …)
13
- *
14
- * Neither half decides risk locally: verdicts are platform-side.
15
- *
16
- * A client is configured or it is not constructed — `Beltic.fromEnv()`
17
- * reads `BELTIC_*`, and there is no disabled client (GAP-78). Evidence is
18
- * a side channel of the work it observes, so a platform outage never
19
- * throws into that work (GAP-70): the transport waits it out, `session`
20
- * runs without a stream, `evaluate` answers `Decision.absent()` — never an
21
- * invented verdict. What the platform *rejects* (a 4xx, a forked chain)
22
- * is the client's fault and throws.
23
- */
24
-
25
- declare const SDK_VERSION = "0.7.0";
26
- interface BelticOptions {
27
- apiKey: string;
28
- baseUrl: string;
29
- /** The agent's 32-byte Ed25519 seed as 64 hex — the buyer half. Without it, `session` is unavailable; the seller half works. */
30
- agentSeed?: string | undefined;
31
- }
32
- declare class Beltic {
33
- private readonly api;
34
- private readonly transport;
35
- /** Open handles by conversation id (GAP-84). */
36
- private readonly sessions;
37
- /**
38
- * The client the environment describes: `BELTIC_API_KEY` and
39
- * `BELTIC_BASE_URL`, both required, and `BELTIC_AGENT_SEED` (64 hex) for
40
- * the buyer half. A missing required variable is a configuration error,
41
- * thrown (GAP-78).
42
- */
43
- static fromEnv(env?: Record<string, string | undefined>): Beltic;
44
- constructor(opts: BelticOptions);
45
- /**
46
- * A conversation: new without an id, resumed with one — the id `session.id()`
47
- * answered in this or any earlier process (GAP-84). One handle per
48
- * conversation in-process until it closes; the options count when a
49
- * session is opened. See `Session`.
50
- */
51
- session(id?: string, opts?: SessionOptions): Session;
52
- /**
53
- * The platform's verdict on a payment — the seller's before it verifies,
54
- * the buyer's before it presents (Fraud SDK RFC › Evaluation Client).
55
- * Read-your-writes: the session's buffered evidence is flushed first so
56
- * the platform judges what the caller already saw (GAP-16/88). With a `callId`
57
- * the platform answers the decision already taken for that call anywhere
58
- * in the conversation (GAP-84). Absent when the platform could not be
59
- * reached (GAP-70).
60
- */
61
- evaluate(sessionId: string, payment: PaymentSummary, opts?: {
62
- callId?: string | undefined;
63
- }): Promise<Decision>;
64
- /** Send everything buffered now — every session's — and wait for that attempt. */
65
- flush(): Promise<void>;
66
- shutdown(): Promise<void>;
67
- /** `process.env` where there is a `process` (Node); `{}` on workerd, where the shell passes its `env`. */
68
- private static processEnv;
69
- }
70
-
71
- export { Beltic as B, SDK_VERSION as S, type BelticOptions as a };