@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.
- package/dist/{adapter-CF-cUYSA.d.ts → adapter-CLy44CD2.d.ts} +4 -3
- package/dist/ai/index.d.ts +5 -5
- package/dist/ai/index.js +49 -39
- package/dist/{chunk-46QN2KEZ.js → chunk-4BUUPU3O.js} +0 -9
- package/dist/chunk-4MG6VNAU.js +276 -0
- package/dist/chunk-WY5ZX4BD.js +177 -0
- package/dist/{chunk-SFGM7KOG.js → chunk-X3W2Z5GC.js} +3 -0
- package/dist/index.d.ts +15 -6
- package/dist/index.js +329 -228
- package/dist/protocol/index.d.ts +2 -3
- package/dist/protocol/index.js +1 -5
- package/dist/session-gK51QVAM.d.ts +484 -0
- package/dist/{index-Bjs3BPPU.d.ts → verdict-CDAsxktI.d.ts} +25 -17
- package/dist/x402/hono.d.ts +16 -7
- package/dist/x402/hono.js +14 -8
- package/dist/x402/index.d.ts +22 -46
- package/dist/x402/index.js +33 -53
- package/package.json +2 -30
- package/dist/chunk-7G5EHNVW.js +0 -17
- package/dist/chunk-GSH5APZW.js +0 -39
- package/dist/chunk-LM4NIYE5.js +0 -92
- package/dist/chunk-SO6HPRJT.js +0 -82
- package/dist/chunk-U7HM37CB.js +0 -17
- package/dist/chunk-VM7MK43J.js +0 -13
- package/dist/client-C-mV_3A0.d.ts +0 -91
- package/dist/mcp/index.d.ts +0 -60
- package/dist/mcp/index.js +0 -130
- package/dist/middleware-BwAYpBTl.d.ts +0 -15
- package/dist/seller/anti-fraud-gateway.d.ts +0 -42
- package/dist/seller/anti-fraud-gateway.js +0 -38
- package/dist/session-5TClPLI4.d.ts +0 -250
- package/dist/verdict-BAahb5po.d.ts +0 -26
- package/dist/x402/express.d.ts +0 -15
- package/dist/x402/express.js +0 -27
package/dist/index.js
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
Verdict
|
|
3
|
+
} from "./chunk-4BUUPU3O.js";
|
|
4
|
+
import {
|
|
5
|
+
BelticDisabledError,
|
|
6
|
+
DEFAULT_OPEN_RETRY_MS,
|
|
7
|
+
Session,
|
|
8
|
+
Sessions,
|
|
3
9
|
recordCall
|
|
4
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-4MG6VNAU.js";
|
|
5
11
|
import {
|
|
6
|
-
|
|
12
|
+
canonicalBytes,
|
|
13
|
+
canonicalize,
|
|
7
14
|
didKeyFromEd25519,
|
|
8
15
|
fromHex,
|
|
9
16
|
memorySigner,
|
|
17
|
+
sha256,
|
|
10
18
|
toHex
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import {
|
|
13
|
-
Verdict
|
|
14
|
-
} from "./chunk-46QN2KEZ.js";
|
|
15
|
-
import {
|
|
16
|
-
presentedFrom,
|
|
17
|
-
summaryOf
|
|
18
|
-
} from "./chunk-7G5EHNVW.js";
|
|
19
|
+
} from "./chunk-X3W2Z5GC.js";
|
|
19
20
|
|
|
20
21
|
// src/core/api-client.ts
|
|
21
22
|
var BelticApiError = class extends Error {
|
|
@@ -115,212 +116,269 @@ var MemoryCorrelationStore = class {
|
|
|
115
116
|
}
|
|
116
117
|
};
|
|
117
118
|
|
|
118
|
-
// src/core/
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
119
|
+
// src/core/identity.ts
|
|
120
|
+
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
121
|
+
import { dirname } from "path";
|
|
122
|
+
function identityFromSeed(seed, credential) {
|
|
123
|
+
const signer = memorySigner(seed);
|
|
124
|
+
const did = didKeyFromEd25519(signer.publicKey);
|
|
125
|
+
return { did, signer: { ...signer, keyId: did }, ...credential ? { credential } : {} };
|
|
126
|
+
}
|
|
127
|
+
function ephemeralIdentity(credential) {
|
|
128
|
+
return identityFromSeed(memorySigner().seed, credential);
|
|
129
|
+
}
|
|
130
|
+
function fileIdentity(path, credential) {
|
|
131
|
+
let seed;
|
|
132
|
+
try {
|
|
133
|
+
seed = fromHex(JSON.parse(readFileSync(path, "utf8")).seed);
|
|
134
|
+
} catch {
|
|
135
|
+
seed = memorySigner().seed;
|
|
136
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
137
|
+
writeFileSync(path, `${JSON.stringify({ seed: toHex(seed) })}
|
|
138
|
+
`, { mode: 384 });
|
|
139
|
+
}
|
|
140
|
+
return identityFromSeed(seed, credential);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/core/payment-moment.ts
|
|
144
|
+
function summaryOf(m) {
|
|
145
|
+
return {
|
|
146
|
+
protocol: m.protocol,
|
|
147
|
+
payee: m.payee,
|
|
148
|
+
amount: { value: m.amount.value, currency: m.amount.currency },
|
|
149
|
+
...m.payer ? { payer: m.payer } : {}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function presentedFrom(summary, raw) {
|
|
153
|
+
return { ...summary, artifact: "payment-signature", raw };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/core/decision.ts
|
|
157
|
+
var Decision = class _Decision {
|
|
158
|
+
constructor(evaluation) {
|
|
159
|
+
this.evaluation = evaluation;
|
|
138
160
|
}
|
|
139
|
-
|
|
140
|
-
|
|
161
|
+
static ABSENT = new _Decision(null);
|
|
162
|
+
static of(evaluation) {
|
|
163
|
+
return new _Decision(evaluation);
|
|
141
164
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
* acknowledged. `false` when the event was dropped, or (fail-open) when
|
|
145
|
-
* the chain can no longer take it.
|
|
146
|
-
*/
|
|
147
|
-
async emit(kind, payload) {
|
|
148
|
-
if (!this.deps.failOpen) return this.chainEvent(kind, payload);
|
|
149
|
-
try {
|
|
150
|
-
return await this.chainEvent(kind, payload);
|
|
151
|
-
} catch (err) {
|
|
152
|
-
this.deps.onError?.(err);
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
165
|
+
static absent() {
|
|
166
|
+
return _Decision.ABSENT;
|
|
155
167
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const { callId, ...start } = call;
|
|
159
|
-
return openCall(this, "tool_call", callId, { transport: "local", ...start });
|
|
160
|
-
}
|
|
161
|
-
async chainEvent(kind, payload) {
|
|
162
|
-
const halted = this.deps.transport.haltedError(this.id, this.source);
|
|
163
|
-
if (halted) throw halted;
|
|
164
|
-
const ts = this.now().toISOString();
|
|
165
|
-
if (!this.deps.transport.hasRoom()) {
|
|
166
|
-
this.dropped++;
|
|
167
|
-
this.droppedFirstTs ??= ts;
|
|
168
|
-
this.droppedLastTs = ts;
|
|
169
|
-
return false;
|
|
170
|
-
}
|
|
171
|
-
if (this.dropped > 0) {
|
|
172
|
-
this.deps.transport.enqueue(
|
|
173
|
-
await this.next(
|
|
174
|
-
"transport.gap",
|
|
175
|
-
{ dropped: this.dropped, firstTs: this.droppedFirstTs, lastTs: this.droppedLastTs },
|
|
176
|
-
ts
|
|
177
|
-
)
|
|
178
|
-
);
|
|
179
|
-
this.dropped = 0;
|
|
180
|
-
this.droppedFirstTs = this.droppedLastTs = null;
|
|
181
|
-
}
|
|
182
|
-
const body = payload;
|
|
183
|
-
this.deps.transport.enqueue(
|
|
184
|
-
await this.next(kind, this.deps.redact ? this.deps.redact(kind, body) : body, ts)
|
|
185
|
-
);
|
|
186
|
-
return true;
|
|
168
|
+
get value() {
|
|
169
|
+
return this.evaluation?.decision ?? null;
|
|
187
170
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
this.closed = true;
|
|
191
|
-
await this.emit("session.close", { reason, ...extra });
|
|
192
|
-
await this.flush();
|
|
193
|
-
this.deps.onClosed?.(this);
|
|
171
|
+
get reasonCodes() {
|
|
172
|
+
return this.evaluation?.reasonCodes ?? [];
|
|
194
173
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
174
|
+
get decisionId() {
|
|
175
|
+
return this.evaluation?.decisionId ?? null;
|
|
176
|
+
}
|
|
177
|
+
get allowed() {
|
|
178
|
+
return this.value === "ALLOW";
|
|
179
|
+
}
|
|
180
|
+
get denied() {
|
|
181
|
+
return this.value === "DENY";
|
|
182
|
+
}
|
|
183
|
+
get review() {
|
|
184
|
+
return this.value === "REVIEW";
|
|
185
|
+
}
|
|
186
|
+
get absent() {
|
|
187
|
+
return this.evaluation === null;
|
|
188
|
+
}
|
|
189
|
+
/** Whether a gate must stop the payment (GAP-52); an absent verdict never blocks. */
|
|
190
|
+
blocks(onReview) {
|
|
191
|
+
return this.evaluation ? Verdict.of(this.evaluation.decision).blocks(onReview) : false;
|
|
192
|
+
}
|
|
193
|
+
/** One sentence for the agent or the person: what Beltic said and why. */
|
|
194
|
+
explain() {
|
|
195
|
+
if (!this.evaluation) return "Beltic could not be asked about this payment.";
|
|
196
|
+
const verb = this.denied ? "denied" : this.review ? "asked for review of" : "allowed";
|
|
197
|
+
const why = this.reasonCodes.length > 0 ? ` (${this.reasonCodes.join(", ")})` : "";
|
|
198
|
+
return `Beltic ${verb} this payment${why}.`;
|
|
208
199
|
}
|
|
209
200
|
};
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
201
|
+
|
|
202
|
+
// src/core/run.ts
|
|
203
|
+
var MEMORY = 256;
|
|
204
|
+
var Memory = class {
|
|
205
|
+
map = /* @__PURE__ */ new Map();
|
|
206
|
+
get(key) {
|
|
207
|
+
return this.map.get(key);
|
|
208
|
+
}
|
|
209
|
+
set(key, value) {
|
|
210
|
+
this.map.delete(key);
|
|
211
|
+
this.map.set(key, value);
|
|
212
|
+
if (this.map.size > MEMORY) this.map.delete(this.map.keys().next().value);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
var Run = class _Run {
|
|
216
|
+
constructor(deps, key, opts = {}) {
|
|
213
217
|
this.deps = deps;
|
|
218
|
+
this.key = key;
|
|
219
|
+
this.opts = opts;
|
|
220
|
+
}
|
|
221
|
+
opened = null;
|
|
222
|
+
current = null;
|
|
223
|
+
/** JCS hash of the mandate on the chain, and of the one the open input carried. */
|
|
224
|
+
declared = null;
|
|
225
|
+
openedWith = null;
|
|
226
|
+
closed = false;
|
|
227
|
+
timer = null;
|
|
228
|
+
calls = new Memory();
|
|
229
|
+
decisions = new Memory();
|
|
230
|
+
byPayment = new Memory();
|
|
231
|
+
/** The session this run records into — opened on first use, `null` when there is none. */
|
|
232
|
+
session() {
|
|
233
|
+
const next = this.deps.sessions.open(this.key, this.opener);
|
|
234
|
+
this.opened = next;
|
|
235
|
+
return next.then((session) => {
|
|
236
|
+
if (session !== this.current) {
|
|
237
|
+
this.current = session;
|
|
238
|
+
this.declared = this.openedWith;
|
|
239
|
+
}
|
|
240
|
+
return session;
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
opener = async () => {
|
|
244
|
+
const open = this.opts.open;
|
|
245
|
+
const input = typeof open === "function" ? await open() : open ?? {};
|
|
246
|
+
this.openedWith = input.intent ? _Run.hash(input.intent) : null;
|
|
247
|
+
return input;
|
|
248
|
+
};
|
|
249
|
+
/** `intent.declared`, unless the mandate is the one already on the chain. */
|
|
250
|
+
async declare(intent) {
|
|
251
|
+
const session = await this.session();
|
|
252
|
+
if (!session) return false;
|
|
253
|
+
const hash = _Run.hash(intent);
|
|
254
|
+
if (hash === this.declared) return false;
|
|
255
|
+
const ok = await session.emit("intent.declared", intent);
|
|
256
|
+
if (ok) this.declared = hash;
|
|
257
|
+
this.touch();
|
|
258
|
+
return ok;
|
|
214
259
|
}
|
|
215
260
|
/**
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
261
|
+
* The platform's verdict on a payment about to be presented. Asked once
|
|
262
|
+
* per call id: a host that re-runs its approval step reads the same
|
|
263
|
+
* `Decision`. An absent verdict is not memoized, so the next attempt
|
|
264
|
+
* asks again.
|
|
220
265
|
*/
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
266
|
+
async decide(payment, opts = {}) {
|
|
267
|
+
const known = opts.callId ? this.decisions.get(opts.callId) : void 0;
|
|
268
|
+
if (known) return known;
|
|
269
|
+
const session = await this.session();
|
|
270
|
+
if (!session) return Decision.absent();
|
|
271
|
+
if (opts.intent) await this.declare(opts.intent);
|
|
272
|
+
const summary = summaryOf(payment);
|
|
273
|
+
const evaluation = await this.deps.evaluate(session.id, summary);
|
|
274
|
+
if (!evaluation) return Decision.absent();
|
|
275
|
+
const decision = Decision.of(evaluation);
|
|
276
|
+
if (opts.callId) this.decisions.set(opts.callId, decision);
|
|
277
|
+
for (const key of _Run.paymentKeys(summary)) this.byPayment.set(key, decision);
|
|
278
|
+
const call = opts.callId ? this.calls.get(opts.callId)?.call : void 0;
|
|
279
|
+
await session.emit("gateway.decision", {
|
|
280
|
+
gateway: "beltic",
|
|
281
|
+
call: _Run.callOf(call),
|
|
282
|
+
decision: evaluation.decision,
|
|
283
|
+
reasonCodes: [...evaluation.reasonCodes],
|
|
284
|
+
record: {
|
|
285
|
+
decisionId: evaluation.decisionId,
|
|
286
|
+
callId: opts.callId ?? null,
|
|
287
|
+
payment: summary
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
this.touch();
|
|
291
|
+
return decision;
|
|
292
|
+
}
|
|
293
|
+
/** The decision given for a call id, or absent. */
|
|
294
|
+
decision(callId) {
|
|
295
|
+
return this.decisions.get(callId) ?? Decision.absent();
|
|
296
|
+
}
|
|
225
297
|
/**
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
* the same key; a closed key is forgotten. When the platform refuses to
|
|
230
|
-
* open one, a fail-open client resolves null — the host runs without
|
|
231
|
-
* evidence — until `openRetryMs` has passed (GAP-71); otherwise the
|
|
232
|
-
* refusal is thrown and the next call tries again. The identity is
|
|
233
|
-
* required either way: that is configuration.
|
|
298
|
+
* The decision given for a payment with the same comparable core (payee,
|
|
299
|
+
* amount, payer — or payee and amount when one side names no payer), or
|
|
300
|
+
* absent.
|
|
234
301
|
*/
|
|
235
|
-
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
);
|
|
240
|
-
this.opened.set(key, next);
|
|
241
|
-
next.catch(() => this.forget(key, next));
|
|
242
|
-
return next;
|
|
243
|
-
}
|
|
244
|
-
forget(key, entry) {
|
|
245
|
-
if (this.opened.get(key) === entry) this.opened.delete(key);
|
|
246
|
-
}
|
|
247
|
-
async openFresh(input, onClosed) {
|
|
248
|
-
this.identityFor("open");
|
|
249
|
-
const create = async () => this.create(typeof input === "function" ? await input() : input, onClosed);
|
|
250
|
-
if (!this.deps.failOpen) return create();
|
|
251
|
-
if (Date.now() < this.retryAt) return null;
|
|
252
|
-
try {
|
|
253
|
-
const session = await create();
|
|
254
|
-
this.retryAt = 0;
|
|
255
|
-
return session;
|
|
256
|
-
} catch (err) {
|
|
257
|
-
this.retryAt = Date.now() + (this.deps.openRetryMs ?? DEFAULT_OPEN_RETRY_MS);
|
|
258
|
-
this.deps.onError?.(err);
|
|
259
|
-
return null;
|
|
302
|
+
decisionFor(payment) {
|
|
303
|
+
for (const key of _Run.paymentKeys(summaryOf(payment))) {
|
|
304
|
+
const known = this.byPayment.get(key);
|
|
305
|
+
if (known) return known;
|
|
260
306
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
if (
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
307
|
+
return Decision.absent();
|
|
308
|
+
}
|
|
309
|
+
/** A tool call the host runs itself, reported as two events by its own call id. */
|
|
310
|
+
tools = {
|
|
311
|
+
start: async (call) => {
|
|
312
|
+
const session = await this.session();
|
|
313
|
+
if (!session) return false;
|
|
314
|
+
const span = session.toolCall(call);
|
|
315
|
+
this.calls.set(call.callId, { call, span });
|
|
316
|
+
this.touch();
|
|
317
|
+
return span.opened;
|
|
318
|
+
},
|
|
319
|
+
end: async (callId, outcome) => {
|
|
320
|
+
const span = this.take(callId);
|
|
321
|
+
if (!span) return false;
|
|
322
|
+
this.touch();
|
|
323
|
+
return span.end(outcome);
|
|
324
|
+
},
|
|
325
|
+
fail: async (callId, error, outcome) => {
|
|
326
|
+
const span = this.take(callId);
|
|
327
|
+
if (!span) return false;
|
|
328
|
+
this.touch();
|
|
329
|
+
return span.fail(error, outcome);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
/** A person's answer about a call, as the decision it was (GAP-75). */
|
|
333
|
+
async humanDecided(callId, input) {
|
|
334
|
+
const session = await this.session();
|
|
335
|
+
if (!session) return false;
|
|
336
|
+
const ok = await session.emit("gateway.decision", {
|
|
337
|
+
gateway: "human",
|
|
338
|
+
call: _Run.callOf(this.calls.get(callId)?.call),
|
|
339
|
+
decision: input.allowed ? "ALLOW" : "DENY",
|
|
340
|
+
reasonCodes: [`USER_${input.outcome.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}`],
|
|
341
|
+
record: {
|
|
342
|
+
callId,
|
|
343
|
+
outcome: input.outcome,
|
|
344
|
+
responder: input.responder ?? null,
|
|
345
|
+
...input.record ?? {}
|
|
346
|
+
}
|
|
297
347
|
});
|
|
298
|
-
|
|
348
|
+
this.touch();
|
|
349
|
+
return ok;
|
|
299
350
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
if (
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
351
|
+
async close(reason = "completed") {
|
|
352
|
+
if (this.closed) return;
|
|
353
|
+
this.closed = true;
|
|
354
|
+
if (this.timer) clearTimeout(this.timer);
|
|
355
|
+
this.deps.onClosed(this);
|
|
356
|
+
const session = this.opened ? await this.opened.catch(() => null) : null;
|
|
357
|
+
await session?.close(reason);
|
|
358
|
+
}
|
|
359
|
+
take(callId) {
|
|
360
|
+
const known = this.calls.get(callId);
|
|
361
|
+
if (!known?.span) return null;
|
|
362
|
+
const { span } = known;
|
|
363
|
+
known.span = null;
|
|
364
|
+
return span;
|
|
365
|
+
}
|
|
366
|
+
touch() {
|
|
367
|
+
if (!this.opts.idleMs || this.closed) return;
|
|
368
|
+
if (this.timer) clearTimeout(this.timer);
|
|
369
|
+
this.timer = setTimeout(() => void this.close("expired"), this.opts.idleMs);
|
|
370
|
+
this.timer.unref?.();
|
|
371
|
+
}
|
|
372
|
+
static hash(intent) {
|
|
373
|
+
return toHex(sha256(canonicalBytes(intent)));
|
|
374
|
+
}
|
|
375
|
+
/** With the payer first, then without it. */
|
|
376
|
+
static paymentKeys(summary) {
|
|
377
|
+
const { payer, ...core } = summary;
|
|
378
|
+
return payer ? [canonicalize(summary), canonicalize(core)] : [canonicalize(core)];
|
|
379
|
+
}
|
|
380
|
+
static callOf(call) {
|
|
381
|
+
return call ? { tool: call.toolName, args: call.input } : { tool: "unknown" };
|
|
324
382
|
}
|
|
325
383
|
};
|
|
326
384
|
|
|
@@ -486,8 +544,10 @@ var Transport = class {
|
|
|
486
544
|
};
|
|
487
545
|
|
|
488
546
|
// src/client.ts
|
|
489
|
-
var SDK_VERSION = "0.
|
|
490
|
-
var
|
|
547
|
+
var SDK_VERSION = "0.4.0";
|
|
548
|
+
var ENV_REQUIRED = ["BELTIC_API_KEY", "BELTIC_BASE_URL", "BELTIC_AGENT_SEED"];
|
|
549
|
+
var ENV_CREDENTIAL = "BELTIC_AGENT_CREDENTIAL";
|
|
550
|
+
var Beltic = class _Beltic {
|
|
491
551
|
api;
|
|
492
552
|
transport;
|
|
493
553
|
sessions;
|
|
@@ -495,8 +555,43 @@ var Beltic = class {
|
|
|
495
555
|
correlation;
|
|
496
556
|
onReview;
|
|
497
557
|
failOpen;
|
|
558
|
+
/** `false` for a disabled client (GAP-78): nothing is posted, `run`/`sessions.open` answer without a session. */
|
|
559
|
+
enabled;
|
|
498
560
|
onError;
|
|
561
|
+
runs = /* @__PURE__ */ new Map();
|
|
562
|
+
/**
|
|
563
|
+
* The client the environment describes: `BELTIC_API_KEY`,
|
|
564
|
+
* `BELTIC_BASE_URL`, `BELTIC_AGENT_SEED` (64 hex) and optionally
|
|
565
|
+
* `BELTIC_AGENT_CREDENTIAL`, fail-open by default. None set → a disabled
|
|
566
|
+
* client; some set → a configuration error, thrown (GAP-78).
|
|
567
|
+
*/
|
|
568
|
+
static fromEnv(env = _Beltic.processEnv(), opts = {}) {
|
|
569
|
+
const missing = ENV_REQUIRED.filter((name) => !env[name]);
|
|
570
|
+
if (missing.length === ENV_REQUIRED.length) return _Beltic.disabled(opts);
|
|
571
|
+
if (missing.length > 0)
|
|
572
|
+
throw new Error(
|
|
573
|
+
`Beltic.fromEnv: ${missing.join(", ")} missing \u2014 set all of ${ENV_REQUIRED.join(", ")}, or none to disable`
|
|
574
|
+
);
|
|
575
|
+
return new _Beltic({
|
|
576
|
+
failOpen: true,
|
|
577
|
+
...opts,
|
|
578
|
+
apiKey: env.BELTIC_API_KEY,
|
|
579
|
+
baseUrl: env.BELTIC_BASE_URL,
|
|
580
|
+
identity: identityFromSeed(fromHex(env.BELTIC_AGENT_SEED), env[ENV_CREDENTIAL])
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
/** A client that records nothing and never throws into the work: the null object for "no evidence stream" (GAP-78). */
|
|
584
|
+
static disabled(opts = {}) {
|
|
585
|
+
return new _Beltic({
|
|
586
|
+
...opts,
|
|
587
|
+
apiKey: "",
|
|
588
|
+
baseUrl: "http://beltic.disabled.invalid",
|
|
589
|
+
failOpen: true,
|
|
590
|
+
enabled: false
|
|
591
|
+
});
|
|
592
|
+
}
|
|
499
593
|
constructor(opts) {
|
|
594
|
+
this.enabled = opts.enabled ?? true;
|
|
500
595
|
this.failOpen = opts.failOpen ?? false;
|
|
501
596
|
this.onError = opts.onError ?? ((err) => console.error("[beltic]", err));
|
|
502
597
|
this.api = new ApiClient({ ...opts, userAgent: `@belticlabs/agent-risk-sdk/${SDK_VERSION}` });
|
|
@@ -514,7 +609,8 @@ var Beltic = class {
|
|
|
514
609
|
now: opts.now,
|
|
515
610
|
failOpen: this.failOpen,
|
|
516
611
|
onError: this.onError,
|
|
517
|
-
openRetryMs: opts.openRetryMs
|
|
612
|
+
openRetryMs: opts.openRetryMs,
|
|
613
|
+
enabled: this.enabled
|
|
518
614
|
});
|
|
519
615
|
this.identity = opts.identity;
|
|
520
616
|
this.correlation = opts.correlation ?? new MemoryCorrelationStore();
|
|
@@ -529,6 +625,7 @@ var Beltic = class {
|
|
|
529
625
|
* platform could not be asked.
|
|
530
626
|
*/
|
|
531
627
|
async evaluate(sessionId, payment) {
|
|
628
|
+
if (!this.enabled) return null;
|
|
532
629
|
const input = { sessionId, payment: summaryOf(payment) };
|
|
533
630
|
if (!this.failOpen) return this.decide(input);
|
|
534
631
|
try {
|
|
@@ -543,48 +640,52 @@ var Beltic = class {
|
|
|
543
640
|
const out = await this.api.post("/v1/evaluate", input);
|
|
544
641
|
return { ...out, verdict: Verdict.of(out.decision) };
|
|
545
642
|
}
|
|
643
|
+
/**
|
|
644
|
+
* The run for a key of the host's own — one object per key until it
|
|
645
|
+
* closes (the options count on the first call only). See `Run`.
|
|
646
|
+
*/
|
|
647
|
+
run(key, opts = {}) {
|
|
648
|
+
const existing = this.runs.get(key);
|
|
649
|
+
if (existing) return existing;
|
|
650
|
+
const run = new Run(
|
|
651
|
+
{
|
|
652
|
+
sessions: this.sessions,
|
|
653
|
+
evaluate: (sessionId, payment) => this.evaluate(sessionId, payment),
|
|
654
|
+
onClosed: (closed) => {
|
|
655
|
+
if (this.runs.get(key) === closed) this.runs.delete(key);
|
|
656
|
+
}
|
|
657
|
+
},
|
|
658
|
+
key,
|
|
659
|
+
opts
|
|
660
|
+
);
|
|
661
|
+
this.runs.set(key, run);
|
|
662
|
+
return run;
|
|
663
|
+
}
|
|
546
664
|
flush() {
|
|
547
665
|
return this.transport.flush();
|
|
548
666
|
}
|
|
549
667
|
shutdown() {
|
|
550
668
|
return this.transport.close();
|
|
551
669
|
}
|
|
670
|
+
/** `process.env` where there is a `process` (Node); `{}` on workerd, where the shell passes its `env`. */
|
|
671
|
+
static processEnv() {
|
|
672
|
+
return globalThis.process?.env ?? {};
|
|
673
|
+
}
|
|
552
674
|
};
|
|
553
675
|
function createBeltic(opts) {
|
|
554
676
|
return new Beltic(opts);
|
|
555
677
|
}
|
|
556
|
-
|
|
557
|
-
// src/core/identity.ts
|
|
558
|
-
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
559
|
-
import { dirname } from "path";
|
|
560
|
-
function identityFromSeed(seed, credential) {
|
|
561
|
-
const signer = memorySigner(seed);
|
|
562
|
-
const did = didKeyFromEd25519(signer.publicKey);
|
|
563
|
-
return { did, signer: { ...signer, keyId: did }, ...credential ? { credential } : {} };
|
|
564
|
-
}
|
|
565
|
-
function ephemeralIdentity(credential) {
|
|
566
|
-
return identityFromSeed(memorySigner().seed, credential);
|
|
567
|
-
}
|
|
568
|
-
function fileIdentity(path, credential) {
|
|
569
|
-
let seed;
|
|
570
|
-
try {
|
|
571
|
-
seed = fromHex(JSON.parse(readFileSync(path, "utf8")).seed);
|
|
572
|
-
} catch {
|
|
573
|
-
seed = memorySigner().seed;
|
|
574
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
575
|
-
writeFileSync(path, `${JSON.stringify({ seed: toHex(seed) })}
|
|
576
|
-
`, { mode: 384 });
|
|
577
|
-
}
|
|
578
|
-
return identityFromSeed(seed, credential);
|
|
579
|
-
}
|
|
580
678
|
export {
|
|
581
679
|
ApiClient,
|
|
582
680
|
Beltic,
|
|
583
681
|
BelticApiError,
|
|
682
|
+
BelticDisabledError,
|
|
584
683
|
ChainRejectedError,
|
|
585
684
|
DEFAULT_OPEN_RETRY_MS,
|
|
586
685
|
DEFAULT_TRANSPORT,
|
|
686
|
+
Decision,
|
|
587
687
|
MemoryCorrelationStore,
|
|
688
|
+
Run,
|
|
588
689
|
SDK_VERSION,
|
|
589
690
|
Session,
|
|
590
691
|
Sessions,
|
package/dist/protocol/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { b as JsonValue, E as EvidenceSourceAll } from '../
|
|
2
|
-
export { A as ALL_SOURCES, c as ANOMALY_TYPES, d as Amount, e as AmountSchema, f as
|
|
1
|
+
import { b as JsonValue, E as EvidenceSourceAll } from '../verdict-CDAsxktI.js';
|
|
2
|
+
export { A as ALL_SOURCES, c as ANOMALY_TYPES, d as Amount, e as AmountSchema, f as ApiError, g as ApiErrorSchema, C as ChainHead, h as ChainHeadSchema, i as CreatePolicyInput, j as CreatePolicyInputSchema, k as CreatePolicyOutput, l as CreatePolicyOutputSchema, m as CreateSessionInput, n as CreateSessionInputSchema, o as CreateSessionOutput, p as CreateSessionOutputSchema, D as Decision, q as DecisionSchema, r as DeclaredIntent, s as DeclaredIntentSchema, t as DigestedEnvelope, u as EvaluateInput, v as EvaluateInputSchema, w as EvaluateOutput, x as EvaluateOutputSchema, y as EventResult, z as EventResultSchema, B as EventResultStatus, F as EventResultStatusSchema, G as EvidenceAck, H as EvidenceAckSchema, I as EvidenceBatchInput, K as EvidenceBatchInputSchema, L as EvidenceEnvelope, M as EvidenceEvent, N as EvidenceEventSchema, O as EvidenceKind, Q as EvidenceKindSchema, R as EvidenceSource, S as EvidenceSourceAllSchema, T as EvidenceSourceSchema, U as GatewayDecisionPayload, V as GatewayDecisionPayloadSchema, W as Hex64, X as Hex64Schema, Y as IntentDeclaredPayload, Z as IntentDeclaredPayloadSchema, J as JsonObject, _ as JsonValueSchema, $ as LlmCallEndPayload, a0 as LlmCallEndPayloadSchema, a1 as LlmCallStartPayload, a2 as LlmCallStartPayloadSchema, a3 as MAX_BATCH_EVENTS, a4 as OnReview, a5 as PAYMENT_ARTIFACTS, a6 as PLATFORM_KINDS, a7 as PayloadByKind, P as PaymentMomentPayload, a8 as PaymentMomentPayloadSchema, a as PaymentSummary, a9 as PaymentSummarySchema, aa as PlatformAnomalyPayload, ab as PlatformAnomalyPayloadSchema, ac as PlatformEvidenceKind, ad as PlatformEvidenceKindSchema, ae as PlatformObservationPayload, af as PlatformObservationPayloadSchema, ag as SESSION_CLOSE_REASONS, ah as SOURCE_ORDER, ai as SeqSchema, aj as SessionClosePayload, ak as SessionClosePayloadSchema, al as SessionIdSchema, am as SessionOpenPayload, an as SessionOpenPayloadSchema, ao as Sig, ap as SigSchema, aq as TimestampSchema, ar as ToolCallEndPayload, as as ToolCallEndPayloadSchema, at as ToolCallStartPayload, au as ToolCallStartPayloadSchema, av as TransportGapPayload, aw as TransportGapPayloadSchema, ax as Verdict, ay as WIRE_KINDS, az as WIRE_SOURCES, aA as WireEvidenceKind, aB as WireEvidenceKindSchema, aC as compareBySessionSource, aD as compareBySourceSeq, aE as isPlatformKind, aF as isWireKind, aG as payloadSchemaFor } from '../verdict-CDAsxktI.js';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
export { O as OnReview, V as Verdict } from '../verdict-BAahb5po.js';
|
|
5
4
|
|
|
6
5
|
declare const PRIMITIVES: readonly ["THRESHOLD", "MEMBERSHIP", "MATCH", "PRESENCE", "FRESHNESS"];
|
|
7
6
|
declare const PrimitiveSchema: z.ZodEnum<{
|