@belticlabs/agent-risk-sdk 0.2.0 → 0.4.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,7 +1,6 @@
1
- import { D as Decision } from './index-Bjs3BPPU.js';
1
+ import { D as Decision } from './verdict-CDAsxktI.js';
2
2
  import { x402ResourceServer, x402HTTPResourceServer } from '@x402/core/server';
3
- import { B as Beltic } from './client-C-mV_3A0.js';
4
- import { d as SessionBorn, S as Session } from './session-5TClPLI4.js';
3
+ import { B as Beltic, n as SessionBorn, a as Session } from './session-gK51QVAM.js';
5
4
 
6
5
  interface CorrelationContext {
7
6
  path: string;
@@ -17,6 +16,8 @@ interface AttachOptions {
17
16
  decision: Decision;
18
17
  reasonCodes: string[];
19
18
  born: SessionBorn;
19
+ /** The verdict the buyer says it obtained before presenting (GAP-80), unverified. */
20
+ buyerDecisionId: string | null;
20
21
  }) => void) | undefined;
21
22
  }
22
23
  interface AttachedX402 {
@@ -1,6 +1,6 @@
1
- import { P as PaymentMomentPayload } from '../index-Bjs3BPPU.js';
1
+ import { P as PaymentMomentPayload } from '../verdict-CDAsxktI.js';
2
2
  import { LanguageModelMiddleware, Tool, ToolSet } from 'ai';
3
- import { S as Session } from '../session-5TClPLI4.js';
3
+ import { S as SessionSource } from '../session-gK51QVAM.js';
4
4
  import 'zod';
5
5
 
6
6
  interface PaymentMoments {
@@ -12,8 +12,8 @@ interface WrapToolOptions<I = unknown, O = unknown> {
12
12
  /** When the tool performs a purchase, map its input/output to the payment moments it produced. */
13
13
  payment?: (input: I, output: O) => PaymentMoments | null;
14
14
  }
15
- declare function middleware(session: Session | null | undefined): LanguageModelMiddleware;
16
- declare function wrapTool<T extends Tool>(session: Session | null | undefined, tool: T, opts?: WrapToolOptions): T;
17
- declare function wrapTools<T extends ToolSet>(session: Session | null | undefined, tools: T, opts?: Record<string, WrapToolOptions>): T;
15
+ declare function middleware(source: SessionSource): LanguageModelMiddleware;
16
+ declare function wrapTool<T extends Tool>(source: SessionSource, tool: T, opts?: WrapToolOptions): T;
17
+ declare function wrapTools<T extends ToolSet>(source: SessionSource, tools: T, opts?: Record<string, WrapToolOptions>): T;
18
18
 
19
19
  export { type PaymentMoments, type WrapToolOptions, middleware, wrapTool, wrapTools };
package/dist/ai/index.js CHANGED
@@ -1,14 +1,14 @@
1
1
  import {
2
+ Sessions,
2
3
  recordCall
3
- } from "../chunk-GSH5APZW.js";
4
- import {
5
- uuidv7
6
- } from "../chunk-SFGM7KOG.js";
7
- import "../chunk-46QN2KEZ.js";
4
+ } from "../chunk-4MG6VNAU.js";
8
5
  import {
9
6
  toJson,
10
7
  toJsonObject
11
8
  } from "../chunk-FQDHFTVR.js";
9
+ import {
10
+ uuidv7
11
+ } from "../chunk-X3W2Z5GC.js";
12
12
 
13
13
  // src/ai/index.ts
14
14
  var PARAM_KEYS = [
@@ -23,23 +23,29 @@ var PARAM_KEYS = [
23
23
  "responseFormat",
24
24
  "stopSequences"
25
25
  ];
26
- function middleware(session) {
27
- if (!session) return {};
26
+ function middleware(source) {
27
+ if (!source) return {};
28
28
  return {
29
- wrapGenerate: ({ doGenerate, params, model }) => recordCall(
30
- session,
31
- "llm_call",
32
- uuidv7(),
33
- { provider: model.provider, modelId: model.modelId, params: pick(params) },
34
- doGenerate,
35
- (result) => ({
36
- content: toJson(result.content),
37
- finishReason: result.finishReason.unified,
38
- usage: toJsonObject(result.usage)
39
- })
40
- ),
29
+ wrapGenerate: async ({ doGenerate, params, model }) => {
30
+ const session = await Sessions.resolve(source);
31
+ if (!session) return doGenerate();
32
+ return recordCall(
33
+ session,
34
+ "llm_call",
35
+ uuidv7(),
36
+ { provider: model.provider, modelId: model.modelId, params: pick(params) },
37
+ doGenerate,
38
+ (result) => ({
39
+ content: toJson(result.content),
40
+ finishReason: result.finishReason.unified,
41
+ usage: toJsonObject(result.usage)
42
+ })
43
+ );
44
+ },
41
45
  // A stream ends when it drains, not when doStream resolves — so its end is emitted at flush.
42
46
  wrapStream: async ({ doStream, params, model }) => {
47
+ const session = await Sessions.resolve(source);
48
+ if (!session) return doStream();
43
49
  const callId = uuidv7();
44
50
  const started = Date.now();
45
51
  await session.emit("llm_call.start", {
@@ -73,32 +79,36 @@ function middleware(session) {
73
79
  }
74
80
  };
75
81
  }
76
- function wrapTool(session, tool, opts = {}) {
77
- if (!session || !tool.execute) return tool;
82
+ function wrapTool(source, tool, opts = {}) {
83
+ if (!source || !tool.execute) return tool;
78
84
  const original = tool.execute;
79
85
  const toolName = opts.name ?? tool.name ?? "tool";
80
- const execute = (input, options) => recordCall(
81
- session,
82
- "tool_call",
83
- options?.toolCallId ?? uuidv7(),
84
- { toolName, input: toJson(input), transport: "local" },
85
- async () => {
86
- const output = await original(input, options);
87
- return output && typeof output === "object" && Symbol.asyncIterator in output ? collect(output) : output;
88
- },
89
- async (output) => {
90
- const moments = opts.payment?.(input, output) ?? null;
91
- if (moments?.requested) await session.emit("payment.requested", moments.requested);
92
- if (moments?.presented) await session.emit("payment.presented", moments.presented);
93
- return { output: toJson(output) };
94
- }
95
- );
86
+ const execute = async (input, options) => {
87
+ const session = await Sessions.resolve(source);
88
+ if (!session) return original(input, options);
89
+ return recordCall(
90
+ session,
91
+ "tool_call",
92
+ options?.toolCallId ?? uuidv7(),
93
+ { toolName, input: toJson(input), transport: "local" },
94
+ async () => {
95
+ const output = await original(input, options);
96
+ return output && typeof output === "object" && Symbol.asyncIterator in output ? collect(output) : output;
97
+ },
98
+ async (output) => {
99
+ const moments = opts.payment?.(input, output) ?? null;
100
+ if (moments?.requested) await session.emit("payment.requested", moments.requested);
101
+ if (moments?.presented) await session.emit("payment.presented", moments.presented);
102
+ return { output: toJson(output) };
103
+ }
104
+ );
105
+ };
96
106
  return { ...tool, execute };
97
107
  }
98
- function wrapTools(session, tools, opts = {}) {
108
+ function wrapTools(source, tools, opts = {}) {
99
109
  const out = {};
100
110
  for (const [name, tool] of Object.entries(tools))
101
- out[name] = wrapTool(session, tool, { name, ...opts[name] });
111
+ out[name] = wrapTool(source, tool, { name, ...opts[name] });
102
112
  return out;
103
113
  }
104
114
  function pick(params) {
@@ -326,13 +326,6 @@ var EvaluateOutputSchema = z4.strictObject({
326
326
  /** Additive (GAP-21). */
327
327
  decisionId: z4.uuid()
328
328
  });
329
- var AnchorEntrySchema = z4.strictObject({
330
- epoch: z4.string().min(1),
331
- root: Hex64Schema,
332
- sig: z4.string().min(1),
333
- chainRef: z4.string().optional()
334
- });
335
- var AnchorsOutputSchema = z4.strictObject({ entries: z4.array(AnchorEntrySchema) });
336
329
  var CreatePolicyInputSchema = z4.strictObject({ rules: PolicyRulesSchema });
337
330
  var CreatePolicyOutputSchema = z4.strictObject({
338
331
  policyId: z4.uuid(),
@@ -542,8 +535,6 @@ export {
542
535
  EvaluateInputSchema,
543
536
  DecisionSchema,
544
537
  EvaluateOutputSchema,
545
- AnchorEntrySchema,
546
- AnchorsOutputSchema,
547
538
  CreatePolicyInputSchema,
548
539
  CreatePolicyOutputSchema,
549
540
  ApiErrorSchema,
@@ -0,0 +1,276 @@
1
+ import {
2
+ Chain
3
+ } from "./chunk-X3W2Z5GC.js";
4
+
5
+ // src/core/disabled-error.ts
6
+ var BelticDisabledError = class extends Error {
7
+ code = "SDK_DISABLED";
8
+ constructor(entry) {
9
+ super(`${entry} needs a configured client: this Beltic is disabled (no BELTIC_* configured)`);
10
+ this.name = "BelticDisabledError";
11
+ }
12
+ };
13
+
14
+ // src/core/record.ts
15
+ function errorOf(err) {
16
+ const e = err;
17
+ return { name: String(e?.name ?? "Error"), message: String(e?.message ?? err) };
18
+ }
19
+ function openCall(session, kind, callId, start) {
20
+ const started = Date.now();
21
+ const opened = session.emit(`${kind}.start`, { callId, ...start });
22
+ const close = async (outcome) => {
23
+ await opened;
24
+ return session.emit(
25
+ `${kind}.end`,
26
+ { callId, ...outcome, durationMs: Date.now() - started }
27
+ );
28
+ };
29
+ return {
30
+ callId,
31
+ opened,
32
+ end: (outcome = {}) => close(outcome),
33
+ fail: (error, outcome = {}) => close({ error: errorOf(error), ...outcome })
34
+ };
35
+ }
36
+ async function recordCall(session, kind, callId, start, run, end = () => ({})) {
37
+ const span = openCall(session, kind, callId, start);
38
+ await span.opened;
39
+ try {
40
+ const result = await run();
41
+ await span.end(await end(result));
42
+ return result;
43
+ } catch (err) {
44
+ await span.fail(err);
45
+ throw err;
46
+ }
47
+ }
48
+
49
+ // src/core/session.ts
50
+ var Session = class {
51
+ constructor(deps, id, source, expiresAt, born) {
52
+ this.deps = deps;
53
+ this.id = id;
54
+ this.source = source;
55
+ this.expiresAt = expiresAt;
56
+ this.born = born;
57
+ this.chain = Chain.genesis(id, source);
58
+ this.now = deps.now ?? (() => /* @__PURE__ */ new Date());
59
+ }
60
+ chain;
61
+ building = Promise.resolve();
62
+ dropped = 0;
63
+ droppedFirstTs = null;
64
+ droppedLastTs = null;
65
+ closed = false;
66
+ now;
67
+ get head() {
68
+ return this.chain.head;
69
+ }
70
+ get droppedCount() {
71
+ return this.dropped;
72
+ }
73
+ get isClosed() {
74
+ return this.closed;
75
+ }
76
+ /**
77
+ * Resolves once the event is sequenced and buffered — not once it is
78
+ * acknowledged. `false` when the event was dropped, or (fail-open) when
79
+ * the chain can no longer take it.
80
+ */
81
+ async emit(kind, payload) {
82
+ if (!this.deps.failOpen) return this.chainEvent(kind, payload);
83
+ try {
84
+ return await this.chainEvent(kind, payload);
85
+ } catch (err) {
86
+ this.deps.onError?.(err);
87
+ return false;
88
+ }
89
+ }
90
+ /** The tool call whose `execute` the host runs itself; see `ToolCallSpan`. */
91
+ toolCall(call) {
92
+ const { callId, ...start } = call;
93
+ return openCall(this, "tool_call", callId, { transport: "local", ...start });
94
+ }
95
+ async chainEvent(kind, payload) {
96
+ const halted = this.deps.transport.haltedError(this.id, this.source);
97
+ if (halted) throw halted;
98
+ const ts = this.now().toISOString();
99
+ if (!this.deps.transport.hasRoom()) {
100
+ this.dropped++;
101
+ this.droppedFirstTs ??= ts;
102
+ this.droppedLastTs = ts;
103
+ return false;
104
+ }
105
+ if (this.dropped > 0) {
106
+ this.deps.transport.enqueue(
107
+ await this.next(
108
+ "transport.gap",
109
+ { dropped: this.dropped, firstTs: this.droppedFirstTs, lastTs: this.droppedLastTs },
110
+ ts
111
+ )
112
+ );
113
+ this.dropped = 0;
114
+ this.droppedFirstTs = this.droppedLastTs = null;
115
+ }
116
+ const body = payload;
117
+ this.deps.transport.enqueue(
118
+ await this.next(kind, this.deps.redact ? this.deps.redact(kind, body) : body, ts)
119
+ );
120
+ return true;
121
+ }
122
+ async close(reason = "completed", extra = {}) {
123
+ if (this.closed) return;
124
+ this.closed = true;
125
+ await this.emit("session.close", { reason, ...extra });
126
+ await this.flush();
127
+ this.deps.onClosed?.(this);
128
+ }
129
+ /** Read-your-writes: the platform must hold the evidence before anyone judges it (GAP-16/66). */
130
+ flush() {
131
+ return this.deps.transport.flush();
132
+ }
133
+ /** Serialized: two concurrent emits get consecutive seqs, never the same one. */
134
+ next(kind, payload, ts) {
135
+ const run = this.building.then(async () => {
136
+ const built = await this.chain.append({ ts, kind, payload }, this.deps.signer);
137
+ this.chain = built.chain;
138
+ return built.event;
139
+ });
140
+ this.building = run.catch(() => void 0);
141
+ return run;
142
+ }
143
+ };
144
+ var DEFAULT_OPEN_RETRY_MS = 6e4;
145
+ var Sessions = class {
146
+ constructor(deps) {
147
+ this.deps = deps;
148
+ }
149
+ /**
150
+ * One session object per (session, source) per process: a chain's head
151
+ * lives in it, so two objects for the same chain would both start at
152
+ * seq 0 and fork it. Closed sessions are forgotten; a process restart
153
+ * mid-session still loses the head (GAP-67).
154
+ */
155
+ attached = /* @__PURE__ */ new Map();
156
+ /** Buyer sessions by the host's own key (GAP-71). */
157
+ opened = /* @__PURE__ */ new Map();
158
+ retryAt = 0;
159
+ /** The session behind a source: itself, or the one the run opens (null when there is none). */
160
+ static resolve(source) {
161
+ if (!source) return Promise.resolve(null);
162
+ return source instanceof Session ? Promise.resolve(source) : source.session();
163
+ }
164
+ /**
165
+ * Buyer half: the evidence session for a key of the host's own (its
166
+ * session, run or conversation id), opened on first use and reused
167
+ * after. A halted chain is reopened as a fresh session that continues
168
+ * the same key; a closed key is forgotten. When the platform refuses to
169
+ * open one, a fail-open client resolves null — the host runs without
170
+ * evidence — until `openRetryMs` has passed (GAP-71); otherwise the
171
+ * refusal is thrown and the next call tries again. The identity is
172
+ * required either way: that is configuration.
173
+ */
174
+ open(key, input = {}) {
175
+ if (this.deps.enabled === false) return Promise.resolve(null);
176
+ const prior = this.opened.get(key) ?? Promise.resolve(null);
177
+ const next = prior.catch(() => null).then(
178
+ (session) => session && !session.isClosed && !this.deps.transport.haltedError(session.id, session.source) ? session : this.openFresh(input, () => this.forget(key, next))
179
+ );
180
+ this.opened.set(key, next);
181
+ next.catch(() => this.forget(key, next));
182
+ return next;
183
+ }
184
+ forget(key, entry) {
185
+ if (this.opened.get(key) === entry) this.opened.delete(key);
186
+ }
187
+ async openFresh(input, onClosed) {
188
+ this.identityFor("open");
189
+ const create = async () => this.create(typeof input === "function" ? await input() : input, onClosed);
190
+ if (!this.deps.failOpen) return create();
191
+ if (Date.now() < this.retryAt) return null;
192
+ try {
193
+ const session = await create();
194
+ this.retryAt = 0;
195
+ return session;
196
+ } catch (err) {
197
+ this.retryAt = Date.now() + (this.deps.openRetryMs ?? DEFAULT_OPEN_RETRY_MS);
198
+ this.deps.onError?.(err);
199
+ return null;
200
+ }
201
+ }
202
+ /** Buyer half: create an AGENT_TRACE session bound to the agent identity, then announce it on the chain. */
203
+ start(input = {}) {
204
+ if (this.deps.enabled === false)
205
+ return Promise.reject(new BelticDisabledError("sessions.start"));
206
+ return this.create(input);
207
+ }
208
+ identityFor(entry) {
209
+ const identity = this.deps.identity;
210
+ if (!identity)
211
+ throw new Error(`sessions.${entry} needs an agent identity (createBeltic({ identity }))`);
212
+ return identity;
213
+ }
214
+ async create(input, onClosed) {
215
+ const identity = this.identityFor("start");
216
+ const body = {
217
+ source: "AGENT_TRACE",
218
+ agent: { did: identity.did, credential: identity.credential ?? identity.did },
219
+ ...input.intent ? { intent: input.intent } : {}
220
+ };
221
+ const out = await this.deps.api.post("/v1/sessions", body);
222
+ const session = this.attach(out.sessionId, "AGENT_TRACE", out.expiresAt, "buyer", onClosed);
223
+ await session.emit("session.open", {
224
+ runtime: {
225
+ sdk: "@belticlabs/agent-risk-sdk",
226
+ version: this.deps.sdkVersion,
227
+ ...input.runtime
228
+ },
229
+ ...input.attestations ? { attestations: input.attestations } : {}
230
+ });
231
+ if (input.intent) await session.emit("intent.declared", input.intent);
232
+ return session;
233
+ }
234
+ /** Seller half: emit INTERNAL_NETWORK evidence into a session the buyer bound, or open a seller-born one. */
235
+ async ensure(sessionId) {
236
+ if (this.deps.enabled === false) throw new BelticDisabledError("sessions.ensure");
237
+ if (sessionId) return this.attach(sessionId, "INTERNAL_NETWORK", null, "buyer");
238
+ const out = await this.deps.api.post("/v1/sessions", {
239
+ source: "INTERNAL_NETWORK"
240
+ });
241
+ return this.attach(out.sessionId, "INTERNAL_NETWORK", out.expiresAt, "seller");
242
+ }
243
+ attach(id, source, expiresAt, born, onClosed) {
244
+ const key = `${id}:${source}`;
245
+ const existing = this.attached.get(key);
246
+ if (existing) return existing;
247
+ const session = new Session(
248
+ {
249
+ transport: this.deps.transport,
250
+ signer: source === "AGENT_TRACE" ? this.deps.identity?.signer : void 0,
251
+ redact: this.deps.redact,
252
+ now: this.deps.now,
253
+ failOpen: this.deps.failOpen,
254
+ onError: this.deps.onError,
255
+ onClosed: () => {
256
+ this.attached.delete(key);
257
+ onClosed?.();
258
+ }
259
+ },
260
+ id,
261
+ source,
262
+ expiresAt,
263
+ born
264
+ );
265
+ this.attached.set(key, session);
266
+ return session;
267
+ }
268
+ };
269
+
270
+ export {
271
+ BelticDisabledError,
272
+ recordCall,
273
+ Session,
274
+ DEFAULT_OPEN_RETRY_MS,
275
+ Sessions
276
+ };
@@ -0,0 +1,177 @@
1
+ import {
2
+ toJsonObject
3
+ } from "./chunk-FQDHFTVR.js";
4
+ import {
5
+ payloadDigest
6
+ } from "./chunk-X3W2Z5GC.js";
7
+
8
+ // src/x402/binding.ts
9
+ var SESSION_EXTENSION = "beltic.sessionId";
10
+ var SESSION_HEADER = "Beltic-Session-Id";
11
+ var DECISION_EXTENSION = "beltic.decisionId";
12
+ function sessionIdOf(extensions) {
13
+ return stringAt(extensions, SESSION_EXTENSION);
14
+ }
15
+ function decisionIdOf(extensions) {
16
+ return stringAt(extensions, DECISION_EXTENSION);
17
+ }
18
+ function stringAt(extensions, key) {
19
+ const v = extensions?.[key];
20
+ return typeof v === "string" && v.length > 0 ? v : null;
21
+ }
22
+
23
+ // src/x402/moments.ts
24
+ function x402Currency(network, asset) {
25
+ return `${network}/${asset}`;
26
+ }
27
+ function x402Summary(accepts, opts = {}) {
28
+ const payer = opts.payer?.toLowerCase();
29
+ return {
30
+ protocol: "x402",
31
+ payee: accepts?.payTo ?? "unknown",
32
+ amount: {
33
+ value: accepts?.amount ?? "0",
34
+ currency: accepts ? x402Currency(accepts.network ?? "unknown", accepts.asset ?? "unknown") : "unknown"
35
+ },
36
+ ...payer ? { payer } : {}
37
+ };
38
+ }
39
+ function fromAccepts(a, artifact, raw, payer) {
40
+ return { ...x402Summary(a, { payer }), artifact, raw };
41
+ }
42
+ function payerOf(payload) {
43
+ const p = payload.payload ?? {};
44
+ const auth = p.authorization;
45
+ const candidates = [
46
+ auth?.from,
47
+ p.from,
48
+ p.payer,
49
+ p.signer,
50
+ p.permit?.owner
51
+ ];
52
+ const hit = candidates.find((c) => typeof c === "string" && c.length > 0);
53
+ return typeof hit === "string" ? hit.toLowerCase() : void 0;
54
+ }
55
+ var x402Moments = {
56
+ /** The 402 challenge as the buyer saw it, from the `PAYMENT-REQUIRED` header. */
57
+ required(required) {
58
+ return fromAccepts(required.accepts?.[0], "http-402", toJsonObject(required));
59
+ },
60
+ /** The requirements the seller's resource server resolved for a request. */
61
+ requirements(req) {
62
+ return fromAccepts(req, "http-402", toJsonObject(req));
63
+ },
64
+ /** A route's static `accepts` config, before any payment header exists. */
65
+ route(route, raw) {
66
+ const accepts = route ?? {};
67
+ const extra = accepts.extra;
68
+ return fromAccepts(
69
+ {
70
+ payTo: typeof accepts.payTo === "string" ? accepts.payTo : "dynamic",
71
+ amount: priceValue(accepts.price),
72
+ network: accepts.network,
73
+ asset: extra?.asset ?? "route"
74
+ },
75
+ "http-402",
76
+ raw
77
+ );
78
+ },
79
+ /** The signed payment the buyer presented, with the requirement it accepted. */
80
+ payload(payload) {
81
+ return fromAccepts(
82
+ payload.accepted,
83
+ "payment-signature",
84
+ toJsonObject(payload),
85
+ payerOf(payload)
86
+ );
87
+ }
88
+ };
89
+ function priceValue(price) {
90
+ if (typeof price === "string") return price.replace(/[^0-9.]/g, "") || "0";
91
+ if (typeof price === "number") return String(price);
92
+ if (price && typeof price === "object" && typeof price.amount === "string")
93
+ return price.amount;
94
+ return "0";
95
+ }
96
+
97
+ // src/x402/adapter.ts
98
+ function attachX402(beltic, server, http, opts = {}) {
99
+ const inFlight = /* @__PURE__ */ new Map();
100
+ const resolveSession = async (bound, ctx) => {
101
+ const key = opts.correlate?.(ctx) ?? null;
102
+ return bound ?? (key ? await beltic.correlation.resolve(key) : null);
103
+ };
104
+ http?.onProtectedRequest(async (ctx, route) => {
105
+ if (ctx.paymentHeader) return;
106
+ const bound = ctx.adapter.getHeader(SESSION_HEADER) ?? ctx.adapter.getHeader(SESSION_HEADER.toLowerCase());
107
+ const sessionId = await resolveSession(bound ?? null, {
108
+ path: ctx.path,
109
+ method: ctx.method,
110
+ header: (n) => ctx.adapter.getHeader(n)
111
+ });
112
+ if (!sessionId) return;
113
+ const session = await beltic.sessions.ensure(sessionId);
114
+ const accepts = Array.isArray(route.accepts) ? route.accepts[0] : route.accepts;
115
+ await session.emit(
116
+ "payment.requested",
117
+ x402Moments.route(accepts, {
118
+ path: ctx.path,
119
+ method: ctx.method,
120
+ route: JSON.parse(JSON.stringify(route))
121
+ })
122
+ );
123
+ });
124
+ server.onBeforeVerify(async (ctx) => {
125
+ const payload = ctx.paymentPayload;
126
+ const requirements = ctx.requirements;
127
+ const sessionId = await resolveSession(sessionIdOf(payload.extensions), {
128
+ path: payload.resource?.url ?? "",
129
+ method: "PAY",
130
+ header: () => void 0
131
+ });
132
+ const session = await beltic.sessions.ensure(sessionId);
133
+ if (session.born === "seller")
134
+ await session.emit("payment.requested", x402Moments.requirements(requirements));
135
+ const presented = x402Moments.payload(payload);
136
+ await session.emit("payment.presented", presented);
137
+ inFlight.set(payloadDigest(payload), session);
138
+ const out = await beltic.evaluate(session.id, presented);
139
+ if (!out) return;
140
+ opts.onDecision?.({
141
+ sessionId: session.id,
142
+ decision: out.decision,
143
+ reasonCodes: out.reasonCodes,
144
+ born: session.born,
145
+ buyerDecisionId: decisionIdOf(payload.extensions)
146
+ });
147
+ if (out.verdict.blocks(beltic.onReview)) {
148
+ return {
149
+ abort: true,
150
+ reason: `BELTIC_${out.verdict.value}`,
151
+ message: out.reasonCodes.join(",")
152
+ };
153
+ }
154
+ return;
155
+ });
156
+ server.onAfterSettle(async (ctx) => {
157
+ const key = payloadDigest(ctx.paymentPayload);
158
+ const session = inFlight.get(key);
159
+ if (!session) return;
160
+ inFlight.delete(key);
161
+ if (ctx.result.success) await session.close("settled", { transaction: ctx.result.transaction });
162
+ });
163
+ return { inFlight };
164
+ }
165
+
166
+ export {
167
+ SESSION_EXTENSION,
168
+ SESSION_HEADER,
169
+ DECISION_EXTENSION,
170
+ sessionIdOf,
171
+ decisionIdOf,
172
+ x402Currency,
173
+ x402Summary,
174
+ payerOf,
175
+ x402Moments,
176
+ attachX402
177
+ };
@@ -304,6 +304,9 @@ function didKeyFromEd25519(publicKey) {
304
304
  export {
305
305
  toHex,
306
306
  fromHex,
307
+ sha256,
308
+ canonicalize,
309
+ canonicalBytes,
307
310
  payloadDigest,
308
311
  memorySigner,
309
312
  Chain,
package/dist/index.d.ts CHANGED
@@ -1,10 +1,19 @@
1
- export { B as Beltic, a as BelticOptions, C as CorrelationStore, E as Evaluation, M as MemoryCorrelationStore, S as SDK_VERSION, c as createBeltic } from './client-C-mV_3A0.js';
2
- import { S as Session } from './session-5TClPLI4.js';
3
- export { A as AgentIdentity, a as ApiClient, b as ApiClientOptions, B as BelticApiError, C as ChainRejectedError, D as DEFAULT_OPEN_RETRY_MS, c as DEFAULT_TRANSPORT, O as OpenSessionInput, R as RedactFn, d as SessionBorn, e as Sessions, f as StartSessionInput, T as ToolCallSpan, g as Transport, h as TransportClosedError, i as TransportOptions, j as ephemeralIdentity, k as fileIdentity, l as identityFromSeed } from './session-5TClPLI4.js';
4
- import { a as PaymentSummary, J as JsonObject, P as PaymentMomentPayload } from './index-Bjs3BPPU.js';
5
- import './verdict-BAahb5po.js';
1
+ import { a as Session } from './session-gK51QVAM.js';
2
+ export { A as AgentIdentity, b as ApiClient, c as ApiClientOptions, B as Beltic, d as BelticApiError, e as BelticOptions, C as ChainRejectedError, f as CorrelationStore, D as DEFAULT_OPEN_RETRY_MS, g as DEFAULT_TRANSPORT, h as DecideOptions, i as Decision, E as Env, j as Evaluation, H as HumanDecisionInput, M as MemoryCorrelationStore, O as OpenSessionInput, R as RedactFn, k as Run, l as RunOptions, m as SDK_VERSION, n as SessionBorn, S as SessionSource, o as Sessions, p as StartSessionInput, T as ToolCallSpan, q as Transport, r as TransportClosedError, s as TransportOptions, t as createBeltic, u as ephemeralIdentity, v as fileIdentity, w as identityFromSeed } from './session-gK51QVAM.js';
3
+ import { a as PaymentSummary, J as JsonObject, P as PaymentMomentPayload } from './verdict-CDAsxktI.js';
6
4
  import 'zod';
7
5
 
6
+ /**
7
+ * Thrown by the two primitives that cannot fail open (`sessions.start`,
8
+ * `sessions.ensure`) when the client is disabled: a `Beltic.fromEnv` with
9
+ * no `BELTIC_*` configured is a client that records nothing (GAP-78), and
10
+ * a caller that insists on a session gets told so rather than a `null`.
11
+ */
12
+ declare class BelticDisabledError extends Error {
13
+ readonly code = "SDK_DISABLED";
14
+ constructor(entry: string);
15
+ }
16
+
8
17
  /**
9
18
  * The shape the platform judges (`PaymentSummary`) and the shape the record
10
19
  * keeps (`PaymentMomentPayload`) share their comparable core: payee, amount,
@@ -27,4 +36,4 @@ declare function presentedFrom(summary: PaymentSummary, raw: JsonObject): Paymen
27
36
  type CallKind = 'llm_call' | 'tool_call';
28
37
  declare function recordCall<T>(session: Session, kind: CallKind, callId: string, start: JsonObject, run: () => PromiseLike<T>, end?: (result: T) => Promise<JsonObject> | JsonObject): Promise<T>;
29
38
 
30
- export { type CallKind, Session, presentedFrom, recordCall, summaryOf };
39
+ export { BelticDisabledError, type CallKind, Session, presentedFrom, recordCall, summaryOf };