@verax-ai/proxy 0.2.2 → 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/README.md +6 -1
- package/dist/approvals.d.ts +13 -4
- package/dist/approvals.js +140 -22
- package/dist/checkpoints.d.ts +9 -0
- package/dist/checkpoints.js +44 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/ledger-manifest.d.ts +9 -0
- package/dist/ledger-manifest.js +49 -6
- package/dist/ledger.d.ts +8 -0
- package/dist/ledger.js +45 -18
- package/dist/policy.d.ts +12 -1
- package/dist/policy.js +87 -16
- package/dist/proxy.js +177 -27
- package/dist/reconcile.d.ts +4 -1
- package/dist/reconcile.js +39 -2
- package/dist/types.d.ts +5 -2
- package/dist/verify-ledger.d.ts +39 -1
- package/dist/verify-ledger.js +731 -36
- package/package.json +3 -1
package/dist/reconcile.d.ts
CHANGED
|
@@ -67,7 +67,8 @@ export type CardCsvOpts = {
|
|
|
67
67
|
};
|
|
68
68
|
export declare function parseCardCsv(text: string, opts: CardCsvOpts): ParsedCardCsv;
|
|
69
69
|
export declare function parseChannelJsonl(text: string): ChannelRow[];
|
|
70
|
-
/**
|
|
70
|
+
/** Decision refs, read the same way `verifyLedger` lists decision files. */
|
|
71
|
+
export declare function loadDecisionRefsFromDir(stateDir: string): Set<string>;
|
|
71
72
|
export declare function loadEffectsFromDir(stateDir: string): LedgerEffect[];
|
|
72
73
|
export declare function reconcile(channelRows: readonly ChannelRow[], effects: readonly LedgerEffect[], opts?: {
|
|
73
74
|
toleranceMs?: number;
|
|
@@ -78,4 +79,6 @@ export declare function reconcile(channelRows: readonly ChannelRow[], effects: r
|
|
|
78
79
|
approvals?: readonly ApprovalRow[];
|
|
79
80
|
skipped?: CardCsvSkip[];
|
|
80
81
|
descriptorsByPayee?: Readonly<Record<string, readonly string[]>>;
|
|
82
|
+
/** Effect refs that have a decision. Absent means none: an effect is not a match. */
|
|
83
|
+
decisionRefs?: ReadonlySet<string>;
|
|
81
84
|
}): ReconcileReport;
|
package/dist/reconcile.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { readLedgerManifest, requireLedgerPiecePath } from "./ledger-manifest.js";
|
|
4
5
|
function asChannelRow(raw, line) {
|
|
5
6
|
if (raw === null || typeof raw !== "object") {
|
|
6
7
|
throw new Error(`channel-row-invalid:${line}`);
|
|
@@ -208,6 +209,39 @@ export function parseChannelJsonl(text) {
|
|
|
208
209
|
return rows;
|
|
209
210
|
}
|
|
210
211
|
/** Read-only: does not take ledger.lock. */
|
|
212
|
+
function decisionPaths(dir) {
|
|
213
|
+
const manifest = readLedgerManifest(dir);
|
|
214
|
+
if (manifest && Array.isArray(manifest.pieces) && manifest.pieces.length > 0) {
|
|
215
|
+
return manifest.pieces
|
|
216
|
+
.map((p) => requireLedgerPiecePath(dir, p.decisions))
|
|
217
|
+
.filter((p) => existsSync(p));
|
|
218
|
+
}
|
|
219
|
+
const tek = join(dir, "decisions.jsonl");
|
|
220
|
+
return existsSync(tek) ? [tek] : [];
|
|
221
|
+
}
|
|
222
|
+
/** Decision refs, read the same way `verifyLedger` lists decision files. */
|
|
223
|
+
export function loadDecisionRefsFromDir(stateDir) {
|
|
224
|
+
const refs = new Set();
|
|
225
|
+
for (const path of decisionPaths(stateDir)) {
|
|
226
|
+
let text = "";
|
|
227
|
+
try {
|
|
228
|
+
text = readFileSync(path, "utf8");
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
if (err.code === "ENOENT")
|
|
232
|
+
continue;
|
|
233
|
+
throw err;
|
|
234
|
+
}
|
|
235
|
+
for (const line of text.split("\n")) {
|
|
236
|
+
if (line === "")
|
|
237
|
+
continue;
|
|
238
|
+
const row = JSON.parse(line);
|
|
239
|
+
if (typeof row.claims?.ref === "string")
|
|
240
|
+
refs.add(row.claims.ref);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return refs;
|
|
244
|
+
}
|
|
211
245
|
export function loadEffectsFromDir(stateDir) {
|
|
212
246
|
try {
|
|
213
247
|
const text = readFileSync(join(stateDir, "effects.jsonl"), "utf8");
|
|
@@ -291,6 +325,8 @@ function descriptorsFor(effect, approvals, byPayee) {
|
|
|
291
325
|
}
|
|
292
326
|
export function reconcile(channelRows, effects, opts) {
|
|
293
327
|
const toleranceMs = opts?.toleranceMs ?? 60_000;
|
|
328
|
+
const decisionRefs = opts?.decisionRefs ?? new Set();
|
|
329
|
+
const decided = (effect) => decisionRefs.has(effect.row.ref);
|
|
294
330
|
if (channelRows.length === 0) {
|
|
295
331
|
return {
|
|
296
332
|
scope: { channel: "", windowStartMs: 0, windowEndMs: 0, rowCount: 0 },
|
|
@@ -326,6 +362,7 @@ export function reconcile(channelRows, effects, opts) {
|
|
|
326
362
|
const marked = { ...row, toleranceMs: rowTol };
|
|
327
363
|
if (typeof row.ref === "string" && row.ref !== "") {
|
|
328
364
|
const idx = effects.findIndex((e, i) => !used.has(i) &&
|
|
365
|
+
decided(e) &&
|
|
329
366
|
(e.row.ref === row.ref ||
|
|
330
367
|
opts?.approvals?.some((a) => a.ref === row.ref && a.allowRef === e.row.ref)));
|
|
331
368
|
if (idx === -1) {
|
|
@@ -359,7 +396,7 @@ export function reconcile(channelRows, effects, opts) {
|
|
|
359
396
|
let unknownNear = false;
|
|
360
397
|
let descriptorMiss = false;
|
|
361
398
|
const near = effects.findIndex((e, i) => {
|
|
362
|
-
if (used.has(i) || e.row.effectClass !== row.subject)
|
|
399
|
+
if (used.has(i) || !decided(e) || e.row.effectClass !== row.subject)
|
|
363
400
|
return false;
|
|
364
401
|
if (Math.abs(row.occurredAtMs - e.row.timestampMs) > rowTol)
|
|
365
402
|
return false;
|
package/dist/types.d.ts
CHANGED
|
@@ -45,11 +45,14 @@ export type Policy = {
|
|
|
45
45
|
/** Document-root posture. Absent keeps today's optional `_inputs`. */
|
|
46
46
|
requireInputs?: boolean;
|
|
47
47
|
evaluate(call: ToolCall, principal: Principal, ctx?: PolicyEvalCtx): PolicyDecision;
|
|
48
|
+
/** Spend-rule day bucket. Absent means UTC, the same as `dayOffsetMinutes: 0`. */
|
|
49
|
+
dayOffsetMinutes?: number;
|
|
48
50
|
rule(id: string | null): {
|
|
49
51
|
id: string;
|
|
50
52
|
text: string;
|
|
51
53
|
spend?: {
|
|
52
54
|
dailyMaxMinor?: number;
|
|
55
|
+
dayOffsetMinutes?: number;
|
|
53
56
|
};
|
|
54
57
|
} | null;
|
|
55
58
|
};
|
|
@@ -115,8 +118,8 @@ export type DecisionInputs = {
|
|
|
115
118
|
resolves: string;
|
|
116
119
|
};
|
|
117
120
|
};
|
|
118
|
-
/** How an operator's approval reached the ledger: `verax approve
|
|
119
|
-
export type ApprovalChannel = "cli" | "http";
|
|
121
|
+
/** How an operator's approval reached the ledger: a person at `verax approve`, a non-interactive `--from-script` run, or the body's `/api/approve`. */
|
|
122
|
+
export type ApprovalChannel = "cli" | "cli-script" | "http";
|
|
120
123
|
export type InputsLog = {
|
|
121
124
|
append(ref: string, inputs: DecisionInputs): Promise<void>;
|
|
122
125
|
get(ref: string): Promise<DecisionInputs | null>;
|
package/dist/verify-ledger.d.ts
CHANGED
|
@@ -16,10 +16,48 @@ export type VerifyResult = {
|
|
|
16
16
|
effectsBound: number;
|
|
17
17
|
effectsOrphaned: number;
|
|
18
18
|
trust: VerifyTrust;
|
|
19
|
+
/** Which key answered for `self` effect rows. Same honesty rules as `trust`. */
|
|
20
|
+
effectTrust: VerifyTrust;
|
|
21
|
+
/** Which key answered for `same-org` effect rows. Agreement is not trust. */
|
|
22
|
+
witnessTrust: VerifyTrust;
|
|
23
|
+
/** Which key answered for checkpoint rows. Same honesty rules as `trust`. */
|
|
24
|
+
checkpointTrust: VerifyTrust;
|
|
25
|
+
/** What `index.jsonl` still names. Missing file is not a failure. */
|
|
26
|
+
index: VerifyIndex;
|
|
27
|
+
/**
|
|
28
|
+
* Absent index: effect completeness was not checked, and that absence is
|
|
29
|
+
* not itself a failure. Present index: every ref it marks was compared to
|
|
30
|
+
* a bound effect row.
|
|
31
|
+
*/
|
|
32
|
+
effectCompleteness: string;
|
|
33
|
+
/** What the newest checkpoint covers, and the records after it. */
|
|
34
|
+
tail: VerifyTail;
|
|
19
35
|
problems: string[];
|
|
20
36
|
};
|
|
37
|
+
export type VerifyIndex = {
|
|
38
|
+
present: boolean;
|
|
39
|
+
/** Index refs that are not decisions in the ledger. */
|
|
40
|
+
missing: number;
|
|
41
|
+
line: string;
|
|
42
|
+
};
|
|
43
|
+
export type VerifyTail = {
|
|
44
|
+
line: string;
|
|
45
|
+
/** Newest signed checkpoint, or null when the file has none. */
|
|
46
|
+
checkpoint: {
|
|
47
|
+
receiptCount: number | null;
|
|
48
|
+
chainHeadHash: string | null;
|
|
49
|
+
/** True when some decision's hash is the checkpoint head. Null when the checkpoint carries no head. */
|
|
50
|
+
ledgerHoldsRecord: boolean | null;
|
|
51
|
+
} | null;
|
|
52
|
+
};
|
|
21
53
|
export type VerifyOptions = {
|
|
22
|
-
/** Verify against this key instead of the one
|
|
54
|
+
/** Verify decision records against this key instead of the one they carry. */
|
|
23
55
|
publicKeyPem?: string;
|
|
56
|
+
/** Verify `self` effect rows against this key instead of the one those rows carry. */
|
|
57
|
+
effectPublicKeyPem?: string;
|
|
58
|
+
/** Verify `same-org` effect rows against this key instead of the one those rows carry. */
|
|
59
|
+
witnessPublicKeyPem?: string;
|
|
60
|
+
/** Verify every checkpoint row against this key instead of one key taken from the file. */
|
|
61
|
+
checkpointPublicKeyPem?: string;
|
|
24
62
|
};
|
|
25
63
|
export declare function verifyLedger(dir: string, opts?: VerifyOptions): Promise<VerifyResult>;
|