@attest-ai/core 0.5.0 → 0.9.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/cjs/index.cjs +741 -25
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +145 -8
- package/dist/cjs/proto/index.cjs +81 -0
- package/dist/cjs/proto/index.cjs.map +1 -1
- package/dist/cjs/proto/index.d.cts +145 -3
- package/dist/esm/cli.js +321 -2
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/index.d.ts +145 -8
- package/dist/esm/index.js +724 -25
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/proto/index.d.ts +145 -3
- package/dist/esm/proto/index.js +77 -0
- package/dist/esm/proto/index.js.map +1 -1
- package/package.json +4 -4
- package/dist/adapters/anthropic.d.ts +0 -31
- package/dist/adapters/anthropic.d.ts.map +0 -1
- package/dist/adapters/anthropic.js +0 -42
- package/dist/adapters/anthropic.js.map +0 -1
- package/dist/adapters/gemini.d.ts +0 -30
- package/dist/adapters/gemini.d.ts.map +0 -1
- package/dist/adapters/gemini.js +0 -41
- package/dist/adapters/gemini.js.map +0 -1
- package/dist/adapters/index.d.ts +0 -11
- package/dist/adapters/index.d.ts.map +0 -1
- package/dist/adapters/index.js +0 -7
- package/dist/adapters/index.js.map +0 -1
- package/dist/adapters/manual.d.ts +0 -9
- package/dist/adapters/manual.d.ts.map +0 -1
- package/dist/adapters/manual.js +0 -16
- package/dist/adapters/manual.js.map +0 -1
- package/dist/adapters/ollama.d.ts +0 -19
- package/dist/adapters/ollama.d.ts.map +0 -1
- package/dist/adapters/ollama.js +0 -30
- package/dist/adapters/ollama.js.map +0 -1
- package/dist/adapters/openai.d.ts +0 -34
- package/dist/adapters/openai.d.ts.map +0 -1
- package/dist/adapters/openai.js +0 -42
- package/dist/adapters/openai.js.map +0 -1
- package/dist/adapters/otel.d.ts +0 -25
- package/dist/adapters/otel.d.ts.map +0 -1
- package/dist/adapters/otel.js +0 -133
- package/dist/adapters/otel.js.map +0 -1
- package/dist/agent.d.ts +0 -14
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js +0 -54
- package/dist/agent.js.map +0 -1
- package/dist/client.d.ts +0 -23
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -171
- package/dist/client.js.map +0 -1
- package/dist/config.d.ts +0 -21
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -31
- package/dist/config.js.map +0 -1
- package/dist/context.d.ts +0 -4
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -3
- package/dist/context.js.map +0 -1
- package/dist/delegate.d.ts +0 -3
- package/dist/delegate.d.ts.map +0 -1
- package/dist/delegate.js +0 -24
- package/dist/delegate.js.map +0 -1
- package/dist/engine-downloader.d.ts +0 -7
- package/dist/engine-downloader.d.ts.map +0 -1
- package/dist/engine-downloader.js +0 -136
- package/dist/engine-downloader.js.map +0 -1
- package/dist/engine-manager.d.ts +0 -26
- package/dist/engine-manager.d.ts.map +0 -1
- package/dist/engine-manager.js +0 -172
- package/dist/engine-manager.js.map +0 -1
- package/dist/expect.d.ts +0 -133
- package/dist/expect.d.ts.map +0 -1
- package/dist/expect.js +0 -330
- package/dist/expect.js.map +0 -1
- package/dist/index.d.ts +0 -16
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -15
- package/dist/index.js.map +0 -1
- package/dist/proto/codec.d.ts +0 -20
- package/dist/proto/codec.d.ts.map +0 -1
- package/dist/proto/codec.js +0 -71
- package/dist/proto/codec.js.map +0 -1
- package/dist/proto/constants.d.ts +0 -21
- package/dist/proto/constants.d.ts.map +0 -1
- package/dist/proto/constants.js +0 -25
- package/dist/proto/constants.js.map +0 -1
- package/dist/proto/errors.d.ts +0 -13
- package/dist/proto/errors.d.ts.map +0 -1
- package/dist/proto/errors.js +0 -24
- package/dist/proto/errors.js.map +0 -1
- package/dist/proto/index.d.ts +0 -5
- package/dist/proto/index.d.ts.map +0 -1
- package/dist/proto/index.js +0 -5
- package/dist/proto/index.js.map +0 -1
- package/dist/proto/types.d.ts +0 -104
- package/dist/proto/types.d.ts.map +0 -1
- package/dist/proto/types.js +0 -2
- package/dist/proto/types.js.map +0 -1
- package/dist/result.d.ts +0 -15
- package/dist/result.d.ts.map +0 -1
- package/dist/result.js +0 -32
- package/dist/result.js.map +0 -1
- package/dist/simulation.d.ts +0 -8
- package/dist/simulation.d.ts.map +0 -1
- package/dist/simulation.js +0 -21
- package/dist/simulation.js.map +0 -1
- package/dist/tier.d.ts +0 -5
- package/dist/tier.d.ts.map +0 -1
- package/dist/tier.js +0 -10
- package/dist/tier.js.map +0 -1
- package/dist/trace-tree.d.ts +0 -20
- package/dist/trace-tree.d.ts.map +0 -1
- package/dist/trace-tree.js +0 -92
- package/dist/trace-tree.js.map +0 -1
- package/dist/trace.d.ts +0 -41
- package/dist/trace.d.ts.map +0 -1
- package/dist/trace.js +0 -88
- package/dist/trace.js.map +0 -1
- package/dist/version.d.ts +0 -3
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js +0 -3
- package/dist/version.js.map +0 -1
package/dist/esm/cli.js
CHANGED
|
@@ -6,7 +6,220 @@ import * as path from "path";
|
|
|
6
6
|
import * as os from "os";
|
|
7
7
|
|
|
8
8
|
// src/version.ts
|
|
9
|
-
var VERSION = "0.
|
|
9
|
+
var VERSION = "0.9.0";
|
|
10
|
+
|
|
11
|
+
// src/calibration.ts
|
|
12
|
+
import { createHash } from "crypto";
|
|
13
|
+
function computeAgreement(pairs, threshold) {
|
|
14
|
+
if (pairs.length === 0) {
|
|
15
|
+
throw new Error("no labeled pairs available");
|
|
16
|
+
}
|
|
17
|
+
if (threshold <= 0 || threshold >= 1) {
|
|
18
|
+
throw new Error("threshold must be in (0, 1)");
|
|
19
|
+
}
|
|
20
|
+
const n = pairs.length;
|
|
21
|
+
let humanPos = 0;
|
|
22
|
+
let judgePos = 0;
|
|
23
|
+
let bothPos = 0;
|
|
24
|
+
let bothNeg = 0;
|
|
25
|
+
for (const p of pairs) {
|
|
26
|
+
const hPos = p.human >= threshold;
|
|
27
|
+
const jPos = p.judge >= threshold;
|
|
28
|
+
if (hPos) humanPos++;
|
|
29
|
+
if (jPos) judgePos++;
|
|
30
|
+
if (hPos && jPos) bothPos++;
|
|
31
|
+
if (!hPos && !jPos) bothNeg++;
|
|
32
|
+
}
|
|
33
|
+
const humanNeg = n - humanPos;
|
|
34
|
+
const judgeNeg = n - judgePos;
|
|
35
|
+
const agreement = (bothPos + bothNeg) / n;
|
|
36
|
+
const expected = (humanPos * judgePos + humanNeg * judgeNeg) / (n * n);
|
|
37
|
+
const cohenKappa = expected < 1 ? (agreement - expected) / (1 - expected) : 0;
|
|
38
|
+
const rocAuc = rocAUC(pairs, threshold);
|
|
39
|
+
return { threshold, n, agreement, cohenKappa, rocAuc };
|
|
40
|
+
}
|
|
41
|
+
function rocAUC(pairs, threshold) {
|
|
42
|
+
const rows = pairs.map((p) => ({
|
|
43
|
+
score: p.judge,
|
|
44
|
+
pos: p.human >= threshold
|
|
45
|
+
}));
|
|
46
|
+
const pos = rows.filter((r) => r.pos).length;
|
|
47
|
+
const neg = rows.length - pos;
|
|
48
|
+
if (pos === 0 || neg === 0) return 0;
|
|
49
|
+
rows.sort((a, b) => a.score - b.score);
|
|
50
|
+
let rankSumPos = 0;
|
|
51
|
+
let i = 0;
|
|
52
|
+
while (i < rows.length) {
|
|
53
|
+
let j = i;
|
|
54
|
+
while (j < rows.length && rows[j].score === rows[i].score) {
|
|
55
|
+
j++;
|
|
56
|
+
}
|
|
57
|
+
const avgRank = (i + j + 1) / 2;
|
|
58
|
+
for (let k = i; k < j; k++) {
|
|
59
|
+
if (rows[k].pos) rankSumPos += avgRank;
|
|
60
|
+
}
|
|
61
|
+
i = j;
|
|
62
|
+
}
|
|
63
|
+
const auc = (rankSumPos - pos * (pos + 1) / 2) / (pos * neg);
|
|
64
|
+
if (Number.isNaN(auc) || !Number.isFinite(auc)) return 0;
|
|
65
|
+
return auc;
|
|
66
|
+
}
|
|
67
|
+
function loadLabelsCSV(text) {
|
|
68
|
+
const rows = parseCSV(text);
|
|
69
|
+
if (rows.length === 0) {
|
|
70
|
+
throw new Error("empty CSV");
|
|
71
|
+
}
|
|
72
|
+
let start = 0;
|
|
73
|
+
if (rows[0].length >= 2) {
|
|
74
|
+
const firstHuman = parseFloatStrict(rows[0][1]);
|
|
75
|
+
if (firstHuman === null) start = 1;
|
|
76
|
+
}
|
|
77
|
+
const out = [];
|
|
78
|
+
for (let idx = start; idx < rows.length; idx++) {
|
|
79
|
+
const row = rows[idx];
|
|
80
|
+
const lineNum = idx + 1;
|
|
81
|
+
if (row.length === 0) continue;
|
|
82
|
+
if (row[0].trimStart().startsWith("#")) continue;
|
|
83
|
+
if (row.length < 2) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`CSV line ${lineNum}: want at least 2 columns (input, human_label)`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
const human = parseFloatStrict(row[1]);
|
|
89
|
+
if (human === null) {
|
|
90
|
+
throw new Error(`CSV line ${lineNum}: human_label not a float: ${row[1]}`);
|
|
91
|
+
}
|
|
92
|
+
const rec = {
|
|
93
|
+
input: row[0],
|
|
94
|
+
humanLabel: human,
|
|
95
|
+
judgeScore: 0,
|
|
96
|
+
judgeKnown: false
|
|
97
|
+
};
|
|
98
|
+
if (row.length >= 3 && row[2].trim() !== "") {
|
|
99
|
+
const judge = parseFloatStrict(row[2]);
|
|
100
|
+
if (judge === null) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`CSV line ${lineNum}: judge_score not a float: ${row[2]}`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
rec.judgeScore = judge;
|
|
106
|
+
rec.judgeKnown = true;
|
|
107
|
+
}
|
|
108
|
+
out.push(rec);
|
|
109
|
+
}
|
|
110
|
+
if (out.length === 0) {
|
|
111
|
+
throw new Error("CSV contained no labeled rows");
|
|
112
|
+
}
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
115
|
+
function loadLabelsJSONL(text) {
|
|
116
|
+
const out = [];
|
|
117
|
+
const lines = text.split(/\r?\n/);
|
|
118
|
+
for (let i = 0; i < lines.length; i++) {
|
|
119
|
+
const stripped = lines[i].trim();
|
|
120
|
+
if (!stripped) continue;
|
|
121
|
+
let obj;
|
|
122
|
+
try {
|
|
123
|
+
obj = JSON.parse(stripped);
|
|
124
|
+
} catch (err) {
|
|
125
|
+
throw new Error(`JSONL line ${i + 1}: ${err.message}`);
|
|
126
|
+
}
|
|
127
|
+
if (Object.keys(obj).length === 0) continue;
|
|
128
|
+
if (obj["human_label"] === void 0) {
|
|
129
|
+
throw new Error(`JSONL line ${i + 1}: missing human_label`);
|
|
130
|
+
}
|
|
131
|
+
const human = parseJSONNumber(obj["human_label"]);
|
|
132
|
+
if (human === null) {
|
|
133
|
+
throw new Error(`JSONL line ${i + 1}: human_label is not a number`);
|
|
134
|
+
}
|
|
135
|
+
const rec = {
|
|
136
|
+
input: typeof obj["input"] === "string" ? obj["input"] : "",
|
|
137
|
+
humanLabel: human,
|
|
138
|
+
judgeScore: 0,
|
|
139
|
+
judgeKnown: false
|
|
140
|
+
};
|
|
141
|
+
if (obj["judge_score"] !== void 0 && obj["judge_score"] !== null) {
|
|
142
|
+
const judge = parseJSONNumber(obj["judge_score"]);
|
|
143
|
+
if (judge === null) {
|
|
144
|
+
throw new Error(`JSONL line ${i + 1}: judge_score is not a number`);
|
|
145
|
+
}
|
|
146
|
+
rec.judgeScore = judge;
|
|
147
|
+
rec.judgeKnown = true;
|
|
148
|
+
}
|
|
149
|
+
out.push(rec);
|
|
150
|
+
}
|
|
151
|
+
if (out.length === 0) {
|
|
152
|
+
throw new Error("JSONL contained no labeled rows");
|
|
153
|
+
}
|
|
154
|
+
return out;
|
|
155
|
+
}
|
|
156
|
+
function parseFloatStrict(raw) {
|
|
157
|
+
const trimmed = raw.trim();
|
|
158
|
+
if (trimmed === "") return null;
|
|
159
|
+
const n = Number(trimmed);
|
|
160
|
+
if (Number.isNaN(n)) return null;
|
|
161
|
+
return n;
|
|
162
|
+
}
|
|
163
|
+
function parseJSONNumber(value) {
|
|
164
|
+
if (typeof value !== "number") return null;
|
|
165
|
+
if (Number.isNaN(value)) return null;
|
|
166
|
+
return value;
|
|
167
|
+
}
|
|
168
|
+
function parseCSV(text) {
|
|
169
|
+
const rows = [];
|
|
170
|
+
let current = [];
|
|
171
|
+
let field = "";
|
|
172
|
+
let inQuotes = false;
|
|
173
|
+
let i = 0;
|
|
174
|
+
while (i < text.length) {
|
|
175
|
+
const ch = text[i];
|
|
176
|
+
if (inQuotes) {
|
|
177
|
+
if (ch === '"') {
|
|
178
|
+
if (text[i + 1] === '"') {
|
|
179
|
+
field += '"';
|
|
180
|
+
i += 2;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
inQuotes = false;
|
|
184
|
+
i++;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
field += ch;
|
|
188
|
+
i++;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (ch === '"') {
|
|
192
|
+
inQuotes = true;
|
|
193
|
+
i++;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (ch === ",") {
|
|
197
|
+
current.push(field);
|
|
198
|
+
field = "";
|
|
199
|
+
i++;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (ch === "\n") {
|
|
203
|
+
current.push(field);
|
|
204
|
+
rows.push(current);
|
|
205
|
+
current = [];
|
|
206
|
+
field = "";
|
|
207
|
+
i++;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (ch === "\r") {
|
|
211
|
+
i++;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
field += ch;
|
|
215
|
+
i++;
|
|
216
|
+
}
|
|
217
|
+
if (field.length > 0 || current.length > 0) {
|
|
218
|
+
current.push(field);
|
|
219
|
+
rows.push(current);
|
|
220
|
+
}
|
|
221
|
+
return rows;
|
|
222
|
+
}
|
|
10
223
|
|
|
11
224
|
// src/cli.ts
|
|
12
225
|
function cacheDir() {
|
|
@@ -139,11 +352,117 @@ function main(argv = process.argv.slice(2)) {
|
|
|
139
352
|
cmdValidate(process.cwd());
|
|
140
353
|
process.exit(0);
|
|
141
354
|
}
|
|
355
|
+
if (args[0] === "calibrate") {
|
|
356
|
+
cmdCalibrate(args.slice(1));
|
|
357
|
+
process.exit(0);
|
|
358
|
+
}
|
|
142
359
|
process.stderr.write(
|
|
143
|
-
"Usage: attest <command>\n\nCommands:\n --version Print version\n init Scaffold a test project\n validate Validate test suite\n cache stats Show cache statistics\n cache clear Clear cache database\n"
|
|
360
|
+
"Usage: attest <command>\n\nCommands:\n --version Print version\n init Scaffold a test project\n validate Validate test suite\n cache stats Show cache statistics\n cache clear Clear cache database\n calibrate Compute judge agreement metrics over labeled data\n"
|
|
144
361
|
);
|
|
145
362
|
process.exit(args.length === 0 ? 0 : 1);
|
|
146
363
|
}
|
|
364
|
+
function parseCalibrateArgs(args) {
|
|
365
|
+
const opts = {
|
|
366
|
+
labels: "",
|
|
367
|
+
rubric: "default",
|
|
368
|
+
rubricVersion: "",
|
|
369
|
+
threshold: 0.5
|
|
370
|
+
};
|
|
371
|
+
for (let i = 0; i < args.length; i++) {
|
|
372
|
+
const arg = args[i];
|
|
373
|
+
const next = args[i + 1];
|
|
374
|
+
if (arg === "--labels") {
|
|
375
|
+
if (next === void 0) throw new Error("--labels requires a path");
|
|
376
|
+
opts.labels = next;
|
|
377
|
+
i++;
|
|
378
|
+
} else if (arg === "--rubric") {
|
|
379
|
+
if (next === void 0) throw new Error("--rubric requires a name");
|
|
380
|
+
opts.rubric = next;
|
|
381
|
+
i++;
|
|
382
|
+
} else if (arg === "--rubric-version") {
|
|
383
|
+
if (next === void 0) throw new Error("--rubric-version requires a value");
|
|
384
|
+
opts.rubricVersion = next;
|
|
385
|
+
i++;
|
|
386
|
+
} else if (arg === "--threshold") {
|
|
387
|
+
if (next === void 0) throw new Error("--threshold requires a number");
|
|
388
|
+
const parsed = Number(next);
|
|
389
|
+
if (next.trim() === "" || Number.isNaN(parsed)) {
|
|
390
|
+
throw new Error(`--threshold value ${JSON.stringify(next)} is not a number`);
|
|
391
|
+
}
|
|
392
|
+
opts.threshold = parsed;
|
|
393
|
+
i++;
|
|
394
|
+
} else {
|
|
395
|
+
throw new Error(`unknown calibrate flag: ${arg}`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (!opts.labels) {
|
|
399
|
+
throw new Error("--labels is required");
|
|
400
|
+
}
|
|
401
|
+
return opts;
|
|
402
|
+
}
|
|
403
|
+
function cmdCalibrate(args) {
|
|
404
|
+
let opts;
|
|
405
|
+
try {
|
|
406
|
+
opts = parseCalibrateArgs(args);
|
|
407
|
+
} catch (err) {
|
|
408
|
+
process.stderr.write(`calibrate: ${err.message}
|
|
409
|
+
`);
|
|
410
|
+
process.exit(2);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
const text = fs.readFileSync(opts.labels, "utf-8");
|
|
414
|
+
const ext = path.extname(opts.labels).toLowerCase();
|
|
415
|
+
let records;
|
|
416
|
+
try {
|
|
417
|
+
if (ext === ".jsonl" || ext === ".ndjson") {
|
|
418
|
+
records = loadLabelsJSONL(text);
|
|
419
|
+
} else if (ext === ".csv") {
|
|
420
|
+
records = loadLabelsCSV(text);
|
|
421
|
+
} else if (text.trimStart().startsWith("{")) {
|
|
422
|
+
records = loadLabelsJSONL(text);
|
|
423
|
+
} else {
|
|
424
|
+
records = loadLabelsCSV(text);
|
|
425
|
+
}
|
|
426
|
+
} catch (err) {
|
|
427
|
+
process.stderr.write(`calibrate: ${err.message}
|
|
428
|
+
`);
|
|
429
|
+
process.exit(1);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const pairs = records.filter((r) => r.judgeKnown).map((r) => ({ human: r.humanLabel, judge: r.judgeScore }));
|
|
433
|
+
const missing = records.filter((r) => !r.judgeKnown).length;
|
|
434
|
+
if (pairs.length === 0) {
|
|
435
|
+
process.stderr.write(
|
|
436
|
+
`calibrate: no rows had judge_score (${missing} rows missing); pre-score the labels file first
|
|
437
|
+
`
|
|
438
|
+
);
|
|
439
|
+
process.exit(1);
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
let result;
|
|
443
|
+
try {
|
|
444
|
+
result = computeAgreement(pairs, opts.threshold);
|
|
445
|
+
} catch (err) {
|
|
446
|
+
process.stderr.write(`calibrate: ${err.message}
|
|
447
|
+
`);
|
|
448
|
+
process.exit(1);
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const output = {
|
|
452
|
+
rubric_name: opts.rubric,
|
|
453
|
+
rubric_version: opts.rubricVersion,
|
|
454
|
+
threshold: result.threshold,
|
|
455
|
+
label_count: result.n,
|
|
456
|
+
agreement: round3(result.agreement),
|
|
457
|
+
cohen_kappa: round3(result.cohenKappa),
|
|
458
|
+
roc_auc: round3(result.rocAuc),
|
|
459
|
+
missing_judge: missing
|
|
460
|
+
};
|
|
461
|
+
process.stdout.write(JSON.stringify(output, null, 2) + "\n");
|
|
462
|
+
}
|
|
463
|
+
function round3(x) {
|
|
464
|
+
return Math.round(x * 1e3) / 1e3;
|
|
465
|
+
}
|
|
147
466
|
var isDirectExecution = typeof import.meta.url === "string" && import.meta.url.startsWith("file:") && process.argv[1] !== void 0 && import.meta.url.endsWith(path.basename(process.argv[1]));
|
|
148
467
|
if (isDirectExecution) {
|
|
149
468
|
main();
|
package/dist/esm/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli.ts","../../src/version.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * CLI entry point for Attest: npx attest or attest (after global install).\n *\n * Ported from Python sdks/python/src/attest/__main__.py\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { VERSION } from \"./version.js\";\n\nfunction cacheDir(): string {\n const envOverride = process.env[\"ATTEST_CACHE_DIR\"];\n if (envOverride) return envOverride;\n return path.join(os.homedir(), \".attest\", \"cache\");\n}\n\nfunction cacheDbPath(): string {\n return path.join(cacheDir(), \"attest.db\");\n}\n\nfunction cmdCacheStats(): void {\n const dbPath = cacheDbPath();\n const exists = fs.existsSync(dbPath);\n let fileSize = 0;\n if (exists) {\n fileSize = fs.statSync(dbPath).size;\n }\n const stats = { exists, file_size: fileSize, path: dbPath };\n process.stdout.write(JSON.stringify(stats) + \"\\n\");\n}\n\nfunction cmdCacheClear(): void {\n const dbPath = cacheDbPath();\n if (fs.existsSync(dbPath)) {\n fs.unlinkSync(dbPath);\n process.stdout.write(`Cleared cache: ${dbPath}\\n`);\n } else {\n process.stdout.write(`No cache to clear: ${dbPath}\\n`);\n }\n}\n\nconst CONFTEST_TEMPLATE = `import { describe, it, expect } from \"vitest\";\nimport { attestExpect, TraceBuilder } from \"@attest-ai/core\";\n\ndescribe(\"my agent\", () => {\n it(\"returns expected output\", async () => {\n const trace = new TraceBuilder(\"my-agent\")\n .setInput({ user_message: \"hello\" })\n .setOutput({ message: \"world\" })\n .build();\n\n attestExpect(trace).outputContains(\"world\");\n });\n});\n`;\n\nfunction cmdInit(targetDir: string): void {\n const testsDir = path.join(targetDir, \"tests\");\n fs.mkdirSync(testsDir, { recursive: true });\n process.stdout.write(`Directory: ${testsDir}\\n`);\n\n const sampleTestPath = path.join(testsDir, \"test_my_agent.test.ts\");\n if (fs.existsSync(sampleTestPath)) {\n process.stderr.write(`Skipped (exists): ${sampleTestPath}\\n`);\n } else {\n fs.writeFileSync(sampleTestPath, CONFTEST_TEMPLATE);\n process.stdout.write(`Created: ${sampleTestPath}\\n`);\n }\n}\n\nfunction cmdValidate(targetDir: string): void {\n const testsDir = path.join(targetDir, \"tests\");\n\n let testFiles: string[] = [];\n if (fs.existsSync(testsDir)) {\n testFiles = fs\n .readdirSync(testsDir)\n .filter((f) => f.endsWith(\".test.ts\") || f.endsWith(\".test.js\"));\n }\n\n if (testFiles.length === 0) {\n process.stderr.write(\n `Warning: no test files (*.test.ts / *.test.js) found in ${testsDir}.\\n`,\n );\n } else {\n process.stdout.write(`Found ${testFiles.length} test file(s) in ${testsDir}\\n`);\n }\n\n // Check for stale golden traces\n const STALE_DAYS = 30;\n const now = Date.now();\n const goldenFiles = findGoldenFiles(targetDir);\n for (const golden of goldenFiles) {\n const mtime = fs.statSync(golden).mtimeMs;\n const ageDays = Math.floor((now - mtime) / (1000 * 60 * 60 * 24));\n if (ageDays > STALE_DAYS) {\n process.stderr.write(\n `Warning: golden trace ${golden} is ${ageDays} days old (>${STALE_DAYS} days). Consider regenerating.\\n`,\n );\n }\n }\n}\n\nfunction findGoldenFiles(dir: string): string[] {\n const results: string[] = [];\n if (!fs.existsSync(dir)) return results;\n\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory() && entry.name !== \"node_modules\") {\n results.push(...findGoldenFiles(fullPath));\n } else if (entry.isFile() && entry.name.endsWith(\".golden\")) {\n results.push(fullPath);\n }\n }\n return results;\n}\n\nexport function main(argv: string[] = process.argv.slice(2)): void {\n const args = argv;\n\n if (args[0] === \"--version\") {\n process.stdout.write(`attest ${VERSION}\\n`);\n process.exit(0);\n }\n\n if (args[0] === \"cache\" && args.length >= 2) {\n if (args[1] === \"stats\") {\n cmdCacheStats();\n process.exit(0);\n } else if (args[1] === \"clear\") {\n cmdCacheClear();\n process.exit(0);\n } else {\n process.stderr.write(`Unknown cache subcommand: ${args[1]}\\n`);\n process.stderr.write(\"Available: cache stats, cache clear\\n\");\n process.exit(1);\n }\n }\n\n if (args[0] === \"init\") {\n cmdInit(process.cwd());\n process.exit(0);\n }\n\n if (args[0] === \"validate\") {\n cmdValidate(process.cwd());\n process.exit(0);\n }\n\n process.stderr.write(\n \"Usage: attest <command>\\n\\n\" +\n \"Commands:\\n\" +\n \" --version Print version\\n\" +\n \" init Scaffold a test project\\n\" +\n \" validate Validate test suite\\n\" +\n \" cache stats Show cache statistics\\n\" +\n \" cache clear Clear cache database\\n\",\n );\n process.exit(args.length === 0 ? 0 : 1);\n}\n\n// Run when executed directly\nconst isDirectExecution =\n typeof import.meta.url === \"string\" &&\n import.meta.url.startsWith(\"file:\") &&\n process.argv[1] !== undefined &&\n import.meta.url.endsWith(path.basename(process.argv[1]));\n\nif (isDirectExecution) {\n main();\n}\n","export const VERSION = \"0.5.0\";\nexport const ENGINE_VERSION = \"0.5.0\";\n"],"mappings":";;;AAOA,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,YAAY,QAAQ;;;ACTb,IAAM,UAAU;;;ADYvB,SAAS,WAAmB;AAC1B,QAAM,cAAc,QAAQ,IAAI,kBAAkB;AAClD,MAAI,YAAa,QAAO;AACxB,SAAY,UAAQ,WAAQ,GAAG,WAAW,OAAO;AACnD;AAEA,SAAS,cAAsB;AAC7B,SAAY,UAAK,SAAS,GAAG,WAAW;AAC1C;AAEA,SAAS,gBAAsB;AAC7B,QAAM,SAAS,YAAY;AAC3B,QAAM,SAAY,cAAW,MAAM;AACnC,MAAI,WAAW;AACf,MAAI,QAAQ;AACV,eAAc,YAAS,MAAM,EAAE;AAAA,EACjC;AACA,QAAM,QAAQ,EAAE,QAAQ,WAAW,UAAU,MAAM,OAAO;AAC1D,UAAQ,OAAO,MAAM,KAAK,UAAU,KAAK,IAAI,IAAI;AACnD;AAEA,SAAS,gBAAsB;AAC7B,QAAM,SAAS,YAAY;AAC3B,MAAO,cAAW,MAAM,GAAG;AACzB,IAAG,cAAW,MAAM;AACpB,YAAQ,OAAO,MAAM,kBAAkB,MAAM;AAAA,CAAI;AAAA,EACnD,OAAO;AACL,YAAQ,OAAO,MAAM,sBAAsB,MAAM;AAAA,CAAI;AAAA,EACvD;AACF;AAEA,IAAM,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAe1B,SAAS,QAAQ,WAAyB;AACxC,QAAM,WAAgB,UAAK,WAAW,OAAO;AAC7C,EAAG,aAAU,UAAU,EAAE,WAAW,KAAK,CAAC;AAC1C,UAAQ,OAAO,MAAM,cAAc,QAAQ;AAAA,CAAI;AAE/C,QAAM,iBAAsB,UAAK,UAAU,uBAAuB;AAClE,MAAO,cAAW,cAAc,GAAG;AACjC,YAAQ,OAAO,MAAM,qBAAqB,cAAc;AAAA,CAAI;AAAA,EAC9D,OAAO;AACL,IAAG,iBAAc,gBAAgB,iBAAiB;AAClD,YAAQ,OAAO,MAAM,YAAY,cAAc;AAAA,CAAI;AAAA,EACrD;AACF;AAEA,SAAS,YAAY,WAAyB;AAC5C,QAAM,WAAgB,UAAK,WAAW,OAAO;AAE7C,MAAI,YAAsB,CAAC;AAC3B,MAAO,cAAW,QAAQ,GAAG;AAC3B,gBACG,eAAY,QAAQ,EACpB,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,KAAK,EAAE,SAAS,UAAU,CAAC;AAAA,EACnE;AAEA,MAAI,UAAU,WAAW,GAAG;AAC1B,YAAQ,OAAO;AAAA,MACb,2DAA2D,QAAQ;AAAA;AAAA,IACrE;AAAA,EACF,OAAO;AACL,YAAQ,OAAO,MAAM,SAAS,UAAU,MAAM,oBAAoB,QAAQ;AAAA,CAAI;AAAA,EAChF;AAGA,QAAM,aAAa;AACnB,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,cAAc,gBAAgB,SAAS;AAC7C,aAAW,UAAU,aAAa;AAChC,UAAM,QAAW,YAAS,MAAM,EAAE;AAClC,UAAM,UAAU,KAAK,OAAO,MAAM,UAAU,MAAO,KAAK,KAAK,GAAG;AAChE,QAAI,UAAU,YAAY;AACxB,cAAQ,OAAO;AAAA,QACb,yBAAyB,MAAM,OAAO,OAAO,eAAe,UAAU;AAAA;AAAA,MACxE;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,KAAuB;AAC9C,QAAM,UAAoB,CAAC;AAC3B,MAAI,CAAI,cAAW,GAAG,EAAG,QAAO;AAEhC,QAAM,UAAa,eAAY,KAAK,EAAE,eAAe,KAAK,CAAC;AAC3D,aAAW,SAAS,SAAS;AAC3B,UAAM,WAAgB,UAAK,KAAK,MAAM,IAAI;AAC1C,QAAI,MAAM,YAAY,KAAK,MAAM,SAAS,gBAAgB;AACxD,cAAQ,KAAK,GAAG,gBAAgB,QAAQ,CAAC;AAAA,IAC3C,WAAW,MAAM,OAAO,KAAK,MAAM,KAAK,SAAS,SAAS,GAAG;AAC3D,cAAQ,KAAK,QAAQ;AAAA,IACvB;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,KAAK,OAAiB,QAAQ,KAAK,MAAM,CAAC,GAAS;AACjE,QAAM,OAAO;AAEb,MAAI,KAAK,CAAC,MAAM,aAAa;AAC3B,YAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,CAAI;AAC1C,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,KAAK,CAAC,MAAM,WAAW,KAAK,UAAU,GAAG;AAC3C,QAAI,KAAK,CAAC,MAAM,SAAS;AACvB,oBAAc;AACd,cAAQ,KAAK,CAAC;AAAA,IAChB,WAAW,KAAK,CAAC,MAAM,SAAS;AAC9B,oBAAc;AACd,cAAQ,KAAK,CAAC;AAAA,IAChB,OAAO;AACL,cAAQ,OAAO,MAAM,6BAA6B,KAAK,CAAC,CAAC;AAAA,CAAI;AAC7D,cAAQ,OAAO,MAAM,uCAAuC;AAC5D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,MAAI,KAAK,CAAC,MAAM,QAAQ;AACtB,YAAQ,QAAQ,IAAI,CAAC;AACrB,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,KAAK,CAAC,MAAM,YAAY;AAC1B,gBAAY,QAAQ,IAAI,CAAC;AACzB,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,OAAO;AAAA,IACb;AAAA,EAOF;AACA,UAAQ,KAAK,KAAK,WAAW,IAAI,IAAI,CAAC;AACxC;AAGA,IAAM,oBACJ,OAAO,YAAY,QAAQ,YAC3B,YAAY,IAAI,WAAW,OAAO,KAClC,QAAQ,KAAK,CAAC,MAAM,UACpB,YAAY,IAAI,SAAc,cAAS,QAAQ,KAAK,CAAC,CAAC,CAAC;AAEzD,IAAI,mBAAmB;AACrB,OAAK;AACP;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli.ts","../../src/version.ts","../../src/calibration.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * CLI entry point for Attest: npx attest or attest (after global install).\n *\n * Ported from Python sdks/python/src/attest/__main__.py\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { VERSION } from \"./version.js\";\nimport {\n computeAgreement,\n loadLabelsCSV,\n loadLabelsJSONL,\n type LabelPair,\n} from \"./calibration.js\";\n\nfunction cacheDir(): string {\n const envOverride = process.env[\"ATTEST_CACHE_DIR\"];\n if (envOverride) return envOverride;\n return path.join(os.homedir(), \".attest\", \"cache\");\n}\n\nfunction cacheDbPath(): string {\n return path.join(cacheDir(), \"attest.db\");\n}\n\nfunction cmdCacheStats(): void {\n const dbPath = cacheDbPath();\n const exists = fs.existsSync(dbPath);\n let fileSize = 0;\n if (exists) {\n fileSize = fs.statSync(dbPath).size;\n }\n const stats = { exists, file_size: fileSize, path: dbPath };\n process.stdout.write(JSON.stringify(stats) + \"\\n\");\n}\n\nfunction cmdCacheClear(): void {\n const dbPath = cacheDbPath();\n if (fs.existsSync(dbPath)) {\n fs.unlinkSync(dbPath);\n process.stdout.write(`Cleared cache: ${dbPath}\\n`);\n } else {\n process.stdout.write(`No cache to clear: ${dbPath}\\n`);\n }\n}\n\nconst CONFTEST_TEMPLATE = `import { describe, it, expect } from \"vitest\";\nimport { attestExpect, TraceBuilder } from \"@attest-ai/core\";\n\ndescribe(\"my agent\", () => {\n it(\"returns expected output\", async () => {\n const trace = new TraceBuilder(\"my-agent\")\n .setInput({ user_message: \"hello\" })\n .setOutput({ message: \"world\" })\n .build();\n\n attestExpect(trace).outputContains(\"world\");\n });\n});\n`;\n\nfunction cmdInit(targetDir: string): void {\n const testsDir = path.join(targetDir, \"tests\");\n fs.mkdirSync(testsDir, { recursive: true });\n process.stdout.write(`Directory: ${testsDir}\\n`);\n\n const sampleTestPath = path.join(testsDir, \"test_my_agent.test.ts\");\n if (fs.existsSync(sampleTestPath)) {\n process.stderr.write(`Skipped (exists): ${sampleTestPath}\\n`);\n } else {\n fs.writeFileSync(sampleTestPath, CONFTEST_TEMPLATE);\n process.stdout.write(`Created: ${sampleTestPath}\\n`);\n }\n}\n\nfunction cmdValidate(targetDir: string): void {\n const testsDir = path.join(targetDir, \"tests\");\n\n let testFiles: string[] = [];\n if (fs.existsSync(testsDir)) {\n testFiles = fs\n .readdirSync(testsDir)\n .filter((f) => f.endsWith(\".test.ts\") || f.endsWith(\".test.js\"));\n }\n\n if (testFiles.length === 0) {\n process.stderr.write(\n `Warning: no test files (*.test.ts / *.test.js) found in ${testsDir}.\\n`,\n );\n } else {\n process.stdout.write(`Found ${testFiles.length} test file(s) in ${testsDir}\\n`);\n }\n\n // Check for stale golden traces\n const STALE_DAYS = 30;\n const now = Date.now();\n const goldenFiles = findGoldenFiles(targetDir);\n for (const golden of goldenFiles) {\n const mtime = fs.statSync(golden).mtimeMs;\n const ageDays = Math.floor((now - mtime) / (1000 * 60 * 60 * 24));\n if (ageDays > STALE_DAYS) {\n process.stderr.write(\n `Warning: golden trace ${golden} is ${ageDays} days old (>${STALE_DAYS} days). Consider regenerating.\\n`,\n );\n }\n }\n}\n\nfunction findGoldenFiles(dir: string): string[] {\n const results: string[] = [];\n if (!fs.existsSync(dir)) return results;\n\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory() && entry.name !== \"node_modules\") {\n results.push(...findGoldenFiles(fullPath));\n } else if (entry.isFile() && entry.name.endsWith(\".golden\")) {\n results.push(fullPath);\n }\n }\n return results;\n}\n\nexport function main(argv: string[] = process.argv.slice(2)): void {\n const args = argv;\n\n if (args[0] === \"--version\") {\n process.stdout.write(`attest ${VERSION}\\n`);\n process.exit(0);\n }\n\n if (args[0] === \"cache\" && args.length >= 2) {\n if (args[1] === \"stats\") {\n cmdCacheStats();\n process.exit(0);\n } else if (args[1] === \"clear\") {\n cmdCacheClear();\n process.exit(0);\n } else {\n process.stderr.write(`Unknown cache subcommand: ${args[1]}\\n`);\n process.stderr.write(\"Available: cache stats, cache clear\\n\");\n process.exit(1);\n }\n }\n\n if (args[0] === \"init\") {\n cmdInit(process.cwd());\n process.exit(0);\n }\n\n if (args[0] === \"validate\") {\n cmdValidate(process.cwd());\n process.exit(0);\n }\n\n if (args[0] === \"calibrate\") {\n cmdCalibrate(args.slice(1));\n process.exit(0);\n }\n\n process.stderr.write(\n \"Usage: attest <command>\\n\\n\" +\n \"Commands:\\n\" +\n \" --version Print version\\n\" +\n \" init Scaffold a test project\\n\" +\n \" validate Validate test suite\\n\" +\n \" cache stats Show cache statistics\\n\" +\n \" cache clear Clear cache database\\n\" +\n \" calibrate Compute judge agreement metrics over labeled data\\n\",\n );\n process.exit(args.length === 0 ? 0 : 1);\n}\n\ninterface CalibrateOptions {\n labels: string;\n rubric: string;\n rubricVersion: string;\n threshold: number;\n}\n\nfunction parseCalibrateArgs(args: string[]): CalibrateOptions {\n const opts: CalibrateOptions = {\n labels: \"\",\n rubric: \"default\",\n rubricVersion: \"\",\n threshold: 0.5,\n };\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]!;\n const next = args[i + 1];\n if (arg === \"--labels\") {\n if (next === undefined) throw new Error(\"--labels requires a path\");\n opts.labels = next;\n i++;\n } else if (arg === \"--rubric\") {\n if (next === undefined) throw new Error(\"--rubric requires a name\");\n opts.rubric = next;\n i++;\n } else if (arg === \"--rubric-version\") {\n if (next === undefined) throw new Error(\"--rubric-version requires a value\");\n opts.rubricVersion = next;\n i++;\n } else if (arg === \"--threshold\") {\n if (next === undefined) throw new Error(\"--threshold requires a number\");\n const parsed = Number(next);\n if (next.trim() === \"\" || Number.isNaN(parsed)) {\n throw new Error(`--threshold value ${JSON.stringify(next)} is not a number`);\n }\n opts.threshold = parsed;\n i++;\n } else {\n throw new Error(`unknown calibrate flag: ${arg}`);\n }\n }\n if (!opts.labels) {\n throw new Error(\"--labels is required\");\n }\n return opts;\n}\n\nfunction cmdCalibrate(args: string[]): void {\n let opts: CalibrateOptions;\n try {\n opts = parseCalibrateArgs(args);\n } catch (err) {\n process.stderr.write(`calibrate: ${(err as Error).message}\\n`);\n process.exit(2);\n return;\n }\n\n const text = fs.readFileSync(opts.labels, \"utf-8\");\n const ext = path.extname(opts.labels).toLowerCase();\n let records;\n try {\n if (ext === \".jsonl\" || ext === \".ndjson\") {\n records = loadLabelsJSONL(text);\n } else if (ext === \".csv\") {\n records = loadLabelsCSV(text);\n } else if (text.trimStart().startsWith(\"{\")) {\n records = loadLabelsJSONL(text);\n } else {\n records = loadLabelsCSV(text);\n }\n } catch (err) {\n process.stderr.write(`calibrate: ${(err as Error).message}\\n`);\n process.exit(1);\n return;\n }\n\n const pairs: LabelPair[] = records\n .filter((r) => r.judgeKnown)\n .map((r) => ({ human: r.humanLabel, judge: r.judgeScore }));\n const missing = records.filter((r) => !r.judgeKnown).length;\n if (pairs.length === 0) {\n process.stderr.write(\n `calibrate: no rows had judge_score (${missing} rows missing); pre-score the labels file first\\n`,\n );\n process.exit(1);\n return;\n }\n\n let result;\n try {\n result = computeAgreement(pairs, opts.threshold);\n } catch (err) {\n process.stderr.write(`calibrate: ${(err as Error).message}\\n`);\n process.exit(1);\n return;\n }\n\n const output = {\n rubric_name: opts.rubric,\n rubric_version: opts.rubricVersion,\n threshold: result.threshold,\n label_count: result.n,\n agreement: round3(result.agreement),\n cohen_kappa: round3(result.cohenKappa),\n roc_auc: round3(result.rocAuc),\n missing_judge: missing,\n };\n process.stdout.write(JSON.stringify(output, null, 2) + \"\\n\");\n}\n\nfunction round3(x: number): number {\n return Math.round(x * 1000) / 1000;\n}\n\n// Run when executed directly\nconst isDirectExecution =\n typeof import.meta.url === \"string\" &&\n import.meta.url.startsWith(\"file:\") &&\n process.argv[1] !== undefined &&\n import.meta.url.endsWith(path.basename(process.argv[1]));\n\nif (isDirectExecution) {\n main();\n}\n","export const VERSION = \"0.9.0\";\nexport const ENGINE_VERSION = \"0.9.0\";\n","/**\n * Calibration helpers — TS parity with engine/internal/assertion/judge/calibration.go\n * and sdks/python/src/attest/calibration.py.\n *\n * Computes Cohen's κ, agreement %, and ROC-AUC over (human, judge) score\n * pairs and parses CSV/JSONL labels files. Metrics match the engine\n * byte-for-byte on the same input.\n */\n\nimport { createHash } from \"node:crypto\";\n\nexport interface LabelPair {\n readonly human: number;\n readonly judge: number;\n}\n\nexport interface AgreementResult {\n readonly threshold: number;\n readonly n: number;\n readonly agreement: number;\n readonly cohenKappa: number;\n readonly rocAuc: number;\n}\n\nexport interface LabeledRecord {\n input: string;\n humanLabel: number;\n judgeScore: number;\n judgeKnown: boolean;\n}\n\n/**\n * Compute Cohen's κ, agreement %, and ROC-AUC over labeled pairs.\n *\n * Throws when pairs is empty or threshold is outside (0, 1). Cohen's κ\n * degenerates to 0 when all labels land in a single class for both\n * raters; ROC-AUC is reported as 0 when only one human-class is\n * present.\n */\nexport function computeAgreement(\n pairs: readonly LabelPair[],\n threshold: number,\n): AgreementResult {\n if (pairs.length === 0) {\n throw new Error(\"no labeled pairs available\");\n }\n if (threshold <= 0 || threshold >= 1) {\n throw new Error(\"threshold must be in (0, 1)\");\n }\n\n const n = pairs.length;\n let humanPos = 0;\n let judgePos = 0;\n let bothPos = 0;\n let bothNeg = 0;\n for (const p of pairs) {\n const hPos = p.human >= threshold;\n const jPos = p.judge >= threshold;\n if (hPos) humanPos++;\n if (jPos) judgePos++;\n if (hPos && jPos) bothPos++;\n if (!hPos && !jPos) bothNeg++;\n }\n const humanNeg = n - humanPos;\n const judgeNeg = n - judgePos;\n\n const agreement = (bothPos + bothNeg) / n;\n const expected =\n (humanPos * judgePos + humanNeg * judgeNeg) / (n * n);\n const cohenKappa =\n expected < 1 ? (agreement - expected) / (1 - expected) : 0;\n\n const rocAuc = rocAUC(pairs, threshold);\n\n return { threshold, n, agreement, cohenKappa, rocAuc };\n}\n\nfunction rocAUC(pairs: readonly LabelPair[], threshold: number): number {\n const rows: { score: number; pos: boolean }[] = pairs.map((p) => ({\n score: p.judge,\n pos: p.human >= threshold,\n }));\n const pos = rows.filter((r) => r.pos).length;\n const neg = rows.length - pos;\n if (pos === 0 || neg === 0) return 0;\n\n rows.sort((a, b) => a.score - b.score);\n\n let rankSumPos = 0;\n let i = 0;\n while (i < rows.length) {\n let j = i;\n while (j < rows.length && rows[j]!.score === rows[i]!.score) {\n j++;\n }\n const avgRank = (i + j + 1) / 2;\n for (let k = i; k < j; k++) {\n if (rows[k]!.pos) rankSumPos += avgRank;\n }\n i = j;\n }\n\n const auc = (rankSumPos - (pos * (pos + 1)) / 2) / (pos * neg);\n if (Number.isNaN(auc) || !Number.isFinite(auc)) return 0;\n return auc;\n}\n\n/**\n * Parse a 2- or 3-column CSV (input, human_label[, judge_score]).\n * Header detected when row[0][1] does not parse as a float. Lines\n * beginning with `#` are skipped.\n */\nexport function loadLabelsCSV(text: string): LabeledRecord[] {\n const rows = parseCSV(text);\n if (rows.length === 0) {\n throw new Error(\"empty CSV\");\n }\n let start = 0;\n if (rows[0]!.length >= 2) {\n const firstHuman = parseFloatStrict(rows[0]![1]!);\n if (firstHuman === null) start = 1;\n }\n const out: LabeledRecord[] = [];\n for (let idx = start; idx < rows.length; idx++) {\n const row = rows[idx]!;\n const lineNum = idx + 1;\n if (row.length === 0) continue;\n if (row[0]!.trimStart().startsWith(\"#\")) continue;\n if (row.length < 2) {\n throw new Error(\n `CSV line ${lineNum}: want at least 2 columns (input, human_label)`,\n );\n }\n const human = parseFloatStrict(row[1]!);\n if (human === null) {\n throw new Error(`CSV line ${lineNum}: human_label not a float: ${row[1]}`);\n }\n const rec: LabeledRecord = {\n input: row[0]!,\n humanLabel: human,\n judgeScore: 0,\n judgeKnown: false,\n };\n if (row.length >= 3 && row[2]!.trim() !== \"\") {\n const judge = parseFloatStrict(row[2]!);\n if (judge === null) {\n throw new Error(\n `CSV line ${lineNum}: judge_score not a float: ${row[2]}`,\n );\n }\n rec.judgeScore = judge;\n rec.judgeKnown = true;\n }\n out.push(rec);\n }\n if (out.length === 0) {\n throw new Error(\"CSV contained no labeled rows\");\n }\n return out;\n}\n\n/**\n * Parse a newline-delimited JSON file. Each line is an object with the\n * shape {\"input\": \"...\", \"human_label\": 0.9, \"judge_score\": 0.8}.\n * `judge_score` is optional.\n */\nexport function loadLabelsJSONL(text: string): LabeledRecord[] {\n const out: LabeledRecord[] = [];\n const lines = text.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i++) {\n const stripped = lines[i]!.trim();\n if (!stripped) continue;\n let obj: Record<string, unknown>;\n try {\n obj = JSON.parse(stripped) as Record<string, unknown>;\n } catch (err) {\n throw new Error(`JSONL line ${i + 1}: ${(err as Error).message}`);\n }\n if (Object.keys(obj).length === 0) continue;\n if (obj[\"human_label\"] === undefined) {\n throw new Error(`JSONL line ${i + 1}: missing human_label`);\n }\n const human = parseJSONNumber(obj[\"human_label\"]);\n if (human === null) {\n throw new Error(`JSONL line ${i + 1}: human_label is not a number`);\n }\n const rec: LabeledRecord = {\n input: typeof obj[\"input\"] === \"string\" ? obj[\"input\"] : \"\",\n humanLabel: human,\n judgeScore: 0,\n judgeKnown: false,\n };\n if (obj[\"judge_score\"] !== undefined && obj[\"judge_score\"] !== null) {\n const judge = parseJSONNumber(obj[\"judge_score\"]);\n if (judge === null) {\n throw new Error(`JSONL line ${i + 1}: judge_score is not a number`);\n }\n rec.judgeScore = judge;\n rec.judgeKnown = true;\n }\n out.push(rec);\n }\n if (out.length === 0) {\n throw new Error(\"JSONL contained no labeled rows\");\n }\n return out;\n}\n\n/**\n * Return the 16-character SHA-256 prefix used by the engine for prompt\n * hashes. Mirrors promptHash in engine/internal/assertion/judge_eval.go\n * so SDK-recorded calibration rows align with JudgeMetadata.prompt_hash.\n */\nexport function promptHash(text: string): string {\n return createHash(\"sha256\").update(text).digest(\"hex\").slice(0, 16);\n}\n\n/**\n * Parse a string as a number, rejecting empty input and values that\n * JavaScript's `Number()` would silently coerce to 0. Returns null on\n * rejection so callers can throw a useful error. Matches Go's\n * strconv.ParseFloat and Python's float() — both error on the empty\n * string, while bare `Number(\"\")` returns 0.\n *\n * Permits \"Infinity\" so the loader stays consistent with strconv.ParseFloat;\n * downstream consumers (CalibrationStore.Record, computeAgreement) handle\n * the [0, 1] range check separately.\n */\nfunction parseFloatStrict(raw: string): number | null {\n const trimmed = raw.trim();\n if (trimmed === \"\") return null;\n const n = Number(trimmed);\n if (Number.isNaN(n)) return null;\n return n;\n}\n\n/**\n * Coerce a JSON value to a finite number. Accepts only `number` (rejects\n * stringly-typed numbers and booleans) so calibration files cannot\n * accidentally smuggle non-numeric labels past type-loose JSON parsing.\n */\nfunction parseJSONNumber(value: unknown): number | null {\n if (typeof value !== \"number\") return null;\n if (Number.isNaN(value)) return null;\n return value;\n}\n\n/**\n * Minimal RFC-4180-ish CSV parser. Handles quoted fields with embedded\n * commas and CRLF line endings. Sufficient for calibration label files;\n * callers needing full CSV support should bring their own parser.\n */\nfunction parseCSV(text: string): string[][] {\n const rows: string[][] = [];\n let current: string[] = [];\n let field = \"\";\n let inQuotes = false;\n let i = 0;\n while (i < text.length) {\n const ch = text[i]!;\n if (inQuotes) {\n if (ch === '\"') {\n if (text[i + 1] === '\"') {\n field += '\"';\n i += 2;\n continue;\n }\n inQuotes = false;\n i++;\n continue;\n }\n field += ch;\n i++;\n continue;\n }\n if (ch === '\"') {\n inQuotes = true;\n i++;\n continue;\n }\n if (ch === \",\") {\n current.push(field);\n field = \"\";\n i++;\n continue;\n }\n if (ch === \"\\n\") {\n current.push(field);\n rows.push(current);\n current = [];\n field = \"\";\n i++;\n continue;\n }\n if (ch === \"\\r\") {\n i++;\n continue;\n }\n field += ch;\n i++;\n }\n if (field.length > 0 || current.length > 0) {\n current.push(field);\n rows.push(current);\n }\n return rows;\n}\n"],"mappings":"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","names":[]}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { InitializeResult, Trace, Assertion, EvaluateBatchResult, Step, AssertionResult } from './proto/index.js';
|
|
2
|
-
export { AgentId, AssertionId, AssertionSpec, ConstraintSpec, ContentSpec, ERR_ASSERTION_ERROR, ERR_ENGINE_ERROR, ERR_INVALID_TRACE, ERR_PROVIDER_ERROR, ERR_SESSION_ERROR, ERR_TIMEOUT, EmbeddingSpec, EngineTimeoutError, ErrorData, EvaluateBatchParams, InitializeParams, LlmJudgeSpec, PluginSpec, ProtocolError, RPCError, RPCRequest, RPCResponse, STATUS_HARD_FAIL, STATUS_PASS, STATUS_SOFT_FAIL, STEP_AGENT_CALL, STEP_LLM_CALL, STEP_RETRIEVAL, STEP_TOOL_CALL, SchemaSpec, ShutdownResult, SubmitPluginResultParams, TYPE_CONSTRAINT, TYPE_CONTENT, TYPE_EMBEDDING, TYPE_LLM_JUDGE, TYPE_PLUGIN, TYPE_SCHEMA, TYPE_TRACE, TYPE_TRACE_TREE, TraceId, TraceMetadata, TraceSpec, TraceTreeSpec, agentId, assertionId, decodeResponse, encodeRequest, extractId, extractResult, traceId } from './proto/index.js';
|
|
1
|
+
import { InitializeResult, ProtocolLogger, ProtocolDiagnostic, Trace, Assertion, EvaluateBatchResult, DriftReport, SimulatePersona, ConversationMessage, SimulateFaultConfig, Step, AssertionResult } from './proto/index.js';
|
|
2
|
+
export { AgentId, AssertionId, AssertionSpec, BiasProbe, ConstraintSpec, ContentSpec, ERR_ASSERTION_ERROR, ERR_ENGINE_ERROR, ERR_INVALID_TRACE, ERR_PROVIDER_ERROR, ERR_SESSION_ERROR, ERR_TIMEOUT, EmbeddingSpec, EngineTimeoutError, ErrorData, EvaluateBatchParams, GenerateUserMessageParams, GenerateUserMessageResult, InitializeParams, JudgeAgreement, JudgeMetadata, LlmJudgeSpec, PluginSpec, ProtocolDesyncError, ProtocolDiagnosticBuffer, ProtocolDiagnosticKind, ProtocolError, QueryDriftParams, QueryDriftResult, RPCError, RPCRequest, RPCResponse, STATUS_HARD_FAIL, STATUS_PASS, STATUS_SOFT_FAIL, STEP_AGENT_CALL, STEP_LLM_CALL, STEP_RETRIEVAL, STEP_TOOL_CALL, SchemaSpec, ShutdownResult, SubmitPluginResultParams, TYPE_CONSTRAINT, TYPE_CONTENT, TYPE_EMBEDDING, TYPE_LLM_JUDGE, TYPE_PLUGIN, TYPE_SCHEMA, TYPE_TRACE, TYPE_TRACE_TREE, TraceId, TraceMetadata, TraceSpec, TraceTreeSpec, agentId, assertionId, decodeResponse, encodeRequest, extractId, extractResult, layerForType, previewLine, traceId } from './proto/index.js';
|
|
3
3
|
import { ChildProcess } from 'node:child_process';
|
|
4
4
|
import { Interface } from 'node:readline';
|
|
5
5
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
6
6
|
import { EventEmitter } from 'node:events';
|
|
7
7
|
|
|
8
|
-
declare const VERSION = "0.
|
|
9
|
-
declare const ENGINE_VERSION = "0.
|
|
8
|
+
declare const VERSION = "0.9.0";
|
|
9
|
+
declare const ENGINE_VERSION = "0.9.0";
|
|
10
10
|
|
|
11
11
|
declare class EngineManager {
|
|
12
12
|
private enginePath;
|
|
@@ -31,6 +31,28 @@ declare class EngineManager {
|
|
|
31
31
|
get readlineInterface(): Interface | null;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
interface AttestClientOptions {
|
|
35
|
+
/** Logger override. When omitted, warnings only emit if ATTEST_DEBUG_PROTOCOL=1. */
|
|
36
|
+
logger?: ProtocolLogger;
|
|
37
|
+
/** Listener invoked for every protocol diagnostic. */
|
|
38
|
+
onDiagnostic?: (diagnostic: ProtocolDiagnostic) => void;
|
|
39
|
+
/** Diagnostic ring-buffer capacity. Default 32. */
|
|
40
|
+
diagnosticBufferSize?: number;
|
|
41
|
+
/** Number of diagnostics in `desyncWindowMs` that triggers desync rejection. Default 3. */
|
|
42
|
+
desyncThreshold?: number;
|
|
43
|
+
/** Window length for desync detection in milliseconds. Default 1000. */
|
|
44
|
+
desyncWindowMs?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* AttestClient owns request/response correlation over the engine's
|
|
48
|
+
* NDJSON protocol.
|
|
49
|
+
*
|
|
50
|
+
* Desync detection is a one-way latch: once the diagnostic rate breaches
|
|
51
|
+
* `desyncThreshold` within `desyncWindowMs` the client refuses further
|
|
52
|
+
* sends for the lifetime of the instance. There is no reset hook by
|
|
53
|
+
* design — recovery requires constructing a new client over a fresh
|
|
54
|
+
* engine subprocess.
|
|
55
|
+
*/
|
|
34
56
|
declare class AttestClient {
|
|
35
57
|
private readonly engine;
|
|
36
58
|
private requestId;
|
|
@@ -38,10 +60,23 @@ declare class AttestClient {
|
|
|
38
60
|
private writeChain;
|
|
39
61
|
private readerActive;
|
|
40
62
|
private lineHandler;
|
|
41
|
-
|
|
63
|
+
private readonly logger;
|
|
64
|
+
private readonly diagnosticBuffer;
|
|
65
|
+
private readonly diagnosticListeners;
|
|
66
|
+
private readonly desyncThreshold;
|
|
67
|
+
private readonly desyncWindowMs;
|
|
68
|
+
private desynced;
|
|
69
|
+
constructor(engine: EngineManager, options?: AttestClientOptions);
|
|
70
|
+
/** Subscribe to protocol diagnostics. Returns an unsubscribe function. */
|
|
71
|
+
onProtocolDiagnostic(listener: (diagnostic: ProtocolDiagnostic) => void): () => void;
|
|
72
|
+
/** Snapshot of the diagnostic ring buffer (most recent last). */
|
|
73
|
+
protocolDiagnostics(): readonly ProtocolDiagnostic[];
|
|
42
74
|
startReader(): void;
|
|
43
75
|
stopReader(): void;
|
|
76
|
+
private recordDiagnostic;
|
|
77
|
+
private maybeTriggerDesync;
|
|
44
78
|
private handleLine;
|
|
79
|
+
private handleDecodeFailure;
|
|
45
80
|
private failAll;
|
|
46
81
|
sendRequest(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<unknown>;
|
|
47
82
|
evaluateBatch(trace: Trace, assertions: readonly Assertion[], options?: {
|
|
@@ -50,6 +85,12 @@ declare class AttestClient {
|
|
|
50
85
|
submitPluginResult(traceId: string, pluginName: string, assertionId: string, status: string, score: number, explanation: string, options?: {
|
|
51
86
|
timeout?: number;
|
|
52
87
|
}): Promise<boolean>;
|
|
88
|
+
queryDrift(assertionId: string, windowSize?: number, options?: {
|
|
89
|
+
timeout?: number;
|
|
90
|
+
}): Promise<DriftReport>;
|
|
91
|
+
generateUserMessage(persona: SimulatePersona, conversationHistory: readonly ConversationMessage[], faultConfig?: SimulateFaultConfig, options?: {
|
|
92
|
+
timeout?: number;
|
|
93
|
+
}): Promise<string>;
|
|
53
94
|
}
|
|
54
95
|
|
|
55
96
|
declare class TraceBuilder {
|
|
@@ -106,6 +147,29 @@ declare class AgentResult {
|
|
|
106
147
|
get failCount(): number;
|
|
107
148
|
}
|
|
108
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Terminal-grade rendering of Attest assertion failures.
|
|
152
|
+
*
|
|
153
|
+
* Mirrors the Python attest.cli.diagnostics module so PR comments,
|
|
154
|
+
* pytest output, and vitest output show the same fields. Use as the
|
|
155
|
+
* message argument to a vitest `expect(result.passed, ...)` so
|
|
156
|
+
* reviewers see trace path, expected vs actual, and judge metadata.
|
|
157
|
+
*/
|
|
158
|
+
|
|
159
|
+
interface RenderOptions {
|
|
160
|
+
/** Emit ANSI colour escapes. Default false (plain text). */
|
|
161
|
+
color?: boolean;
|
|
162
|
+
/** Source location annotation appended after each block. */
|
|
163
|
+
testFile?: string;
|
|
164
|
+
/** Render passing assertions in addition to failures. Default false. */
|
|
165
|
+
includePassing?: boolean;
|
|
166
|
+
}
|
|
167
|
+
declare function renderAssertionFailure(result: AssertionResult, options?: RenderOptions): string;
|
|
168
|
+
declare function renderSummary(result: AgentResult, options?: {
|
|
169
|
+
color?: boolean;
|
|
170
|
+
}): string;
|
|
171
|
+
declare function renderDiagnostics(result: AgentResult, options?: RenderOptions): string;
|
|
172
|
+
|
|
109
173
|
declare class TraceTree {
|
|
110
174
|
readonly root: Trace;
|
|
111
175
|
constructor(root: Trace);
|
|
@@ -517,7 +581,7 @@ declare class ExpectChain {
|
|
|
517
581
|
* ```
|
|
518
582
|
*/
|
|
519
583
|
outputSimilarTo(reference: string, opts?: {
|
|
520
|
-
threshold?: number;
|
|
584
|
+
threshold?: number | "dynamic";
|
|
521
585
|
model?: string;
|
|
522
586
|
soft?: boolean;
|
|
523
587
|
}): this;
|
|
@@ -538,7 +602,7 @@ declare class ExpectChain {
|
|
|
538
602
|
*/
|
|
539
603
|
passesJudge(criteria: string, opts?: {
|
|
540
604
|
rubric?: string;
|
|
541
|
-
threshold?: number;
|
|
605
|
+
threshold?: number | "dynamic";
|
|
542
606
|
model?: string;
|
|
543
607
|
soft?: boolean;
|
|
544
608
|
}): this;
|
|
@@ -805,21 +869,36 @@ declare function tier(level: number): <T extends (...args: never[]) => unknown>(
|
|
|
805
869
|
* @param options.simulation - Enable simulation mode. All `evaluateBatch` calls
|
|
806
870
|
* return deterministic pass results without spawning the engine or calling any
|
|
807
871
|
* LLM. Equivalent to setting `ATTEST_SIMULATION=1` in the environment.
|
|
872
|
+
* @param options.sampleRate - Sampling rate for continuous evaluation (0.0–1.0).
|
|
873
|
+
* Equivalent to setting `ATTEST_SAMPLE_RATE` in the environment.
|
|
808
874
|
*/
|
|
809
875
|
declare function config(options: {
|
|
810
876
|
simulation?: boolean;
|
|
877
|
+
sampleRate?: number;
|
|
811
878
|
}): void;
|
|
812
879
|
/**
|
|
813
880
|
* Returns true when simulation mode is active via either the programmatic API
|
|
814
881
|
* or the `ATTEST_SIMULATION` environment variable.
|
|
815
882
|
*/
|
|
816
883
|
declare function isSimulationMode(): boolean;
|
|
884
|
+
/**
|
|
885
|
+
* Returns the sampling rate for continuous evaluation. Priority:
|
|
886
|
+
* programmatic `config({ sampleRate })` > `ATTEST_SAMPLE_RATE` env var > 0.
|
|
887
|
+
*/
|
|
888
|
+
declare function getSampleRate(): number;
|
|
817
889
|
/**
|
|
818
890
|
* Reset all programmatic config to defaults. Primarily for use in tests.
|
|
819
891
|
* Environment variables are not affected.
|
|
820
892
|
*/
|
|
821
893
|
declare function resetConfig(): void;
|
|
822
894
|
|
|
895
|
+
/**
|
|
896
|
+
* Return a deterministic all-pass result for every assertion without
|
|
897
|
+
* contacting the engine or any LLM provider. Used when simulation mode
|
|
898
|
+
* is active (`ATTEST_SIMULATION=1` or `config({ simulation: true })`).
|
|
899
|
+
*/
|
|
900
|
+
declare function simulationEvaluateBatch(assertions: readonly Assertion[]): EvaluateBatchResult;
|
|
901
|
+
|
|
823
902
|
interface PluginResult {
|
|
824
903
|
readonly status: string;
|
|
825
904
|
readonly score: number;
|
|
@@ -882,6 +961,64 @@ declare class ContinuousEvalRunner {
|
|
|
882
961
|
private scheduleNext;
|
|
883
962
|
}
|
|
884
963
|
|
|
964
|
+
/**
|
|
965
|
+
* Calibration helpers — TS parity with engine/internal/assertion/judge/calibration.go
|
|
966
|
+
* and sdks/python/src/attest/calibration.py.
|
|
967
|
+
*
|
|
968
|
+
* Computes Cohen's κ, agreement %, and ROC-AUC over (human, judge) score
|
|
969
|
+
* pairs and parses CSV/JSONL labels files. Metrics match the engine
|
|
970
|
+
* byte-for-byte on the same input.
|
|
971
|
+
*/
|
|
972
|
+
interface LabelPair {
|
|
973
|
+
readonly human: number;
|
|
974
|
+
readonly judge: number;
|
|
975
|
+
}
|
|
976
|
+
interface AgreementResult {
|
|
977
|
+
readonly threshold: number;
|
|
978
|
+
readonly n: number;
|
|
979
|
+
readonly agreement: number;
|
|
980
|
+
readonly cohenKappa: number;
|
|
981
|
+
readonly rocAuc: number;
|
|
982
|
+
}
|
|
983
|
+
interface LabeledRecord {
|
|
984
|
+
input: string;
|
|
985
|
+
humanLabel: number;
|
|
986
|
+
judgeScore: number;
|
|
987
|
+
judgeKnown: boolean;
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Compute Cohen's κ, agreement %, and ROC-AUC over labeled pairs.
|
|
991
|
+
*
|
|
992
|
+
* Throws when pairs is empty or threshold is outside (0, 1). Cohen's κ
|
|
993
|
+
* degenerates to 0 when all labels land in a single class for both
|
|
994
|
+
* raters; ROC-AUC is reported as 0 when only one human-class is
|
|
995
|
+
* present.
|
|
996
|
+
*/
|
|
997
|
+
declare function computeAgreement(pairs: readonly LabelPair[], threshold: number): AgreementResult;
|
|
998
|
+
/**
|
|
999
|
+
* Parse a 2- or 3-column CSV (input, human_label[, judge_score]).
|
|
1000
|
+
* Header detected when row[0][1] does not parse as a float. Lines
|
|
1001
|
+
* beginning with `#` are skipped.
|
|
1002
|
+
*/
|
|
1003
|
+
declare function loadLabelsCSV(text: string): LabeledRecord[];
|
|
1004
|
+
/**
|
|
1005
|
+
* Parse a newline-delimited JSON file. Each line is an object with the
|
|
1006
|
+
* shape {"input": "...", "human_label": 0.9, "judge_score": 0.8}.
|
|
1007
|
+
* `judge_score` is optional.
|
|
1008
|
+
*/
|
|
1009
|
+
declare function loadLabelsJSONL(text: string): LabeledRecord[];
|
|
1010
|
+
/**
|
|
1011
|
+
* Return the 16-character SHA-256 prefix used by the engine for prompt
|
|
1012
|
+
* hashes. Mirrors promptHash in engine/internal/assertion/judge_eval.go
|
|
1013
|
+
* so SDK-recorded calibration rows align with JudgeMetadata.prompt_hash.
|
|
1014
|
+
*/
|
|
1015
|
+
declare function promptHash(text: string): string;
|
|
1016
|
+
|
|
1017
|
+
declare const FRIENDLY_USER: SimulatePersona;
|
|
1018
|
+
declare const ADVERSARIAL_USER: SimulatePersona;
|
|
1019
|
+
declare const CONFUSED_USER: SimulatePersona;
|
|
1020
|
+
declare const COOPERATIVE_USER: SimulatePersona;
|
|
1021
|
+
|
|
885
1022
|
declare class ManualAdapter {
|
|
886
1023
|
private readonly agentId;
|
|
887
1024
|
constructor(agentId?: string);
|
|
@@ -1082,4 +1219,4 @@ interface TraceAdapter {
|
|
|
1082
1219
|
traceFromResponse(response: unknown, options?: Record<string, unknown>): Trace;
|
|
1083
1220
|
}
|
|
1084
1221
|
|
|
1085
|
-
export { Agent, AgentResult, AlertDispatcher, type AlertPayload, AnthropicAdapter, Assertion, AssertionResult, AttestClient, type AttestPlugin, ContinuousEvalRunner, ENGINE_VERSION, EngineManager, EvaluateBatchResult, ExpectChain, GeminiAdapter, InitializeResult, LangChainAdapter, ManualAdapter, OTelAdapter, OllamaAdapter, OpenAIAdapter, PluginRegistry, type PluginResult, Sampler, Step, TIER_1, TIER_2, TIER_3, Trace, type TraceAdapter, TraceBuilder, TraceTree, VERSION, activeBuilder, agent, attestExpect, config, delegate, executePluginAssertion, isSimulationMode, resetConfig, tier };
|
|
1222
|
+
export { ADVERSARIAL_USER, Agent, AgentResult, type AgreementResult, AlertDispatcher, type AlertPayload, AnthropicAdapter, Assertion, AssertionResult, AttestClient, type AttestClientOptions, type AttestPlugin, CONFUSED_USER, COOPERATIVE_USER, ContinuousEvalRunner, ConversationMessage, DriftReport, ENGINE_VERSION, EngineManager, EvaluateBatchResult, ExpectChain, FRIENDLY_USER, GeminiAdapter, InitializeResult, type LabelPair, type LabeledRecord, LangChainAdapter, ManualAdapter, OTelAdapter, OllamaAdapter, OpenAIAdapter, PluginRegistry, type PluginResult, ProtocolDiagnostic, ProtocolLogger, type RenderOptions, Sampler, SimulateFaultConfig, SimulatePersona, Step, TIER_1, TIER_2, TIER_3, Trace, type TraceAdapter, TraceBuilder, TraceTree, VERSION, activeBuilder, agent, attestExpect, computeAgreement, config, delegate, executePluginAssertion, getSampleRate, isSimulationMode, loadLabelsCSV, loadLabelsJSONL, promptHash, renderAssertionFailure, renderDiagnostics, renderSummary, resetConfig, simulationEvaluateBatch, tier };
|