@verax-ai/proxy 0.1.0 → 0.1.2
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/LICENSE +192 -0
- package/README.md +46 -0
- package/dist/approvals.d.ts +30 -1
- package/dist/approvals.js +64 -3
- package/dist/atomic-write.d.ts +1 -0
- package/dist/atomic-write.js +65 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -2
- package/dist/inputs.d.ts +7 -1
- package/dist/inputs.js +35 -18
- package/dist/jsonl-tail.d.ts +26 -0
- package/dist/jsonl-tail.js +218 -0
- package/dist/ledger-manifest.d.ts +88 -0
- package/dist/ledger-manifest.js +225 -0
- package/dist/ledger.d.ts +88 -9
- package/dist/ledger.js +541 -86
- package/dist/policy.js +9 -1
- package/dist/proxy.js +235 -166
- package/dist/serial-queue.d.ts +5 -0
- package/dist/serial-queue.js +9 -0
- package/dist/types.d.ts +3 -0
- package/package.json +11 -1
package/dist/policy.js
CHANGED
|
@@ -197,7 +197,15 @@ export function loadPolicy(json) {
|
|
|
197
197
|
if (id === null)
|
|
198
198
|
return null;
|
|
199
199
|
const found = document.rules.find((r) => r.id === id);
|
|
200
|
-
return found
|
|
200
|
+
return found
|
|
201
|
+
? {
|
|
202
|
+
id: found.id,
|
|
203
|
+
text: found.text,
|
|
204
|
+
...(found.spend?.dailyMaxMinor !== undefined
|
|
205
|
+
? { spend: { dailyMaxMinor: found.spend.dailyMaxMinor } }
|
|
206
|
+
: {}),
|
|
207
|
+
}
|
|
208
|
+
: null;
|
|
201
209
|
},
|
|
202
210
|
evaluate(call, principal, ctx) {
|
|
203
211
|
if (call.name === "pay") {
|
package/dist/proxy.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { signDecisionRecord } from "@cedulon/core";
|
|
4
|
-
import { approvePending, approvalsLogFor, drainApprovalCommands } from "./approvals.js";
|
|
4
|
+
import { approvePending, approvalsLogFor, createApprovalBudgetGuard, drainApprovalCommands, spentTodayMinorOf, } from "./approvals.js";
|
|
5
|
+
import { SerialQueue } from "./serial-queue.js";
|
|
5
6
|
import { diskProbe } from "./disk.js";
|
|
6
7
|
import { effectDescriptor, sha256Canonical } from "./hash.js";
|
|
7
8
|
import { inputsLogFor } from "./inputs.js";
|
|
@@ -129,6 +130,13 @@ function toolCallOf(call) {
|
|
|
129
130
|
export function createProxy(deps) {
|
|
130
131
|
const inputsLog = deps.inputsLog ?? inputsLogFor(deps.ledger);
|
|
131
132
|
const approvals = approvalsLogFor(deps.ledger);
|
|
133
|
+
const admission = new SerialQueue();
|
|
134
|
+
const inFlight = new Map();
|
|
135
|
+
const budgetGuard = createApprovalBudgetGuard({
|
|
136
|
+
policy: deps.policy,
|
|
137
|
+
approvals,
|
|
138
|
+
now: deps.now,
|
|
139
|
+
});
|
|
132
140
|
deps.ledger.effectSigner = deps.effectSigner;
|
|
133
141
|
const ledgerDir = deps.ledger.dir;
|
|
134
142
|
const stateDir = typeof ledgerDir === "string" ? ledgerDir : null;
|
|
@@ -192,30 +200,6 @@ export function createProxy(deps) {
|
|
|
192
200
|
return "daily-limited";
|
|
193
201
|
return null;
|
|
194
202
|
}
|
|
195
|
-
function utcDayStart(ms) {
|
|
196
|
-
const d = new Date(ms);
|
|
197
|
-
return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
|
|
198
|
-
}
|
|
199
|
-
function spentTodayMinorOf(rows, nowMs, currency) {
|
|
200
|
-
const start = utcDayStart(nowMs);
|
|
201
|
-
const end = start + 86_400_000;
|
|
202
|
-
let sum = 0;
|
|
203
|
-
for (const row of rows) {
|
|
204
|
-
if (row.subject !== "spend")
|
|
205
|
-
continue;
|
|
206
|
-
if (row.status !== "pending" && row.status !== "approved")
|
|
207
|
-
continue;
|
|
208
|
-
const created = row.createdAtMs ?? row.expiresAtMs - deps.policy.approvalTtlMs;
|
|
209
|
-
if (created < start || created >= end)
|
|
210
|
-
continue;
|
|
211
|
-
if (row.args.currency !== currency)
|
|
212
|
-
continue;
|
|
213
|
-
const amt = row.args.amountMinor;
|
|
214
|
-
if (typeof amt === "number")
|
|
215
|
-
sum += amt;
|
|
216
|
-
}
|
|
217
|
-
return sum;
|
|
218
|
-
}
|
|
219
203
|
async function spendReauth(requestHash, inputs, timestampMs, subject) {
|
|
220
204
|
const existingReauth = await lookupReauthByHash(deps.ledger, requestHash);
|
|
221
205
|
if (existingReauth)
|
|
@@ -309,6 +293,38 @@ export function createProxy(deps) {
|
|
|
309
293
|
reasonCode: null,
|
|
310
294
|
};
|
|
311
295
|
}
|
|
296
|
+
function launchInner(dispatchedCall, principal, allowRef, flightKey) {
|
|
297
|
+
const work = runInner(dispatchedCall, principal, allowRef);
|
|
298
|
+
inFlight.set(flightKey, work);
|
|
299
|
+
return { kind: "run", work, key: flightKey, replayRef: allowRef };
|
|
300
|
+
}
|
|
301
|
+
async function retryPolicyDeny(dispatchedCall, principal, requestHash, inputs, timestampMs, subject, scopedRef) {
|
|
302
|
+
let spendCtx;
|
|
303
|
+
if (dispatchedCall.name === "spend") {
|
|
304
|
+
const approvalRows = await approvals.listAll();
|
|
305
|
+
spendCtx = {
|
|
306
|
+
spentTodayMinor: (currency) => spentTodayMinorOf(approvalRows, timestampMs, currency, deps.policy.approvalTtlMs),
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
const verdict = deps.policy.evaluate(dispatchedCall, principal, spendCtx);
|
|
310
|
+
if (verdict.decision !== "deny")
|
|
311
|
+
return null;
|
|
312
|
+
const denyRef = deps.nonce();
|
|
313
|
+
await writeRecord({
|
|
314
|
+
decision: "deny",
|
|
315
|
+
reasonCode: verdict.reasonCode,
|
|
316
|
+
ref: denyRef,
|
|
317
|
+
requestHash,
|
|
318
|
+
inputs: {
|
|
319
|
+
...inputs,
|
|
320
|
+
approver: { id: "verax-proxy", via: "proxy", resolves: scopedRef },
|
|
321
|
+
},
|
|
322
|
+
effectHash: null,
|
|
323
|
+
subject,
|
|
324
|
+
timestampMs,
|
|
325
|
+
});
|
|
326
|
+
return denied(verdict.reasonCode, denyRef);
|
|
327
|
+
}
|
|
312
328
|
return {
|
|
313
329
|
approvals,
|
|
314
330
|
inputsLog,
|
|
@@ -330,6 +346,7 @@ export function createProxy(deps) {
|
|
|
330
346
|
policyHash: defer.policyHash,
|
|
331
347
|
approvals,
|
|
332
348
|
inputsLog,
|
|
349
|
+
budgetGuard,
|
|
333
350
|
});
|
|
334
351
|
});
|
|
335
352
|
}
|
|
@@ -366,168 +383,220 @@ export function createProxy(deps) {
|
|
|
366
383
|
return denied("ledger-disk-low", ref);
|
|
367
384
|
}
|
|
368
385
|
const given = readRef(call.arguments);
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if (existing
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
decision: "deny",
|
|
393
|
-
reasonCode: "ref-reuse",
|
|
394
|
-
ref,
|
|
395
|
-
requestHash,
|
|
396
|
-
inputs: resolved.inputs,
|
|
397
|
-
effectHash: null,
|
|
398
|
-
subject: call.name,
|
|
399
|
-
timestampMs,
|
|
400
|
-
});
|
|
401
|
-
return denied("ref-reuse", ref);
|
|
402
|
-
}
|
|
403
|
-
if (existing.decision === "defer") {
|
|
404
|
-
const snap = await approvals.get(scopedRef);
|
|
405
|
-
const boundHit = lookupResolvedBy(deps.ledger, scopedRef);
|
|
406
|
-
if (boundHit?.kind === "expired") {
|
|
407
|
-
if (snap?.status === "pending")
|
|
408
|
-
await approvals.updateStatus(scopedRef, "expired");
|
|
409
|
-
return denied("expired", boundHit.ref);
|
|
410
|
-
}
|
|
411
|
-
if (snap && timestampMs > snap.expiresAtMs) {
|
|
412
|
-
const expireRef = deps.nonce();
|
|
386
|
+
const plan = await admission.enqueue(async () => {
|
|
387
|
+
if (given === "invalid") {
|
|
388
|
+
const ref = deps.nonce();
|
|
389
|
+
await writeRecord({
|
|
390
|
+
decision: "deny",
|
|
391
|
+
reasonCode: "ref-invalid",
|
|
392
|
+
ref,
|
|
393
|
+
requestHash,
|
|
394
|
+
inputs: resolved.inputs,
|
|
395
|
+
effectHash: null,
|
|
396
|
+
subject: call.name,
|
|
397
|
+
timestampMs,
|
|
398
|
+
});
|
|
399
|
+
return { kind: "done", result: denied("ref-invalid", ref) };
|
|
400
|
+
}
|
|
401
|
+
if (typeof given === "string") {
|
|
402
|
+
// The ledger, the approvals snapshot and the resolution index all key on the
|
|
403
|
+
// scoped ref. The brain only ever sees and resends the raw `_ref` it chose.
|
|
404
|
+
const scopedRef = scopedClaimsRef(principal, given);
|
|
405
|
+
const existing = await lookupDecisionByRef(deps.ledger, given, tenantKey(principal));
|
|
406
|
+
if (existing) {
|
|
407
|
+
if (existing.requestHash !== requestHash) {
|
|
408
|
+
const ref = deps.nonce();
|
|
413
409
|
await writeRecord({
|
|
414
410
|
decision: "deny",
|
|
415
|
-
reasonCode: "
|
|
416
|
-
ref
|
|
411
|
+
reasonCode: "ref-reuse",
|
|
412
|
+
ref,
|
|
417
413
|
requestHash,
|
|
418
|
-
inputs:
|
|
419
|
-
...resolved.inputs,
|
|
420
|
-
approver: { id: "verax-proxy", via: "proxy", resolves: scopedRef },
|
|
421
|
-
},
|
|
414
|
+
inputs: resolved.inputs,
|
|
422
415
|
effectHash: null,
|
|
423
416
|
subject: call.name,
|
|
424
417
|
timestampMs,
|
|
425
418
|
});
|
|
426
|
-
|
|
427
|
-
return denied("expired", expireRef);
|
|
419
|
+
return { kind: "done", result: denied("ref-reuse", ref) };
|
|
428
420
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
421
|
+
if (existing.decision === "defer") {
|
|
422
|
+
const snap = await approvals.get(scopedRef);
|
|
423
|
+
const boundHit = lookupResolvedBy(deps.ledger, scopedRef);
|
|
424
|
+
if (boundHit?.kind === "expired") {
|
|
425
|
+
if (snap?.status === "pending")
|
|
426
|
+
await approvals.updateStatus(scopedRef, "expired");
|
|
427
|
+
return { kind: "done", result: denied("expired", boundHit.ref) };
|
|
435
428
|
}
|
|
436
|
-
if (
|
|
437
|
-
|
|
429
|
+
if (snap && timestampMs > snap.expiresAtMs) {
|
|
430
|
+
const expireRef = deps.nonce();
|
|
431
|
+
await writeRecord({
|
|
432
|
+
decision: "deny",
|
|
433
|
+
reasonCode: "expired",
|
|
434
|
+
ref: expireRef,
|
|
435
|
+
requestHash,
|
|
436
|
+
inputs: {
|
|
437
|
+
...resolved.inputs,
|
|
438
|
+
approver: { id: "verax-proxy", via: "proxy", resolves: scopedRef },
|
|
439
|
+
},
|
|
440
|
+
effectHash: null,
|
|
441
|
+
subject: call.name,
|
|
442
|
+
timestampMs,
|
|
443
|
+
});
|
|
444
|
+
await approvals.updateStatus(scopedRef, "expired");
|
|
445
|
+
return { kind: "done", result: denied("expired", expireRef) };
|
|
438
446
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
if (allow.
|
|
442
|
-
|
|
447
|
+
const allowRef = snap?.allowRef ?? (boundHit?.kind === "allow" ? boundHit.ref : undefined);
|
|
448
|
+
const allow = allowRef ? await lookupDecisionByRef(deps.ledger, allowRef) : null;
|
|
449
|
+
if (allow?.ref && allow.decision === "allow" && allow.reasonCode === "approved-by-operator") {
|
|
450
|
+
const bound = await inputsLog.get(allow.ref);
|
|
451
|
+
if (bound?.approver?.resolves && bound.approver.resolves !== scopedRef) {
|
|
452
|
+
return { kind: "done", result: deferred(given) };
|
|
453
|
+
}
|
|
454
|
+
if (!snap?.allowRef) {
|
|
455
|
+
await approvals.updateStatus(scopedRef, "approved", { allowRef: allow.ref });
|
|
456
|
+
}
|
|
457
|
+
if (await hasPrimaryEffect(deps.ledger, allow.ref)) {
|
|
458
|
+
return { kind: "done", result: allowedReplay(allow.ref) };
|
|
459
|
+
}
|
|
460
|
+
if (allow.subject === "spend") {
|
|
461
|
+
return {
|
|
462
|
+
kind: "done",
|
|
463
|
+
result: await spendReauth(requestHash, resolved.inputs, timestampMs, call.name),
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
const policyDeny = await retryPolicyDeny(dispatched, principal, requestHash, resolved.inputs, timestampMs, call.name, scopedRef);
|
|
467
|
+
if (policyDeny)
|
|
468
|
+
return { kind: "done", result: policyDeny };
|
|
469
|
+
const flying = inFlight.get(scopedRef);
|
|
470
|
+
if (flying)
|
|
471
|
+
return { kind: "wait", work: flying, replayRef: allow.ref };
|
|
472
|
+
return launchInner(dispatched, principal, allow.ref, scopedRef);
|
|
443
473
|
}
|
|
444
|
-
return
|
|
474
|
+
return { kind: "done", result: deferred(given) };
|
|
445
475
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
if (existing.decision === "deny") {
|
|
449
|
-
return denied(existing.reasonCode, given, call);
|
|
450
|
-
}
|
|
451
|
-
if (existing.decision === "allow" && existing.ref) {
|
|
452
|
-
if (await hasPrimaryEffect(deps.ledger, existing.ref)) {
|
|
453
|
-
return allowedReplay(existing.ref);
|
|
476
|
+
if (existing.decision === "deny") {
|
|
477
|
+
return { kind: "done", result: denied(existing.reasonCode, given, call) };
|
|
454
478
|
}
|
|
455
|
-
if (existing.
|
|
456
|
-
|
|
479
|
+
if (existing.decision === "allow" && existing.ref) {
|
|
480
|
+
if (await hasPrimaryEffect(deps.ledger, existing.ref)) {
|
|
481
|
+
return { kind: "done", result: allowedReplay(existing.ref) };
|
|
482
|
+
}
|
|
483
|
+
if (existing.subject === "spend") {
|
|
484
|
+
return {
|
|
485
|
+
kind: "done",
|
|
486
|
+
result: await spendReauth(requestHash, resolved.inputs, timestampMs, call.name),
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
const policyDeny = await retryPolicyDeny(dispatched, principal, requestHash, resolved.inputs, timestampMs, call.name, scopedRef);
|
|
490
|
+
if (policyDeny)
|
|
491
|
+
return { kind: "done", result: policyDeny };
|
|
492
|
+
const flying = inFlight.get(scopedRef);
|
|
493
|
+
if (flying)
|
|
494
|
+
return { kind: "wait", work: flying, replayRef: existing.ref };
|
|
495
|
+
const denyRef = deps.nonce();
|
|
496
|
+
await writeRecord({
|
|
497
|
+
decision: "deny",
|
|
498
|
+
reasonCode: "outcome-unknown",
|
|
499
|
+
ref: denyRef,
|
|
500
|
+
requestHash,
|
|
501
|
+
inputs: {
|
|
502
|
+
...resolved.inputs,
|
|
503
|
+
approver: { id: "verax-proxy", via: "proxy", resolves: scopedRef },
|
|
504
|
+
},
|
|
505
|
+
effectHash: null,
|
|
506
|
+
subject: call.name,
|
|
507
|
+
timestampMs,
|
|
508
|
+
});
|
|
509
|
+
return { kind: "done", result: denied("outcome-unknown", denyRef) };
|
|
457
510
|
}
|
|
458
|
-
return runInner(dispatched, principal, existing.ref);
|
|
459
511
|
}
|
|
460
512
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
reasonCode,
|
|
491
|
-
ref,
|
|
492
|
-
requestHash,
|
|
493
|
-
inputs: resolved.inputs,
|
|
494
|
-
effectHash: allow ? sha256Canonical(effectDescriptor(dispatched.name, dispatched.arguments)) : null,
|
|
495
|
-
subject: call.name,
|
|
496
|
-
timestampMs,
|
|
497
|
-
});
|
|
498
|
-
if (typeof given === "string") {
|
|
499
|
-
noteTenantRef(deps.ledger, tenantKey(principal), given);
|
|
500
|
-
}
|
|
501
|
-
if (decision === "defer") {
|
|
502
|
-
const rule = deps.policy.rule(verdict.rule);
|
|
503
|
-
const amount = dispatched.name === "spend" ? dispatched.arguments.amountMinor : dispatched.arguments.amount;
|
|
504
|
-
const payee = dispatched.arguments.payee;
|
|
505
|
-
const currency = dispatched.arguments.currency;
|
|
506
|
-
await approvals.append({
|
|
513
|
+
let spendCtx;
|
|
514
|
+
if (dispatched.name === "spend") {
|
|
515
|
+
const approvalRows = await approvals.listAll();
|
|
516
|
+
spendCtx = {
|
|
517
|
+
spentTodayMinor: (currency) => spentTodayMinorOf(approvalRows, timestampMs, currency, deps.policy.approvalTtlMs),
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
const verdict = deps.policy.evaluate(dispatched, principal, spendCtx);
|
|
521
|
+
let reasonCode = resolved.reasonCode ?? verdict.reasonCode;
|
|
522
|
+
let decision = resolved.reasonCode ? "deny" : verdict.decision;
|
|
523
|
+
const bound = rateBound(timestampMs, decision === "allow" || decision === "defer");
|
|
524
|
+
if (bound) {
|
|
525
|
+
decision = "deny";
|
|
526
|
+
reasonCode = bound;
|
|
527
|
+
}
|
|
528
|
+
if (!bound &&
|
|
529
|
+
decision === "allow" &&
|
|
530
|
+
deps.checkTenantMismatch &&
|
|
531
|
+
(await deps.checkTenantMismatch(dispatched, principal))) {
|
|
532
|
+
decision = "deny";
|
|
533
|
+
reasonCode = "tenant-mismatch";
|
|
534
|
+
}
|
|
535
|
+
const ref = typeof given === "string" ? scopedClaimsRef(principal, given) : deps.nonce();
|
|
536
|
+
// What the brain is told: its own `_ref`, so a retry carries the same key back.
|
|
537
|
+
const shown = typeof given === "string" ? given : ref;
|
|
538
|
+
const allow = decision === "allow";
|
|
539
|
+
await writeRecord({
|
|
540
|
+
decision,
|
|
541
|
+
reasonCode,
|
|
507
542
|
ref,
|
|
508
543
|
requestHash,
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
inputsSummary: {
|
|
514
|
-
count: resolved.inputs.inputs.length,
|
|
515
|
-
ids: resolved.inputs.inputs.map((i) => i.id),
|
|
516
|
-
},
|
|
517
|
-
...(amount !== undefined ? { amount } : {}),
|
|
518
|
-
...(payee !== undefined ? { payee } : {}),
|
|
519
|
-
...(currency !== undefined ? { currency } : {}),
|
|
520
|
-
createdAtMs: timestampMs,
|
|
521
|
-
expiresAtMs: timestampMs + deps.policy.approvalTtlMs,
|
|
522
|
-
status: "pending",
|
|
523
|
-
brain: principal.brain,
|
|
544
|
+
inputs: resolved.inputs,
|
|
545
|
+
effectHash: allow ? sha256Canonical(effectDescriptor(dispatched.name, dispatched.arguments)) : null,
|
|
546
|
+
subject: call.name,
|
|
547
|
+
timestampMs,
|
|
524
548
|
});
|
|
525
|
-
|
|
549
|
+
if (typeof given === "string") {
|
|
550
|
+
noteTenantRef(deps.ledger, tenantKey(principal), given);
|
|
551
|
+
}
|
|
552
|
+
if (decision === "defer") {
|
|
553
|
+
const rule = deps.policy.rule(verdict.rule);
|
|
554
|
+
const amount = dispatched.name === "spend" ? dispatched.arguments.amountMinor : dispatched.arguments.amount;
|
|
555
|
+
const payee = dispatched.arguments.payee;
|
|
556
|
+
const currency = dispatched.arguments.currency;
|
|
557
|
+
await approvals.append({
|
|
558
|
+
ref,
|
|
559
|
+
requestHash,
|
|
560
|
+
subject: dispatched.name,
|
|
561
|
+
args: dispatched.arguments,
|
|
562
|
+
ruleId: verdict.rule,
|
|
563
|
+
ruleText: rule?.text ?? null,
|
|
564
|
+
inputsSummary: {
|
|
565
|
+
count: resolved.inputs.inputs.length,
|
|
566
|
+
ids: resolved.inputs.inputs.map((i) => i.id),
|
|
567
|
+
},
|
|
568
|
+
...(amount !== undefined ? { amount } : {}),
|
|
569
|
+
...(payee !== undefined ? { payee } : {}),
|
|
570
|
+
...(currency !== undefined ? { currency } : {}),
|
|
571
|
+
createdAtMs: timestampMs,
|
|
572
|
+
expiresAtMs: timestampMs + deps.policy.approvalTtlMs,
|
|
573
|
+
status: "pending",
|
|
574
|
+
brain: principal.brain,
|
|
575
|
+
});
|
|
576
|
+
return { kind: "done", result: deferred(shown) };
|
|
577
|
+
}
|
|
578
|
+
if (!allow) {
|
|
579
|
+
return { kind: "done", result: denied(reasonCode, shown, call) };
|
|
580
|
+
}
|
|
581
|
+
return launchInner(dispatched, principal, ref, ref);
|
|
582
|
+
});
|
|
583
|
+
if (plan.kind === "done")
|
|
584
|
+
return plan.result;
|
|
585
|
+
if (plan.kind === "wait") {
|
|
586
|
+
try {
|
|
587
|
+
await plan.work;
|
|
588
|
+
}
|
|
589
|
+
catch {
|
|
590
|
+
// The first call recorded the throw on its effect row.
|
|
591
|
+
}
|
|
592
|
+
return allowedReplay(plan.replayRef);
|
|
593
|
+
}
|
|
594
|
+
try {
|
|
595
|
+
return await plan.work;
|
|
526
596
|
}
|
|
527
|
-
|
|
528
|
-
|
|
597
|
+
finally {
|
|
598
|
+
inFlight.delete(plan.key);
|
|
529
599
|
}
|
|
530
|
-
return runInner(dispatched, principal, ref);
|
|
531
600
|
},
|
|
532
601
|
};
|
|
533
602
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** One async tail so a later turn cannot start until the earlier one settles. */
|
|
2
|
+
export class SerialQueue {
|
|
3
|
+
tail = Promise.resolve();
|
|
4
|
+
enqueue(fn) {
|
|
5
|
+
const run = this.tail.then(fn, fn);
|
|
6
|
+
this.tail = run.then(() => undefined, () => undefined);
|
|
7
|
+
return run;
|
|
8
|
+
}
|
|
9
|
+
}
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@verax-ai/proxy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -25,6 +25,16 @@
|
|
|
25
25
|
"@cedulon/x402-adapter": "0.13.1"
|
|
26
26
|
},
|
|
27
27
|
"description": "The decision proxy behind VERAX: policy, signed decision records, and the ledger they live in.",
|
|
28
|
+
"homepage": "https://verax-ai.com",
|
|
29
|
+
"keywords": [
|
|
30
|
+
"verax",
|
|
31
|
+
"mcp",
|
|
32
|
+
"agent",
|
|
33
|
+
"policy",
|
|
34
|
+
"audit",
|
|
35
|
+
"ledger",
|
|
36
|
+
"cedulon"
|
|
37
|
+
],
|
|
28
38
|
"scripts": {
|
|
29
39
|
"build": "tsc -p tsconfig.build.json",
|
|
30
40
|
"prepack": "npm run build"
|