@attest-ai/core 0.6.0 → 1.0.0-rc.1
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 +890 -127
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +158 -16
- package/dist/cjs/proto/index.cjs +117 -0
- package/dist/cjs/proto/index.cjs.map +1 -1
- package/dist/cjs/proto/index.d.cts +135 -1
- package/dist/esm/cli.d.ts +19 -4
- package/dist/esm/cli.js +478 -116
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/index.d.ts +158 -16
- package/dist/esm/index.js +870 -128
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/proto/index.d.ts +135 -1
- package/dist/esm/proto/index.js +103 -0
- package/dist/esm/proto/index.js.map +1 -1
- package/package.json +4 -3
package/dist/cjs/index.cjs
CHANGED
|
@@ -50,15 +50,27 @@ __export(index_exports, {
|
|
|
50
50
|
EngineTimeoutError: () => EngineTimeoutError,
|
|
51
51
|
ExpectChain: () => ExpectChain,
|
|
52
52
|
FRIENDLY_USER: () => FRIENDLY_USER,
|
|
53
|
+
FailureClass: () => FailureClass,
|
|
53
54
|
GeminiAdapter: () => GeminiAdapter,
|
|
54
55
|
LangChainAdapter: () => LangChainAdapter,
|
|
55
56
|
ManualAdapter: () => ManualAdapter,
|
|
56
57
|
OTelAdapter: () => OTelAdapter,
|
|
58
|
+
OUTCOME_FAIL: () => OUTCOME_FAIL,
|
|
59
|
+
OUTCOME_PASS: () => OUTCOME_PASS,
|
|
60
|
+
OUTCOME_SKIP: () => OUTCOME_SKIP,
|
|
57
61
|
OllamaAdapter: () => OllamaAdapter,
|
|
58
62
|
OpenAIAdapter: () => OpenAIAdapter,
|
|
59
63
|
PluginRegistry: () => PluginRegistry,
|
|
64
|
+
ProtocolDesyncError: () => ProtocolDesyncError,
|
|
65
|
+
ProtocolDiagnosticBuffer: () => ProtocolDiagnosticBuffer,
|
|
60
66
|
ProtocolError: () => ProtocolError,
|
|
67
|
+
REASON_MISSING_EVIDENCE: () => REASON_MISSING_EVIDENCE,
|
|
68
|
+
REASON_PROVIDER_NOT_CONFIGURED: () => REASON_PROVIDER_NOT_CONFIGURED,
|
|
69
|
+
REASON_PROVIDER_UNAVAILABLE: () => REASON_PROVIDER_UNAVAILABLE,
|
|
70
|
+
REASON_SHORT_CIRCUITED: () => REASON_SHORT_CIRCUITED,
|
|
71
|
+
REASON_SIMULATION: () => REASON_SIMULATION,
|
|
61
72
|
STATUS_HARD_FAIL: () => STATUS_HARD_FAIL,
|
|
73
|
+
STATUS_NOT_EVALUATED: () => STATUS_NOT_EVALUATED,
|
|
62
74
|
STATUS_PASS: () => STATUS_PASS,
|
|
63
75
|
STATUS_SOFT_FAIL: () => STATUS_SOFT_FAIL,
|
|
64
76
|
STEP_AGENT_CALL: () => STEP_AGENT_CALL,
|
|
@@ -85,6 +97,7 @@ __export(index_exports, {
|
|
|
85
97
|
agentId: () => agentId,
|
|
86
98
|
assertionId: () => assertionId,
|
|
87
99
|
attestExpect: () => attestExpect,
|
|
100
|
+
computeAgreement: () => computeAgreement,
|
|
88
101
|
config: () => config,
|
|
89
102
|
decodeResponse: () => decodeResponse,
|
|
90
103
|
delegate: () => delegate,
|
|
@@ -94,8 +107,17 @@ __export(index_exports, {
|
|
|
94
107
|
extractResult: () => extractResult,
|
|
95
108
|
getSampleRate: () => getSampleRate,
|
|
96
109
|
isSimulationMode: () => isSimulationMode,
|
|
110
|
+
layerForType: () => layerForType,
|
|
111
|
+
loadLabelsCSV: () => loadLabelsCSV,
|
|
112
|
+
loadLabelsJSONL: () => loadLabelsJSONL,
|
|
113
|
+
previewLine: () => previewLine,
|
|
114
|
+
promptHash: () => promptHash,
|
|
115
|
+
renderAssertionFailure: () => renderAssertionFailure,
|
|
116
|
+
renderDiagnostics: () => renderDiagnostics,
|
|
117
|
+
renderSummary: () => renderSummary,
|
|
118
|
+
requireSimulationCapability: () => requireSimulationCapability,
|
|
97
119
|
resetConfig: () => resetConfig,
|
|
98
|
-
|
|
120
|
+
simulationEnvEnabled: () => simulationEnvEnabled,
|
|
99
121
|
tier: () => tier,
|
|
100
122
|
traceId: () => traceId
|
|
101
123
|
});
|
|
@@ -109,6 +131,15 @@ var STEP_AGENT_CALL = "agent_call";
|
|
|
109
131
|
var STATUS_PASS = "pass";
|
|
110
132
|
var STATUS_SOFT_FAIL = "soft_fail";
|
|
111
133
|
var STATUS_HARD_FAIL = "hard_fail";
|
|
134
|
+
var STATUS_NOT_EVALUATED = "not_evaluated";
|
|
135
|
+
var REASON_PROVIDER_UNAVAILABLE = "provider_unavailable";
|
|
136
|
+
var REASON_PROVIDER_NOT_CONFIGURED = "provider_not_configured";
|
|
137
|
+
var REASON_SIMULATION = "simulation";
|
|
138
|
+
var REASON_MISSING_EVIDENCE = "missing_evidence";
|
|
139
|
+
var REASON_SHORT_CIRCUITED = "short_circuited";
|
|
140
|
+
var OUTCOME_PASS = "pass";
|
|
141
|
+
var OUTCOME_FAIL = "fail";
|
|
142
|
+
var OUTCOME_SKIP = "skip";
|
|
112
143
|
var TYPE_SCHEMA = "schema";
|
|
113
144
|
var TYPE_CONSTRAINT = "constraint";
|
|
114
145
|
var TYPE_TRACE = "trace";
|
|
@@ -134,6 +165,34 @@ function assertionId(id) {
|
|
|
134
165
|
function agentId(id) {
|
|
135
166
|
return id;
|
|
136
167
|
}
|
|
168
|
+
var FailureClass = {
|
|
169
|
+
BrokenCode: "broken_code",
|
|
170
|
+
FlakyJudge: "flaky_judge",
|
|
171
|
+
BadRubric: "bad_rubric",
|
|
172
|
+
MissingTraceData: "missing_trace_data",
|
|
173
|
+
StochasticVariance: "stochastic_variance"
|
|
174
|
+
};
|
|
175
|
+
function layerForType(assertionType) {
|
|
176
|
+
switch (assertionType) {
|
|
177
|
+
case "schema":
|
|
178
|
+
return 1;
|
|
179
|
+
case "constraint":
|
|
180
|
+
return 2;
|
|
181
|
+
case "trace":
|
|
182
|
+
case "trace_tree":
|
|
183
|
+
return 3;
|
|
184
|
+
case "content":
|
|
185
|
+
return 4;
|
|
186
|
+
case "embedding":
|
|
187
|
+
return 5;
|
|
188
|
+
case "llm_judge":
|
|
189
|
+
return 6;
|
|
190
|
+
case "plugin":
|
|
191
|
+
return 8;
|
|
192
|
+
default:
|
|
193
|
+
return 0;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
137
196
|
|
|
138
197
|
// src/proto/errors.ts
|
|
139
198
|
var ProtocolError = class extends Error {
|
|
@@ -160,6 +219,58 @@ var EngineTimeoutError = class extends Error {
|
|
|
160
219
|
this.timeoutMs = timeoutMs;
|
|
161
220
|
}
|
|
162
221
|
};
|
|
222
|
+
var ProtocolDesyncError = class extends Error {
|
|
223
|
+
diagnostics;
|
|
224
|
+
constructor(message, diagnostics) {
|
|
225
|
+
super(message);
|
|
226
|
+
this.name = "ProtocolDesyncError";
|
|
227
|
+
this.diagnostics = diagnostics;
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// src/proto/diagnostics.ts
|
|
232
|
+
var DEFAULT_BUFFER_SIZE = 32;
|
|
233
|
+
var MAX_LINE_PREVIEW = 512;
|
|
234
|
+
var ProtocolDiagnosticBuffer = class {
|
|
235
|
+
capacity;
|
|
236
|
+
entries = [];
|
|
237
|
+
constructor(capacity = DEFAULT_BUFFER_SIZE) {
|
|
238
|
+
if (capacity <= 0) {
|
|
239
|
+
throw new Error(
|
|
240
|
+
`ProtocolDiagnosticBuffer capacity must be positive, got ${capacity}`
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
this.capacity = capacity;
|
|
244
|
+
}
|
|
245
|
+
push(diagnostic) {
|
|
246
|
+
if (this.entries.length >= this.capacity) {
|
|
247
|
+
this.entries.shift();
|
|
248
|
+
}
|
|
249
|
+
this.entries.push(diagnostic);
|
|
250
|
+
}
|
|
251
|
+
snapshot() {
|
|
252
|
+
return this.entries.slice();
|
|
253
|
+
}
|
|
254
|
+
countWithin(windowMs, nowMs) {
|
|
255
|
+
const cutoff = nowMs - windowMs;
|
|
256
|
+
let count = 0;
|
|
257
|
+
for (let i = this.entries.length - 1; i >= 0; i -= 1) {
|
|
258
|
+
if (this.entries[i].timestampMs >= cutoff) {
|
|
259
|
+
count += 1;
|
|
260
|
+
} else {
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return count;
|
|
265
|
+
}
|
|
266
|
+
clear() {
|
|
267
|
+
this.entries.length = 0;
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
function previewLine(line) {
|
|
271
|
+
if (line.length <= MAX_LINE_PREVIEW) return line;
|
|
272
|
+
return line.slice(0, MAX_LINE_PREVIEW) + "\u2026";
|
|
273
|
+
}
|
|
163
274
|
|
|
164
275
|
// src/proto/codec.ts
|
|
165
276
|
function encodeRequest(id, method, params) {
|
|
@@ -222,15 +333,14 @@ function extractId(response) {
|
|
|
222
333
|
}
|
|
223
334
|
|
|
224
335
|
// src/version.ts
|
|
225
|
-
var VERSION = "0.
|
|
226
|
-
var ENGINE_VERSION = "0.
|
|
336
|
+
var VERSION = "1.0.0-rc.1";
|
|
337
|
+
var ENGINE_VERSION = "1.0.0-rc.1";
|
|
227
338
|
|
|
228
339
|
// src/engine-manager.ts
|
|
229
340
|
var import_node_child_process = require("child_process");
|
|
230
341
|
var import_node_readline = require("readline");
|
|
231
342
|
var import_node_fs = require("fs");
|
|
232
343
|
var import_node_path = require("path");
|
|
233
|
-
var import_node_url = require("url");
|
|
234
344
|
|
|
235
345
|
// src/engine-downloader.ts
|
|
236
346
|
var https = __toESM(require("https"), 1);
|
|
@@ -242,10 +352,8 @@ var crypto = __toESM(require("crypto"), 1);
|
|
|
242
352
|
var RELEASE_BASE = `https://github.com/attest-framework/attest/releases/download/v${ENGINE_VERSION}`;
|
|
243
353
|
var CHECKSUMS_URL = `${RELEASE_BASE}/checksums-sha256.txt`;
|
|
244
354
|
var MAX_REDIRECTS = 5;
|
|
245
|
-
function
|
|
246
|
-
|
|
247
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
248
|
-
return dir;
|
|
355
|
+
function versionedBinDir() {
|
|
356
|
+
return path.join(os.homedir(), ".attest", "bin", ENGINE_VERSION);
|
|
249
357
|
}
|
|
250
358
|
function platformKey() {
|
|
251
359
|
const platform2 = os.platform();
|
|
@@ -273,20 +381,8 @@ function binaryFilename() {
|
|
|
273
381
|
return os.platform() === "win32" ? "attest-engine.exe" : "attest-engine";
|
|
274
382
|
}
|
|
275
383
|
function cachedEnginePath() {
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
const versionFile = path.join(dir, ".engine-version");
|
|
279
|
-
if (!fs.existsSync(binPath)) {
|
|
280
|
-
return null;
|
|
281
|
-
}
|
|
282
|
-
if (!fs.existsSync(versionFile)) {
|
|
283
|
-
return null;
|
|
284
|
-
}
|
|
285
|
-
const cachedVersion = fs.readFileSync(versionFile, "utf-8").trim();
|
|
286
|
-
if (cachedVersion !== ENGINE_VERSION) {
|
|
287
|
-
return null;
|
|
288
|
-
}
|
|
289
|
-
return binPath;
|
|
384
|
+
const binPath = path.join(versionedBinDir(), binaryFilename());
|
|
385
|
+
return fs.statSync(binPath, { throwIfNoEntry: false })?.isFile() ? binPath : null;
|
|
290
386
|
}
|
|
291
387
|
function parseChecksums(text) {
|
|
292
388
|
const checksums = /* @__PURE__ */ new Map();
|
|
@@ -306,7 +402,7 @@ function httpsGet(url, redirectCount = 0) {
|
|
|
306
402
|
new Error(`Too many redirects (>${MAX_REDIRECTS}) following ${url}`)
|
|
307
403
|
);
|
|
308
404
|
}
|
|
309
|
-
return new Promise((
|
|
405
|
+
return new Promise((resolve, reject) => {
|
|
310
406
|
const client = url.startsWith("https:") ? https : http;
|
|
311
407
|
client.get(url, (res) => {
|
|
312
408
|
const status = res.statusCode ?? 0;
|
|
@@ -317,7 +413,7 @@ function httpsGet(url, redirectCount = 0) {
|
|
|
317
413
|
return;
|
|
318
414
|
}
|
|
319
415
|
res.resume();
|
|
320
|
-
|
|
416
|
+
resolve(httpsGet(location, redirectCount + 1));
|
|
321
417
|
return;
|
|
322
418
|
}
|
|
323
419
|
if (status < 200 || status >= 300) {
|
|
@@ -327,14 +423,14 @@ function httpsGet(url, redirectCount = 0) {
|
|
|
327
423
|
}
|
|
328
424
|
const chunks = [];
|
|
329
425
|
res.on("data", (chunk) => chunks.push(chunk));
|
|
330
|
-
res.on("end", () =>
|
|
426
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
331
427
|
res.on("error", reject);
|
|
332
428
|
}).on("error", reject);
|
|
333
429
|
});
|
|
334
430
|
}
|
|
335
431
|
async function downloadEngine() {
|
|
336
432
|
const key = platformKey();
|
|
337
|
-
const filename = `attest-engine-${key}`;
|
|
433
|
+
const filename = `attest-engine-${key}${os.platform() === "win32" ? ".exe" : ""}`;
|
|
338
434
|
const binaryUrl = `${RELEASE_BASE}/${filename}`;
|
|
339
435
|
process.stderr.write(`[attest] Downloading engine v${ENGINE_VERSION} for ${key}...
|
|
340
436
|
`);
|
|
@@ -354,66 +450,117 @@ async function downloadEngine() {
|
|
|
354
450
|
const actualHash = crypto.createHash("sha256").update(data).digest("hex");
|
|
355
451
|
if (actualHash !== expectedHash) {
|
|
356
452
|
throw new Error(
|
|
357
|
-
`SHA256 mismatch for ${filename}. Expected: ${expectedHash}, got: ${actualHash}. The download may be corrupted.
|
|
453
|
+
`SHA256 mismatch for ${filename}. Expected: ${expectedHash}, got: ${actualHash}. The download may be corrupted. Retry or download manually.`
|
|
358
454
|
);
|
|
359
455
|
}
|
|
360
456
|
process.stderr.write(`[attest] Checksum verified.
|
|
361
457
|
`);
|
|
362
|
-
const dir =
|
|
363
|
-
|
|
364
|
-
const destPath = path.join(dir,
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
458
|
+
const dir = versionedBinDir();
|
|
459
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
460
|
+
const destPath = path.join(dir, binaryFilename());
|
|
461
|
+
const suffix = `${String(process.pid)}.${crypto.randomBytes(8).toString("hex")}`;
|
|
462
|
+
const tmpPath = path.join(dir, `.${binaryFilename()}.tmp.${suffix}`);
|
|
463
|
+
try {
|
|
464
|
+
fs.writeFileSync(tmpPath, data, { flag: "wx", mode: 493 });
|
|
465
|
+
fs.chmodSync(tmpPath, 493);
|
|
466
|
+
fs.renameSync(tmpPath, destPath);
|
|
467
|
+
} catch (err) {
|
|
468
|
+
if (err.code !== "EEXIST") fs.rmSync(tmpPath, { force: true });
|
|
469
|
+
throw err;
|
|
470
|
+
}
|
|
370
471
|
process.stderr.write(`[attest] Engine installed at ${destPath}
|
|
371
472
|
`);
|
|
372
473
|
return destPath;
|
|
373
474
|
}
|
|
374
475
|
|
|
375
476
|
// src/engine-manager.ts
|
|
376
|
-
var import_meta = {};
|
|
377
477
|
var ENGINE_BINARY_NAME = "attest-engine";
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
478
|
+
var VERSION_PROBE_TIMEOUT_MS = 1e4;
|
|
479
|
+
var EngineVersionMismatchError = class extends Error {
|
|
480
|
+
enginePath;
|
|
481
|
+
source;
|
|
482
|
+
found;
|
|
483
|
+
required;
|
|
484
|
+
constructor(enginePath, source, found, required) {
|
|
485
|
+
const reported = found === null ? "reports no version" : `reports version ${found}`;
|
|
486
|
+
super(
|
|
487
|
+
`attest-engine at ${enginePath} (from ${source}) ${reported}; this SDK requires attest-engine ${required}. Point ATTEST_ENGINE_PATH at a matching engine, or remove the mismatched one from PATH so the SDK can download ${required} into ~/.attest/bin/${required}/.`
|
|
488
|
+
);
|
|
489
|
+
this.name = "EngineVersionMismatchError";
|
|
490
|
+
this.enginePath = enginePath;
|
|
491
|
+
this.source = source;
|
|
492
|
+
this.found = found;
|
|
493
|
+
this.required = required;
|
|
393
494
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
495
|
+
};
|
|
496
|
+
function engineVersion(enginePath) {
|
|
497
|
+
const proc = (0, import_node_child_process.spawnSync)(enginePath, ["version"], {
|
|
498
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
499
|
+
encoding: "utf-8",
|
|
500
|
+
timeout: VERSION_PROBE_TIMEOUT_MS
|
|
501
|
+
});
|
|
502
|
+
if (proc.error) throw proc.error;
|
|
503
|
+
const out = proc.stdout.trim();
|
|
504
|
+
const prefix = `${ENGINE_BINARY_NAME} `;
|
|
505
|
+
if (proc.status !== 0 || !out.startsWith(prefix)) return null;
|
|
506
|
+
return out.slice(prefix.length).trim();
|
|
507
|
+
}
|
|
508
|
+
function onPath() {
|
|
509
|
+
for (const dir of (process.env["PATH"] ?? "").split(import_node_path.delimiter)) {
|
|
510
|
+
if (!dir) continue;
|
|
511
|
+
const candidate = (0, import_node_path.join)(dir, binaryFilename());
|
|
402
512
|
try {
|
|
403
|
-
(0, import_node_fs.accessSync)(
|
|
404
|
-
if ((0, import_node_fs.existsSync)(resolved)) return resolved;
|
|
513
|
+
(0, import_node_fs.accessSync)(candidate, import_node_fs.constants.X_OK);
|
|
405
514
|
} catch {
|
|
515
|
+
continue;
|
|
406
516
|
}
|
|
517
|
+
if ((0, import_node_fs.statSync)(candidate).isFile()) return candidate;
|
|
518
|
+
}
|
|
519
|
+
return null;
|
|
520
|
+
}
|
|
521
|
+
function requireFile(enginePath, label) {
|
|
522
|
+
if (!(0, import_node_fs.existsSync)(enginePath) || !(0, import_node_fs.statSync)(enginePath).isFile()) {
|
|
523
|
+
throw new Error(`${label}=${enginePath} does not exist or is not a file.`);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
async function locateEngine(explicitPath, allowDownload) {
|
|
527
|
+
if (explicitPath) {
|
|
528
|
+
requireFile(explicitPath, "Engine path");
|
|
529
|
+
return [explicitPath, "explicit engine path"];
|
|
530
|
+
}
|
|
531
|
+
const envPath = process.env["ATTEST_ENGINE_PATH"];
|
|
532
|
+
if (envPath) {
|
|
533
|
+
requireFile(envPath, "ATTEST_ENGINE_PATH");
|
|
534
|
+
return [envPath, "ATTEST_ENGINE_PATH"];
|
|
535
|
+
}
|
|
536
|
+
const found = onPath();
|
|
537
|
+
if (found) return [found, "PATH"];
|
|
538
|
+
const cached = cachedEnginePath();
|
|
539
|
+
if (cached) return [cached, "engine cache"];
|
|
540
|
+
if (!allowDownload) {
|
|
541
|
+
throw new Error(
|
|
542
|
+
`No '${ENGINE_BINARY_NAME}' ${ENGINE_VERSION} in ATTEST_ENGINE_PATH, PATH or ~/.attest/bin/${ENGINE_VERSION}/.`
|
|
543
|
+
);
|
|
407
544
|
}
|
|
408
|
-
const noDownload = process.env["ATTEST_ENGINE_NO_DOWNLOAD"];
|
|
545
|
+
const noDownload = process.env["ATTEST_ENGINE_NO_DOWNLOAD"]?.toLowerCase();
|
|
409
546
|
if (noDownload === "1" || noDownload === "true" || noDownload === "yes") {
|
|
410
547
|
throw new Error(
|
|
411
|
-
`Cannot find '${ENGINE_BINARY_NAME}'
|
|
412
|
-
|
|
413
|
-
|
|
548
|
+
`Cannot find '${ENGINE_BINARY_NAME}' ${ENGINE_VERSION} and ATTEST_ENGINE_NO_DOWNLOAD is set.
|
|
549
|
+
Install options:
|
|
550
|
+
1. Build from source: make engine, then set ATTEST_ENGINE_PATH
|
|
551
|
+
2. Download release v${ENGINE_VERSION}: https://github.com/attest-framework/attest/releases
|
|
552
|
+
3. Allow auto-download by unsetting ATTEST_ENGINE_NO_DOWNLOAD`
|
|
414
553
|
);
|
|
415
554
|
}
|
|
416
|
-
return downloadEngine();
|
|
555
|
+
return [await downloadEngine(), "download"];
|
|
556
|
+
}
|
|
557
|
+
async function resolveEngine(explicitPath, options = {}) {
|
|
558
|
+
const [enginePath, source] = await locateEngine(explicitPath, options.allowDownload ?? true);
|
|
559
|
+
const found = engineVersion(enginePath);
|
|
560
|
+
if (found !== ENGINE_VERSION) {
|
|
561
|
+
throw new EngineVersionMismatchError(enginePath, source, found, ENGINE_VERSION);
|
|
562
|
+
}
|
|
563
|
+
return enginePath;
|
|
417
564
|
}
|
|
418
565
|
var EngineManager = class {
|
|
419
566
|
enginePath;
|
|
@@ -428,28 +575,41 @@ var EngineManager = class {
|
|
|
428
575
|
this.logLevel = options?.logLevel ?? "warn";
|
|
429
576
|
}
|
|
430
577
|
async start() {
|
|
431
|
-
|
|
432
|
-
this.enginePath = await findEngineBinary();
|
|
433
|
-
}
|
|
578
|
+
this.enginePath = await resolveEngine(this.enginePath || void 0);
|
|
434
579
|
this.process = (0, import_node_child_process.spawn)(this.enginePath, [`--log-level=${this.logLevel}`], {
|
|
435
580
|
stdio: ["pipe", "pipe", "pipe"]
|
|
436
581
|
});
|
|
437
582
|
this.reader = (0, import_node_readline.createInterface)({ input: this.process.stdout });
|
|
438
|
-
|
|
583
|
+
try {
|
|
584
|
+
this.initResult = await this.initialize();
|
|
585
|
+
} catch (err) {
|
|
586
|
+
await this.stop();
|
|
587
|
+
throw err;
|
|
588
|
+
}
|
|
589
|
+
this.initialized = true;
|
|
590
|
+
return this.initResult;
|
|
591
|
+
}
|
|
592
|
+
/** Send initialize and check the engine's version and capabilities. */
|
|
593
|
+
async initialize() {
|
|
594
|
+
const init = await this.sendRequestInternal("initialize", {
|
|
439
595
|
sdk_name: "attest-typescript",
|
|
440
596
|
sdk_version: VERSION,
|
|
441
597
|
protocol_version: 1,
|
|
442
598
|
required_capabilities: ["layers_1_4"],
|
|
443
599
|
preferred_encoding: "json"
|
|
444
600
|
});
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
601
|
+
if (init.engine_version !== ENGINE_VERSION) {
|
|
602
|
+
throw new EngineVersionMismatchError(
|
|
603
|
+
this.enginePath,
|
|
604
|
+
"initialize",
|
|
605
|
+
init.engine_version,
|
|
606
|
+
ENGINE_VERSION
|
|
449
607
|
);
|
|
450
608
|
}
|
|
451
|
-
|
|
452
|
-
|
|
609
|
+
if (!init.compatible) {
|
|
610
|
+
throw new Error(`Engine incompatible. Missing capabilities: ${JSON.stringify(init.missing)}`);
|
|
611
|
+
}
|
|
612
|
+
return init;
|
|
453
613
|
}
|
|
454
614
|
async stop() {
|
|
455
615
|
if (this.process === null) return;
|
|
@@ -490,11 +650,11 @@ var EngineManager = class {
|
|
|
490
650
|
}
|
|
491
651
|
this.requestId += 1;
|
|
492
652
|
const requestStr = encodeRequest(this.requestId, method, params);
|
|
493
|
-
return new Promise((
|
|
653
|
+
return new Promise((resolve, reject) => {
|
|
494
654
|
const onLine = (line) => {
|
|
495
655
|
try {
|
|
496
656
|
const response = decodeResponse(line);
|
|
497
|
-
|
|
657
|
+
resolve(extractResult(response));
|
|
498
658
|
} catch (err) {
|
|
499
659
|
reject(err);
|
|
500
660
|
}
|
|
@@ -542,7 +702,11 @@ function config(options) {
|
|
|
542
702
|
}
|
|
543
703
|
}
|
|
544
704
|
function isSimulationMode() {
|
|
545
|
-
return _simulationMode ||
|
|
705
|
+
return _simulationMode || simulationEnvEnabled();
|
|
706
|
+
}
|
|
707
|
+
function simulationEnvEnabled() {
|
|
708
|
+
const env = (process.env["ATTEST_SIMULATION"] ?? "").trim().toLowerCase();
|
|
709
|
+
return env === "1" || env === "true" || env === "yes";
|
|
546
710
|
}
|
|
547
711
|
function getSampleRate() {
|
|
548
712
|
if (_sampleRate > 0) return _sampleRate;
|
|
@@ -563,25 +727,11 @@ function resetConfig() {
|
|
|
563
727
|
_sampleRate = 0;
|
|
564
728
|
}
|
|
565
729
|
|
|
566
|
-
// src/simulation.ts
|
|
567
|
-
function simulationEvaluateBatch(assertions) {
|
|
568
|
-
const results = assertions.map((assertion) => ({
|
|
569
|
-
assertion_id: assertion.assertion_id,
|
|
570
|
-
status: "pass",
|
|
571
|
-
score: 1,
|
|
572
|
-
explanation: `[simulation] assertion '${assertion.assertion_id}' passed (type: ${assertion.type})`,
|
|
573
|
-
cost: 0,
|
|
574
|
-
duration_ms: 0
|
|
575
|
-
}));
|
|
576
|
-
return {
|
|
577
|
-
results,
|
|
578
|
-
total_cost: 0,
|
|
579
|
-
total_duration_ms: 0
|
|
580
|
-
};
|
|
581
|
-
}
|
|
582
|
-
|
|
583
730
|
// src/client.ts
|
|
584
731
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
732
|
+
var DEFAULT_DIAGNOSTIC_BUFFER = 32;
|
|
733
|
+
var DEFAULT_DESYNC_THRESHOLD = 3;
|
|
734
|
+
var DEFAULT_DESYNC_WINDOW_MS = 1e3;
|
|
585
735
|
function resolveTimeoutMs(override) {
|
|
586
736
|
if (override !== void 0) return override;
|
|
587
737
|
const env = process.env["ATTEST_ENGINE_TIMEOUT"];
|
|
@@ -603,6 +753,24 @@ function makeTimeoutPromise(method, timeoutMs) {
|
|
|
603
753
|
}, timeoutMs);
|
|
604
754
|
});
|
|
605
755
|
}
|
|
756
|
+
function debugProtocolEnabled() {
|
|
757
|
+
const flag = process.env["ATTEST_DEBUG_PROTOCOL"];
|
|
758
|
+
return flag === "1" || flag === "true" || flag === "yes";
|
|
759
|
+
}
|
|
760
|
+
function envLoggerDefault() {
|
|
761
|
+
if (debugProtocolEnabled()) {
|
|
762
|
+
return {
|
|
763
|
+
warn: (msg, ...args) => console.warn(msg, ...args),
|
|
764
|
+
error: (msg, ...args) => console.error(msg, ...args)
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
return {
|
|
768
|
+
warn: () => {
|
|
769
|
+
},
|
|
770
|
+
error: () => {
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
}
|
|
606
774
|
var AttestClient = class {
|
|
607
775
|
engine;
|
|
608
776
|
requestId = 0;
|
|
@@ -610,8 +778,34 @@ var AttestClient = class {
|
|
|
610
778
|
writeChain = Promise.resolve();
|
|
611
779
|
readerActive = false;
|
|
612
780
|
lineHandler = null;
|
|
613
|
-
|
|
781
|
+
logger;
|
|
782
|
+
diagnosticBuffer;
|
|
783
|
+
diagnosticListeners = /* @__PURE__ */ new Set();
|
|
784
|
+
desyncThreshold;
|
|
785
|
+
desyncWindowMs;
|
|
786
|
+
desynced = false;
|
|
787
|
+
constructor(engine, options) {
|
|
614
788
|
this.engine = engine;
|
|
789
|
+
this.logger = options?.logger ?? envLoggerDefault();
|
|
790
|
+
this.diagnosticBuffer = new ProtocolDiagnosticBuffer(
|
|
791
|
+
options?.diagnosticBufferSize ?? DEFAULT_DIAGNOSTIC_BUFFER
|
|
792
|
+
);
|
|
793
|
+
this.desyncThreshold = options?.desyncThreshold ?? DEFAULT_DESYNC_THRESHOLD;
|
|
794
|
+
this.desyncWindowMs = options?.desyncWindowMs ?? DEFAULT_DESYNC_WINDOW_MS;
|
|
795
|
+
if (options?.onDiagnostic !== void 0) {
|
|
796
|
+
this.diagnosticListeners.add(options.onDiagnostic);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
/** Subscribe to protocol diagnostics. Returns an unsubscribe function. */
|
|
800
|
+
onProtocolDiagnostic(listener) {
|
|
801
|
+
this.diagnosticListeners.add(listener);
|
|
802
|
+
return () => {
|
|
803
|
+
this.diagnosticListeners.delete(listener);
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
/** Snapshot of the diagnostic ring buffer (most recent last). */
|
|
807
|
+
protocolDiagnostics() {
|
|
808
|
+
return this.diagnosticBuffer.snapshot();
|
|
615
809
|
}
|
|
616
810
|
startReader() {
|
|
617
811
|
if (this.readerActive) return;
|
|
@@ -636,35 +830,101 @@ var AttestClient = class {
|
|
|
636
830
|
this.lineHandler = null;
|
|
637
831
|
this.readerActive = false;
|
|
638
832
|
}
|
|
833
|
+
recordDiagnostic(kind, message, rawLine) {
|
|
834
|
+
const diagnostic = {
|
|
835
|
+
kind,
|
|
836
|
+
message,
|
|
837
|
+
rawLine: previewLine(rawLine),
|
|
838
|
+
timestampMs: Date.now()
|
|
839
|
+
};
|
|
840
|
+
this.diagnosticBuffer.push(diagnostic);
|
|
841
|
+
this.logger.warn(`[attest.protocol] ${kind}: ${message}`);
|
|
842
|
+
for (const listener of this.diagnosticListeners) {
|
|
843
|
+
try {
|
|
844
|
+
listener(diagnostic);
|
|
845
|
+
} catch (err) {
|
|
846
|
+
this.logger.error(
|
|
847
|
+
`[attest.protocol] diagnostic listener threw: ${err instanceof Error ? err.message : String(err)}`
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
this.maybeTriggerDesync(diagnostic.timestampMs);
|
|
852
|
+
}
|
|
853
|
+
maybeTriggerDesync(nowMs) {
|
|
854
|
+
if (this.desynced) return;
|
|
855
|
+
const recent = this.diagnosticBuffer.countWithin(this.desyncWindowMs, nowMs);
|
|
856
|
+
if (recent >= this.desyncThreshold) {
|
|
857
|
+
this.desynced = true;
|
|
858
|
+
const snapshot = this.diagnosticBuffer.snapshot();
|
|
859
|
+
const err = new ProtocolDesyncError(
|
|
860
|
+
`Engine protocol desync: ${recent} unroutable response lines within ${this.desyncWindowMs}ms.`,
|
|
861
|
+
snapshot
|
|
862
|
+
);
|
|
863
|
+
this.logger.error(`[attest.protocol] ${err.message}`);
|
|
864
|
+
this.failAll(err);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
639
867
|
handleLine(line) {
|
|
868
|
+
let response;
|
|
869
|
+
try {
|
|
870
|
+
response = decodeResponse(line);
|
|
871
|
+
} catch (err) {
|
|
872
|
+
this.handleDecodeFailure(line, err);
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
640
875
|
let reqId;
|
|
641
876
|
try {
|
|
642
|
-
const response = decodeResponse(line);
|
|
643
877
|
reqId = extractId(response);
|
|
644
|
-
const pending = this.pending.get(reqId);
|
|
645
|
-
if (pending === void 0) return;
|
|
646
|
-
this.pending.delete(reqId);
|
|
647
|
-
try {
|
|
648
|
-
const result = extractResult(response);
|
|
649
|
-
pending.resolve(result);
|
|
650
|
-
} catch (err) {
|
|
651
|
-
pending.reject(err);
|
|
652
|
-
}
|
|
653
878
|
} catch (err) {
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
879
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
880
|
+
this.recordDiagnostic("missing_id", message, line);
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
const pending = this.pending.get(reqId);
|
|
884
|
+
if (pending === void 0) {
|
|
885
|
+
this.recordDiagnostic(
|
|
886
|
+
"unknown_id",
|
|
887
|
+
`no pending request for id=${reqId}`,
|
|
888
|
+
line
|
|
889
|
+
);
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
this.pending.delete(reqId);
|
|
893
|
+
try {
|
|
894
|
+
const result = extractResult(response);
|
|
895
|
+
pending.resolve(result);
|
|
896
|
+
} catch (err) {
|
|
897
|
+
pending.reject(err);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
handleDecodeFailure(line, err) {
|
|
901
|
+
if (err instanceof ProtocolError) {
|
|
902
|
+
let reqId = -1;
|
|
903
|
+
try {
|
|
904
|
+
const raw = JSON.parse(line.trim());
|
|
905
|
+
const idCandidate = raw["id"];
|
|
906
|
+
if (typeof idCandidate === "number" && Number.isFinite(idCandidate)) {
|
|
907
|
+
reqId = idCandidate;
|
|
665
908
|
}
|
|
909
|
+
} catch {
|
|
910
|
+
reqId = -1;
|
|
666
911
|
}
|
|
912
|
+
const pending = this.pending.get(reqId);
|
|
913
|
+
if (pending !== void 0) {
|
|
914
|
+
this.pending.delete(reqId);
|
|
915
|
+
pending.reject(err);
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
this.recordDiagnostic(
|
|
919
|
+
"non_routable_error",
|
|
920
|
+
`engine error code=${err.code} (${err.errorMessage}) had no matching pending id`,
|
|
921
|
+
line
|
|
922
|
+
);
|
|
923
|
+
return;
|
|
667
924
|
}
|
|
925
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
926
|
+
const kind = classifyDecodeError(message);
|
|
927
|
+
this.recordDiagnostic(kind, message, line);
|
|
668
928
|
}
|
|
669
929
|
failAll(err) {
|
|
670
930
|
for (const pending of this.pending.values()) {
|
|
@@ -673,6 +933,12 @@ var AttestClient = class {
|
|
|
673
933
|
this.pending.clear();
|
|
674
934
|
}
|
|
675
935
|
async sendRequest(method, params, timeoutMs) {
|
|
936
|
+
if (this.desynced) {
|
|
937
|
+
throw new ProtocolDesyncError(
|
|
938
|
+
"Engine protocol is desynced; AttestClient will not send further requests.",
|
|
939
|
+
this.diagnosticBuffer.snapshot()
|
|
940
|
+
);
|
|
941
|
+
}
|
|
676
942
|
const resolvedTimeout = resolveTimeoutMs(timeoutMs);
|
|
677
943
|
if (!this.readerActive) {
|
|
678
944
|
return Promise.race([
|
|
@@ -685,12 +951,12 @@ var AttestClient = class {
|
|
|
685
951
|
throw new Error("Engine process not started.");
|
|
686
952
|
}
|
|
687
953
|
let capturedReqId;
|
|
688
|
-
const requestPromise = new Promise((
|
|
954
|
+
const requestPromise = new Promise((resolve, reject) => {
|
|
689
955
|
this.writeChain = this.writeChain.then(() => {
|
|
690
956
|
this.requestId += 1;
|
|
691
957
|
const reqId = this.requestId;
|
|
692
958
|
capturedReqId = reqId;
|
|
693
|
-
this.pending.set(reqId, { resolve
|
|
959
|
+
this.pending.set(reqId, { resolve, reject });
|
|
694
960
|
const requestStr = encodeRequest(reqId, method, params);
|
|
695
961
|
cp.stdin.write(requestStr, (err) => {
|
|
696
962
|
if (err) {
|
|
@@ -710,7 +976,7 @@ var AttestClient = class {
|
|
|
710
976
|
}
|
|
711
977
|
async evaluateBatch(trace, assertions, options) {
|
|
712
978
|
if (isSimulationMode()) {
|
|
713
|
-
|
|
979
|
+
requireSimulationCapability(this.engine["initResult"]);
|
|
714
980
|
}
|
|
715
981
|
const params = {
|
|
716
982
|
trace,
|
|
@@ -749,10 +1015,10 @@ var AttestClient = class {
|
|
|
749
1015
|
if (isSimulationMode()) {
|
|
750
1016
|
return {
|
|
751
1017
|
assertion_id: assertionId2,
|
|
752
|
-
mean:
|
|
1018
|
+
mean: 0,
|
|
753
1019
|
stddev: 0,
|
|
754
1020
|
count: 0,
|
|
755
|
-
latest_score:
|
|
1021
|
+
latest_score: 0,
|
|
756
1022
|
deviation: 0,
|
|
757
1023
|
status: "no_data"
|
|
758
1024
|
};
|
|
@@ -783,6 +1049,23 @@ var AttestClient = class {
|
|
|
783
1049
|
return String(raw.message);
|
|
784
1050
|
}
|
|
785
1051
|
};
|
|
1052
|
+
function classifyDecodeError(message) {
|
|
1053
|
+
if (message.startsWith("malformed JSON")) return "malformed_json";
|
|
1054
|
+
if (message.startsWith("expected JSON object")) return "non_object_response";
|
|
1055
|
+
if (message.startsWith("invalid jsonrpc version")) return "invalid_jsonrpc_version";
|
|
1056
|
+
if (message.startsWith("empty response line")) return "malformed_json";
|
|
1057
|
+
return "malformed_json";
|
|
1058
|
+
}
|
|
1059
|
+
function requireSimulationCapability(init) {
|
|
1060
|
+
if (init === null || init === void 0) {
|
|
1061
|
+
throw new Error("simulation mode: the engine has not been initialized");
|
|
1062
|
+
}
|
|
1063
|
+
if (!(init.capabilities ?? []).includes("simulation_mode")) {
|
|
1064
|
+
throw new Error(
|
|
1065
|
+
`simulation mode is on but attest-engine ${init.engine_version} was not started in simulation mode (capability 'simulation_mode' missing), so it would call real providers. Set ATTEST_SIMULATION=1 before the engine starts (the Vitest fixtures do this for you) and use attest-engine 1.0 or later.`
|
|
1066
|
+
);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
786
1069
|
|
|
787
1070
|
// src/trace.ts
|
|
788
1071
|
var import_node_crypto = require("crypto");
|
|
@@ -873,6 +1156,20 @@ var TraceBuilder = class {
|
|
|
873
1156
|
};
|
|
874
1157
|
|
|
875
1158
|
// src/result.ts
|
|
1159
|
+
var KNOWN_STATUSES = /* @__PURE__ */ new Set([
|
|
1160
|
+
STATUS_PASS,
|
|
1161
|
+
STATUS_SOFT_FAIL,
|
|
1162
|
+
STATUS_HARD_FAIL,
|
|
1163
|
+
STATUS_NOT_EVALUATED
|
|
1164
|
+
]);
|
|
1165
|
+
function normalizeAssertionResult(r) {
|
|
1166
|
+
if (KNOWN_STATUSES.has(r.status)) return r;
|
|
1167
|
+
return {
|
|
1168
|
+
...r,
|
|
1169
|
+
status: STATUS_NOT_EVALUATED,
|
|
1170
|
+
explanation: `[unknown status '${r.status}'] ${r.explanation}`
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
876
1173
|
var AgentResult = class {
|
|
877
1174
|
trace;
|
|
878
1175
|
assertionResults;
|
|
@@ -880,15 +1177,31 @@ var AgentResult = class {
|
|
|
880
1177
|
totalDurationMs;
|
|
881
1178
|
constructor(trace, assertionResults = [], totalCost = 0, totalDurationMs = 0) {
|
|
882
1179
|
this.trace = trace;
|
|
883
|
-
this.assertionResults = assertionResults;
|
|
1180
|
+
this.assertionResults = assertionResults.map(normalizeAssertionResult);
|
|
884
1181
|
this.totalCost = totalCost;
|
|
885
1182
|
this.totalDurationMs = totalDurationMs;
|
|
886
1183
|
}
|
|
1184
|
+
/** True if every assertion has status "pass". */
|
|
887
1185
|
get passed() {
|
|
888
1186
|
return this.assertionResults.every((r) => r.status === STATUS_PASS);
|
|
889
1187
|
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Test outcome after the whole batch. Any hard_fail fails; otherwise any
|
|
1190
|
+
* not_evaluated skips (fails when `strict`); otherwise pass, including
|
|
1191
|
+
* when only soft_fail rows are present (see `softFailures`).
|
|
1192
|
+
*/
|
|
1193
|
+
outcome(options) {
|
|
1194
|
+
if (this.hardFailures.length > 0) return OUTCOME_FAIL;
|
|
1195
|
+
if (this.notEvaluated.length > 0) {
|
|
1196
|
+
return options?.strict === true ? OUTCOME_FAIL : OUTCOME_SKIP;
|
|
1197
|
+
}
|
|
1198
|
+
return OUTCOME_PASS;
|
|
1199
|
+
}
|
|
1200
|
+
/** Failed assertions: hard_fail and soft_fail only. */
|
|
890
1201
|
get failedAssertions() {
|
|
891
|
-
return this.assertionResults.filter(
|
|
1202
|
+
return this.assertionResults.filter(
|
|
1203
|
+
(r) => r.status === STATUS_HARD_FAIL || r.status === STATUS_SOFT_FAIL
|
|
1204
|
+
);
|
|
892
1205
|
}
|
|
893
1206
|
get hardFailures() {
|
|
894
1207
|
return this.assertionResults.filter((r) => r.status === STATUS_HARD_FAIL);
|
|
@@ -896,14 +1209,226 @@ var AgentResult = class {
|
|
|
896
1209
|
get softFailures() {
|
|
897
1210
|
return this.assertionResults.filter((r) => r.status === STATUS_SOFT_FAIL);
|
|
898
1211
|
}
|
|
1212
|
+
/** Assertions the engine did not evaluate; each carries a `reason`. */
|
|
1213
|
+
get notEvaluated() {
|
|
1214
|
+
return this.assertionResults.filter((r) => r.status === STATUS_NOT_EVALUATED);
|
|
1215
|
+
}
|
|
1216
|
+
/** Count of not_evaluated assertions per reason, in first-seen order. */
|
|
1217
|
+
get notEvaluatedReasons() {
|
|
1218
|
+
const counts = {};
|
|
1219
|
+
for (const r of this.notEvaluated) {
|
|
1220
|
+
const key = r.reason || "unknown";
|
|
1221
|
+
counts[key] = (counts[key] ?? 0) + 1;
|
|
1222
|
+
}
|
|
1223
|
+
return counts;
|
|
1224
|
+
}
|
|
1225
|
+
/**
|
|
1226
|
+
* Passed / evaluated assertions; null when nothing was evaluated.
|
|
1227
|
+
* not_evaluated rows are excluded from the denominator.
|
|
1228
|
+
*/
|
|
1229
|
+
get passRate() {
|
|
1230
|
+
const evaluated = this.assertionResults.length - this.notEvaluated.length;
|
|
1231
|
+
return evaluated === 0 ? null : this.passCount / evaluated;
|
|
1232
|
+
}
|
|
899
1233
|
get passCount() {
|
|
900
1234
|
return this.assertionResults.filter((r) => r.status === STATUS_PASS).length;
|
|
901
1235
|
}
|
|
1236
|
+
/** Number of failed assertions (hard_fail + soft_fail). */
|
|
902
1237
|
get failCount() {
|
|
903
|
-
return this.
|
|
1238
|
+
return this.failedAssertions.length;
|
|
1239
|
+
}
|
|
1240
|
+
/** "not evaluated: <reason>[, <reason> ×n ...]" for skipped tests. */
|
|
1241
|
+
skipReason() {
|
|
1242
|
+
const parts = Object.entries(this.notEvaluatedReasons).map(
|
|
1243
|
+
([reason, n]) => n === 1 ? reason : `${reason} \xD7${n}`
|
|
1244
|
+
);
|
|
1245
|
+
return `not evaluated: ${parts.join(", ")}`;
|
|
904
1246
|
}
|
|
905
1247
|
};
|
|
906
1248
|
|
|
1249
|
+
// src/diagnostics.ts
|
|
1250
|
+
var ANSI_RESET = "\x1B[0m";
|
|
1251
|
+
var ANSI_RED = "\x1B[31m";
|
|
1252
|
+
var ANSI_YELLOW = "\x1B[33m";
|
|
1253
|
+
var ANSI_GREEN = "\x1B[32m";
|
|
1254
|
+
var ANSI_DIM = "\x1B[2m";
|
|
1255
|
+
var ANSI_BOLD = "\x1B[1m";
|
|
1256
|
+
var LAYER_NAMES = {
|
|
1257
|
+
1: "schema",
|
|
1258
|
+
2: "constraint",
|
|
1259
|
+
3: "trace",
|
|
1260
|
+
4: "content",
|
|
1261
|
+
5: "embedding",
|
|
1262
|
+
6: "llm_judge",
|
|
1263
|
+
7: "trace_tree",
|
|
1264
|
+
8: "plugin"
|
|
1265
|
+
};
|
|
1266
|
+
function layerLabel(layer, assertionType) {
|
|
1267
|
+
if (layer === 0 && assertionType) {
|
|
1268
|
+
return `L? ${assertionType}`;
|
|
1269
|
+
}
|
|
1270
|
+
const name = LAYER_NAMES[layer] ?? assertionType ?? "uncategorized";
|
|
1271
|
+
return `L${layer} ${name}`;
|
|
1272
|
+
}
|
|
1273
|
+
function statusGlyph(status, color) {
|
|
1274
|
+
let glyph;
|
|
1275
|
+
let c;
|
|
1276
|
+
if (status === STATUS_PASS) {
|
|
1277
|
+
glyph = "PASS";
|
|
1278
|
+
c = ANSI_GREEN;
|
|
1279
|
+
} else if (status === STATUS_SOFT_FAIL) {
|
|
1280
|
+
glyph = "SOFT";
|
|
1281
|
+
c = ANSI_YELLOW;
|
|
1282
|
+
} else if (status === STATUS_HARD_FAIL) {
|
|
1283
|
+
glyph = "FAIL";
|
|
1284
|
+
c = ANSI_RED;
|
|
1285
|
+
} else if (status === STATUS_NOT_EVALUATED) {
|
|
1286
|
+
glyph = "N/E";
|
|
1287
|
+
c = ANSI_DIM;
|
|
1288
|
+
} else {
|
|
1289
|
+
glyph = status.toUpperCase() || "????";
|
|
1290
|
+
c = ANSI_DIM;
|
|
1291
|
+
}
|
|
1292
|
+
return color ? `${c}${glyph}${ANSI_RESET}` : glyph;
|
|
1293
|
+
}
|
|
1294
|
+
function bold(s, color) {
|
|
1295
|
+
return color ? `${ANSI_BOLD}${s}${ANSI_RESET}` : s;
|
|
1296
|
+
}
|
|
1297
|
+
function dim(s, color) {
|
|
1298
|
+
return color ? `${ANSI_DIM}${s}${ANSI_RESET}` : s;
|
|
1299
|
+
}
|
|
1300
|
+
function truncate(value, limit = 280) {
|
|
1301
|
+
if (value.length <= limit) {
|
|
1302
|
+
return value;
|
|
1303
|
+
}
|
|
1304
|
+
return value.slice(0, limit - 3) + "...";
|
|
1305
|
+
}
|
|
1306
|
+
function renderAssertionFailure(result, options = {}) {
|
|
1307
|
+
const color = options.color ?? false;
|
|
1308
|
+
const testFile = options.testFile;
|
|
1309
|
+
const layer = result.layer ?? 0;
|
|
1310
|
+
const label = layerLabel(layer, result.type);
|
|
1311
|
+
const lines = [];
|
|
1312
|
+
lines.push(
|
|
1313
|
+
` ${statusGlyph(result.status, color)} ${bold(result.assertion_id, color)} ${dim(`\u2014 ${label}`, color)}`
|
|
1314
|
+
);
|
|
1315
|
+
if (result.reason) {
|
|
1316
|
+
lines.push(` reason: not evaluated: ${result.reason}`);
|
|
1317
|
+
}
|
|
1318
|
+
if (result.trace_node_path) {
|
|
1319
|
+
lines.push(` trace path: ${result.trace_node_path}`);
|
|
1320
|
+
}
|
|
1321
|
+
if (result.status !== STATUS_NOT_EVALUATED) {
|
|
1322
|
+
lines.push(` score: ${result.score.toFixed(3)}`);
|
|
1323
|
+
}
|
|
1324
|
+
if (result.expected) {
|
|
1325
|
+
lines.push(` expected: ${truncate(result.expected)}`);
|
|
1326
|
+
}
|
|
1327
|
+
if (result.actual) {
|
|
1328
|
+
lines.push(` actual: ${truncate(result.actual)}`);
|
|
1329
|
+
}
|
|
1330
|
+
if (result.explanation) {
|
|
1331
|
+
lines.push(` detail: ${truncate(result.explanation)}`);
|
|
1332
|
+
}
|
|
1333
|
+
if (result.threshold_source && result.threshold_source !== "static") {
|
|
1334
|
+
lines.push(` threshold: ${result.threshold_source}`);
|
|
1335
|
+
}
|
|
1336
|
+
if (result.failure_class) {
|
|
1337
|
+
lines.push(` class: ${result.failure_class}`);
|
|
1338
|
+
}
|
|
1339
|
+
if (result.judge_metadata) {
|
|
1340
|
+
appendJudgeMetadata(lines, result.judge_metadata);
|
|
1341
|
+
}
|
|
1342
|
+
if (result.suggested_action) {
|
|
1343
|
+
lines.push(` hint: ${truncate(result.suggested_action)}`);
|
|
1344
|
+
}
|
|
1345
|
+
if ((result.cost ?? 0) > 0 || (result.duration_ms ?? 0) > 0) {
|
|
1346
|
+
const cost = result.cost ?? 0;
|
|
1347
|
+
const durMs = result.duration_ms ?? 0;
|
|
1348
|
+
lines.push(` cost/lat: $${cost.toFixed(6)} / ${durMs}ms`);
|
|
1349
|
+
}
|
|
1350
|
+
if (testFile) {
|
|
1351
|
+
lines.push(` source: ${testFile}`);
|
|
1352
|
+
}
|
|
1353
|
+
return lines.join("\n") + "\n";
|
|
1354
|
+
}
|
|
1355
|
+
function appendJudgeMetadata(lines, meta) {
|
|
1356
|
+
if (meta.model) {
|
|
1357
|
+
let rubric = meta.rubric_name ?? "default";
|
|
1358
|
+
if (meta.rubric_version) {
|
|
1359
|
+
rubric = `${rubric} @ ${meta.rubric_version}`;
|
|
1360
|
+
}
|
|
1361
|
+
lines.push(` judge: ${meta.model} / ${rubric}`);
|
|
1362
|
+
}
|
|
1363
|
+
if (meta.prompt_hash) {
|
|
1364
|
+
lines.push(` prompt: #${meta.prompt_hash}`);
|
|
1365
|
+
}
|
|
1366
|
+
const samples = meta.sample_scores ?? [];
|
|
1367
|
+
if (samples.length > 1) {
|
|
1368
|
+
const formatted = samples.map((s) => s.toFixed(2)).join(", ");
|
|
1369
|
+
const mean = (meta.score_mean ?? 0).toFixed(2);
|
|
1370
|
+
const stddev = (meta.score_stddev ?? 0).toFixed(2);
|
|
1371
|
+
const flag = meta.high_variance ? " \u26A0 HIGH VARIANCE" : "";
|
|
1372
|
+
lines.push(
|
|
1373
|
+
` samples: [${formatted}] mean=${mean} stddev=${stddev}${flag}`
|
|
1374
|
+
);
|
|
1375
|
+
}
|
|
1376
|
+
const probes = meta.bias_probes ?? [];
|
|
1377
|
+
if (probes.length > 0) {
|
|
1378
|
+
const formatted = probes.map((p) => `${p.name} \u0394${p.delta >= 0 ? "+" : ""}${p.delta.toFixed(2)}`).join(", ");
|
|
1379
|
+
lines.push(` bias: ${formatted}`);
|
|
1380
|
+
}
|
|
1381
|
+
if (meta.calibration) {
|
|
1382
|
+
const c = meta.calibration;
|
|
1383
|
+
lines.push(
|
|
1384
|
+
` calibrated: ${c.label_count} labels, agreement=${c.agreement.toFixed(2)}, \u03BA=${c.cohen_kappa.toFixed(2)}`
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
function renderSummary(result, options = {}) {
|
|
1389
|
+
const color = options.color ?? false;
|
|
1390
|
+
const passN = result.passCount;
|
|
1391
|
+
const soft = result.assertionResults.filter(
|
|
1392
|
+
(r) => r.status === STATUS_SOFT_FAIL
|
|
1393
|
+
).length;
|
|
1394
|
+
const hard = result.assertionResults.filter(
|
|
1395
|
+
(r) => r.status === STATUS_HARD_FAIL
|
|
1396
|
+
).length;
|
|
1397
|
+
const parts = [
|
|
1398
|
+
`${statusGlyph(STATUS_PASS, color)} ${passN}`,
|
|
1399
|
+
`${statusGlyph(STATUS_SOFT_FAIL, color)} ${soft}`,
|
|
1400
|
+
`${statusGlyph(STATUS_HARD_FAIL, color)} ${hard}`,
|
|
1401
|
+
`${statusGlyph(STATUS_NOT_EVALUATED, color)} ${result.notEvaluated.length}`,
|
|
1402
|
+
`cost $${result.totalCost.toFixed(6)}`,
|
|
1403
|
+
`dur ${result.totalDurationMs}ms`
|
|
1404
|
+
];
|
|
1405
|
+
return parts.join(" | ");
|
|
1406
|
+
}
|
|
1407
|
+
function renderDiagnostics(result, options = {}) {
|
|
1408
|
+
const color = options.color ?? false;
|
|
1409
|
+
const includePassing = options.includePassing ?? false;
|
|
1410
|
+
const rows = includePassing ? result.assertionResults : result.assertionResults.filter((r) => r.status !== STATUS_PASS);
|
|
1411
|
+
if (rows.length === 0) {
|
|
1412
|
+
return dim(" (no failures)\n", color);
|
|
1413
|
+
}
|
|
1414
|
+
const out = [""];
|
|
1415
|
+
out.push(bold(`Attest diagnostic \u2014 ${headerCounts(result)}:`, color));
|
|
1416
|
+
for (const r of rows) {
|
|
1417
|
+
out.push(renderAssertionFailure(r, options));
|
|
1418
|
+
}
|
|
1419
|
+
out.push(dim(` Summary: ${renderSummary(result, { color })}`, color));
|
|
1420
|
+
return out.join("\n");
|
|
1421
|
+
}
|
|
1422
|
+
function headerCounts(result) {
|
|
1423
|
+
const total = result.assertionResults.length;
|
|
1424
|
+
const notEvaluated = result.notEvaluated.length;
|
|
1425
|
+
if (result.failCount === 0 && notEvaluated > 0) {
|
|
1426
|
+
return `${notEvaluated} of ${total} assertions not evaluated`;
|
|
1427
|
+
}
|
|
1428
|
+
const text = `${result.failCount} of ${total} assertions failed`;
|
|
1429
|
+
return notEvaluated > 0 ? `${text}, ${notEvaluated} not evaluated` : text;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
907
1432
|
// src/trace-tree.ts
|
|
908
1433
|
var TraceTree = class {
|
|
909
1434
|
root;
|
|
@@ -2108,6 +2633,222 @@ var ContinuousEvalRunner = class {
|
|
|
2108
2633
|
}
|
|
2109
2634
|
};
|
|
2110
2635
|
|
|
2636
|
+
// src/calibration.ts
|
|
2637
|
+
var import_node_crypto3 = require("crypto");
|
|
2638
|
+
function computeAgreement(pairs, threshold) {
|
|
2639
|
+
if (pairs.length === 0) {
|
|
2640
|
+
throw new Error("no labeled pairs available");
|
|
2641
|
+
}
|
|
2642
|
+
if (threshold <= 0 || threshold >= 1) {
|
|
2643
|
+
throw new Error("threshold must be in (0, 1)");
|
|
2644
|
+
}
|
|
2645
|
+
const n = pairs.length;
|
|
2646
|
+
let humanPos = 0;
|
|
2647
|
+
let judgePos = 0;
|
|
2648
|
+
let bothPos = 0;
|
|
2649
|
+
let bothNeg = 0;
|
|
2650
|
+
for (const p of pairs) {
|
|
2651
|
+
const hPos = p.human >= threshold;
|
|
2652
|
+
const jPos = p.judge >= threshold;
|
|
2653
|
+
if (hPos) humanPos++;
|
|
2654
|
+
if (jPos) judgePos++;
|
|
2655
|
+
if (hPos && jPos) bothPos++;
|
|
2656
|
+
if (!hPos && !jPos) bothNeg++;
|
|
2657
|
+
}
|
|
2658
|
+
const humanNeg = n - humanPos;
|
|
2659
|
+
const judgeNeg = n - judgePos;
|
|
2660
|
+
const agreement = (bothPos + bothNeg) / n;
|
|
2661
|
+
const expected = (humanPos * judgePos + humanNeg * judgeNeg) / (n * n);
|
|
2662
|
+
const cohenKappa = expected < 1 ? (agreement - expected) / (1 - expected) : 0;
|
|
2663
|
+
const rocAuc = rocAUC(pairs, threshold);
|
|
2664
|
+
return { threshold, n, agreement, cohenKappa, rocAuc };
|
|
2665
|
+
}
|
|
2666
|
+
function rocAUC(pairs, threshold) {
|
|
2667
|
+
const rows = pairs.map((p) => ({
|
|
2668
|
+
score: p.judge,
|
|
2669
|
+
pos: p.human >= threshold
|
|
2670
|
+
}));
|
|
2671
|
+
const pos = rows.filter((r) => r.pos).length;
|
|
2672
|
+
const neg = rows.length - pos;
|
|
2673
|
+
if (pos === 0 || neg === 0) return 0;
|
|
2674
|
+
rows.sort((a, b) => a.score - b.score);
|
|
2675
|
+
let rankSumPos = 0;
|
|
2676
|
+
let i = 0;
|
|
2677
|
+
while (i < rows.length) {
|
|
2678
|
+
let j = i;
|
|
2679
|
+
while (j < rows.length && rows[j].score === rows[i].score) {
|
|
2680
|
+
j++;
|
|
2681
|
+
}
|
|
2682
|
+
const avgRank = (i + j + 1) / 2;
|
|
2683
|
+
for (let k = i; k < j; k++) {
|
|
2684
|
+
if (rows[k].pos) rankSumPos += avgRank;
|
|
2685
|
+
}
|
|
2686
|
+
i = j;
|
|
2687
|
+
}
|
|
2688
|
+
const auc = (rankSumPos - pos * (pos + 1) / 2) / (pos * neg);
|
|
2689
|
+
if (Number.isNaN(auc) || !Number.isFinite(auc)) return 0;
|
|
2690
|
+
return auc;
|
|
2691
|
+
}
|
|
2692
|
+
function loadLabelsCSV(text) {
|
|
2693
|
+
const rows = parseCSV(text);
|
|
2694
|
+
if (rows.length === 0) {
|
|
2695
|
+
throw new Error("empty CSV");
|
|
2696
|
+
}
|
|
2697
|
+
let start = 0;
|
|
2698
|
+
if (rows[0].length >= 2) {
|
|
2699
|
+
const firstHuman = parseFloatStrict(rows[0][1]);
|
|
2700
|
+
if (firstHuman === null) start = 1;
|
|
2701
|
+
}
|
|
2702
|
+
const out = [];
|
|
2703
|
+
for (let idx = start; idx < rows.length; idx++) {
|
|
2704
|
+
const row = rows[idx];
|
|
2705
|
+
const lineNum = idx + 1;
|
|
2706
|
+
if (row.length === 0) continue;
|
|
2707
|
+
if (row[0].trimStart().startsWith("#")) continue;
|
|
2708
|
+
if (row.length < 2) {
|
|
2709
|
+
throw new Error(
|
|
2710
|
+
`CSV line ${lineNum}: want at least 2 columns (input, human_label)`
|
|
2711
|
+
);
|
|
2712
|
+
}
|
|
2713
|
+
const human = parseFloatStrict(row[1]);
|
|
2714
|
+
if (human === null) {
|
|
2715
|
+
throw new Error(`CSV line ${lineNum}: human_label not a float: ${row[1]}`);
|
|
2716
|
+
}
|
|
2717
|
+
const rec = {
|
|
2718
|
+
input: row[0],
|
|
2719
|
+
humanLabel: human,
|
|
2720
|
+
judgeScore: 0,
|
|
2721
|
+
judgeKnown: false
|
|
2722
|
+
};
|
|
2723
|
+
if (row.length >= 3 && row[2].trim() !== "") {
|
|
2724
|
+
const judge = parseFloatStrict(row[2]);
|
|
2725
|
+
if (judge === null) {
|
|
2726
|
+
throw new Error(
|
|
2727
|
+
`CSV line ${lineNum}: judge_score not a float: ${row[2]}`
|
|
2728
|
+
);
|
|
2729
|
+
}
|
|
2730
|
+
rec.judgeScore = judge;
|
|
2731
|
+
rec.judgeKnown = true;
|
|
2732
|
+
}
|
|
2733
|
+
out.push(rec);
|
|
2734
|
+
}
|
|
2735
|
+
if (out.length === 0) {
|
|
2736
|
+
throw new Error("CSV contained no labeled rows");
|
|
2737
|
+
}
|
|
2738
|
+
return out;
|
|
2739
|
+
}
|
|
2740
|
+
function loadLabelsJSONL(text) {
|
|
2741
|
+
const out = [];
|
|
2742
|
+
const lines = text.split(/\r?\n/);
|
|
2743
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2744
|
+
const stripped = lines[i].trim();
|
|
2745
|
+
if (!stripped) continue;
|
|
2746
|
+
let obj;
|
|
2747
|
+
try {
|
|
2748
|
+
obj = JSON.parse(stripped);
|
|
2749
|
+
} catch (err) {
|
|
2750
|
+
throw new Error(`JSONL line ${i + 1}: ${err.message}`);
|
|
2751
|
+
}
|
|
2752
|
+
if (Object.keys(obj).length === 0) continue;
|
|
2753
|
+
if (obj["human_label"] === void 0) {
|
|
2754
|
+
throw new Error(`JSONL line ${i + 1}: missing human_label`);
|
|
2755
|
+
}
|
|
2756
|
+
const human = parseJSONNumber(obj["human_label"]);
|
|
2757
|
+
if (human === null) {
|
|
2758
|
+
throw new Error(`JSONL line ${i + 1}: human_label is not a number`);
|
|
2759
|
+
}
|
|
2760
|
+
const rec = {
|
|
2761
|
+
input: typeof obj["input"] === "string" ? obj["input"] : "",
|
|
2762
|
+
humanLabel: human,
|
|
2763
|
+
judgeScore: 0,
|
|
2764
|
+
judgeKnown: false
|
|
2765
|
+
};
|
|
2766
|
+
if (obj["judge_score"] !== void 0 && obj["judge_score"] !== null) {
|
|
2767
|
+
const judge = parseJSONNumber(obj["judge_score"]);
|
|
2768
|
+
if (judge === null) {
|
|
2769
|
+
throw new Error(`JSONL line ${i + 1}: judge_score is not a number`);
|
|
2770
|
+
}
|
|
2771
|
+
rec.judgeScore = judge;
|
|
2772
|
+
rec.judgeKnown = true;
|
|
2773
|
+
}
|
|
2774
|
+
out.push(rec);
|
|
2775
|
+
}
|
|
2776
|
+
if (out.length === 0) {
|
|
2777
|
+
throw new Error("JSONL contained no labeled rows");
|
|
2778
|
+
}
|
|
2779
|
+
return out;
|
|
2780
|
+
}
|
|
2781
|
+
function promptHash(text) {
|
|
2782
|
+
return (0, import_node_crypto3.createHash)("sha256").update(text).digest("hex").slice(0, 16);
|
|
2783
|
+
}
|
|
2784
|
+
function parseFloatStrict(raw) {
|
|
2785
|
+
const trimmed = raw.trim();
|
|
2786
|
+
if (trimmed === "") return null;
|
|
2787
|
+
const n = Number(trimmed);
|
|
2788
|
+
if (Number.isNaN(n)) return null;
|
|
2789
|
+
return n;
|
|
2790
|
+
}
|
|
2791
|
+
function parseJSONNumber(value) {
|
|
2792
|
+
if (typeof value !== "number") return null;
|
|
2793
|
+
if (Number.isNaN(value)) return null;
|
|
2794
|
+
return value;
|
|
2795
|
+
}
|
|
2796
|
+
function parseCSV(text) {
|
|
2797
|
+
const rows = [];
|
|
2798
|
+
let current = [];
|
|
2799
|
+
let field = "";
|
|
2800
|
+
let inQuotes = false;
|
|
2801
|
+
let i = 0;
|
|
2802
|
+
while (i < text.length) {
|
|
2803
|
+
const ch = text[i];
|
|
2804
|
+
if (inQuotes) {
|
|
2805
|
+
if (ch === '"') {
|
|
2806
|
+
if (text[i + 1] === '"') {
|
|
2807
|
+
field += '"';
|
|
2808
|
+
i += 2;
|
|
2809
|
+
continue;
|
|
2810
|
+
}
|
|
2811
|
+
inQuotes = false;
|
|
2812
|
+
i++;
|
|
2813
|
+
continue;
|
|
2814
|
+
}
|
|
2815
|
+
field += ch;
|
|
2816
|
+
i++;
|
|
2817
|
+
continue;
|
|
2818
|
+
}
|
|
2819
|
+
if (ch === '"') {
|
|
2820
|
+
inQuotes = true;
|
|
2821
|
+
i++;
|
|
2822
|
+
continue;
|
|
2823
|
+
}
|
|
2824
|
+
if (ch === ",") {
|
|
2825
|
+
current.push(field);
|
|
2826
|
+
field = "";
|
|
2827
|
+
i++;
|
|
2828
|
+
continue;
|
|
2829
|
+
}
|
|
2830
|
+
if (ch === "\n") {
|
|
2831
|
+
current.push(field);
|
|
2832
|
+
rows.push(current);
|
|
2833
|
+
current = [];
|
|
2834
|
+
field = "";
|
|
2835
|
+
i++;
|
|
2836
|
+
continue;
|
|
2837
|
+
}
|
|
2838
|
+
if (ch === "\r") {
|
|
2839
|
+
i++;
|
|
2840
|
+
continue;
|
|
2841
|
+
}
|
|
2842
|
+
field += ch;
|
|
2843
|
+
i++;
|
|
2844
|
+
}
|
|
2845
|
+
if (field.length > 0 || current.length > 0) {
|
|
2846
|
+
current.push(field);
|
|
2847
|
+
rows.push(current);
|
|
2848
|
+
}
|
|
2849
|
+
return rows;
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2111
2852
|
// src/personas.ts
|
|
2112
2853
|
var FRIENDLY_USER = {
|
|
2113
2854
|
name: "FriendlyUser",
|
|
@@ -2543,15 +3284,27 @@ var LangChainAdapter = class {
|
|
|
2543
3284
|
EngineTimeoutError,
|
|
2544
3285
|
ExpectChain,
|
|
2545
3286
|
FRIENDLY_USER,
|
|
3287
|
+
FailureClass,
|
|
2546
3288
|
GeminiAdapter,
|
|
2547
3289
|
LangChainAdapter,
|
|
2548
3290
|
ManualAdapter,
|
|
2549
3291
|
OTelAdapter,
|
|
3292
|
+
OUTCOME_FAIL,
|
|
3293
|
+
OUTCOME_PASS,
|
|
3294
|
+
OUTCOME_SKIP,
|
|
2550
3295
|
OllamaAdapter,
|
|
2551
3296
|
OpenAIAdapter,
|
|
2552
3297
|
PluginRegistry,
|
|
3298
|
+
ProtocolDesyncError,
|
|
3299
|
+
ProtocolDiagnosticBuffer,
|
|
2553
3300
|
ProtocolError,
|
|
3301
|
+
REASON_MISSING_EVIDENCE,
|
|
3302
|
+
REASON_PROVIDER_NOT_CONFIGURED,
|
|
3303
|
+
REASON_PROVIDER_UNAVAILABLE,
|
|
3304
|
+
REASON_SHORT_CIRCUITED,
|
|
3305
|
+
REASON_SIMULATION,
|
|
2554
3306
|
STATUS_HARD_FAIL,
|
|
3307
|
+
STATUS_NOT_EVALUATED,
|
|
2555
3308
|
STATUS_PASS,
|
|
2556
3309
|
STATUS_SOFT_FAIL,
|
|
2557
3310
|
STEP_AGENT_CALL,
|
|
@@ -2578,6 +3331,7 @@ var LangChainAdapter = class {
|
|
|
2578
3331
|
agentId,
|
|
2579
3332
|
assertionId,
|
|
2580
3333
|
attestExpect,
|
|
3334
|
+
computeAgreement,
|
|
2581
3335
|
config,
|
|
2582
3336
|
decodeResponse,
|
|
2583
3337
|
delegate,
|
|
@@ -2587,8 +3341,17 @@ var LangChainAdapter = class {
|
|
|
2587
3341
|
extractResult,
|
|
2588
3342
|
getSampleRate,
|
|
2589
3343
|
isSimulationMode,
|
|
3344
|
+
layerForType,
|
|
3345
|
+
loadLabelsCSV,
|
|
3346
|
+
loadLabelsJSONL,
|
|
3347
|
+
previewLine,
|
|
3348
|
+
promptHash,
|
|
3349
|
+
renderAssertionFailure,
|
|
3350
|
+
renderDiagnostics,
|
|
3351
|
+
renderSummary,
|
|
3352
|
+
requireSimulationCapability,
|
|
2590
3353
|
resetConfig,
|
|
2591
|
-
|
|
3354
|
+
simulationEnvEnabled,
|
|
2592
3355
|
tier,
|
|
2593
3356
|
traceId
|
|
2594
3357
|
});
|