@attest-ai/core 0.6.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/esm/index.js CHANGED
@@ -31,6 +31,27 @@ function assertionId(id) {
31
31
  function agentId(id) {
32
32
  return id;
33
33
  }
34
+ function layerForType(assertionType) {
35
+ switch (assertionType) {
36
+ case "schema":
37
+ return 1;
38
+ case "constraint":
39
+ return 2;
40
+ case "trace":
41
+ case "trace_tree":
42
+ return 3;
43
+ case "content":
44
+ return 4;
45
+ case "embedding":
46
+ return 5;
47
+ case "llm_judge":
48
+ return 6;
49
+ case "plugin":
50
+ return 8;
51
+ default:
52
+ return 0;
53
+ }
54
+ }
34
55
 
35
56
  // src/proto/errors.ts
36
57
  var ProtocolError = class extends Error {
@@ -57,6 +78,58 @@ var EngineTimeoutError = class extends Error {
57
78
  this.timeoutMs = timeoutMs;
58
79
  }
59
80
  };
81
+ var ProtocolDesyncError = class extends Error {
82
+ diagnostics;
83
+ constructor(message, diagnostics) {
84
+ super(message);
85
+ this.name = "ProtocolDesyncError";
86
+ this.diagnostics = diagnostics;
87
+ }
88
+ };
89
+
90
+ // src/proto/diagnostics.ts
91
+ var DEFAULT_BUFFER_SIZE = 32;
92
+ var MAX_LINE_PREVIEW = 512;
93
+ var ProtocolDiagnosticBuffer = class {
94
+ capacity;
95
+ entries = [];
96
+ constructor(capacity = DEFAULT_BUFFER_SIZE) {
97
+ if (capacity <= 0) {
98
+ throw new Error(
99
+ `ProtocolDiagnosticBuffer capacity must be positive, got ${capacity}`
100
+ );
101
+ }
102
+ this.capacity = capacity;
103
+ }
104
+ push(diagnostic) {
105
+ if (this.entries.length >= this.capacity) {
106
+ this.entries.shift();
107
+ }
108
+ this.entries.push(diagnostic);
109
+ }
110
+ snapshot() {
111
+ return this.entries.slice();
112
+ }
113
+ countWithin(windowMs, nowMs) {
114
+ const cutoff = nowMs - windowMs;
115
+ let count = 0;
116
+ for (let i = this.entries.length - 1; i >= 0; i -= 1) {
117
+ if (this.entries[i].timestampMs >= cutoff) {
118
+ count += 1;
119
+ } else {
120
+ break;
121
+ }
122
+ }
123
+ return count;
124
+ }
125
+ clear() {
126
+ this.entries.length = 0;
127
+ }
128
+ };
129
+ function previewLine(line) {
130
+ if (line.length <= MAX_LINE_PREVIEW) return line;
131
+ return line.slice(0, MAX_LINE_PREVIEW) + "\u2026";
132
+ }
60
133
 
61
134
  // src/proto/codec.ts
62
135
  function encodeRequest(id, method, params) {
@@ -119,8 +192,8 @@ function extractId(response) {
119
192
  }
120
193
 
121
194
  // src/version.ts
122
- var VERSION = "0.6.0";
123
- var ENGINE_VERSION = "0.6.0";
195
+ var VERSION = "0.9.0";
196
+ var ENGINE_VERSION = "0.9.0";
124
197
 
125
198
  // src/engine-manager.ts
126
199
  import { spawn, execSync } from "child_process";
@@ -472,12 +545,16 @@ function simulationEvaluateBatch(assertions) {
472
545
  return {
473
546
  results,
474
547
  total_cost: 0,
475
- total_duration_ms: 0
548
+ total_duration_ms: 0,
549
+ simulated: true
476
550
  };
477
551
  }
478
552
 
479
553
  // src/client.ts
480
554
  var DEFAULT_TIMEOUT_MS = 3e4;
555
+ var DEFAULT_DIAGNOSTIC_BUFFER = 32;
556
+ var DEFAULT_DESYNC_THRESHOLD = 3;
557
+ var DEFAULT_DESYNC_WINDOW_MS = 1e3;
481
558
  function resolveTimeoutMs(override) {
482
559
  if (override !== void 0) return override;
483
560
  const env = process.env["ATTEST_ENGINE_TIMEOUT"];
@@ -499,6 +576,24 @@ function makeTimeoutPromise(method, timeoutMs) {
499
576
  }, timeoutMs);
500
577
  });
501
578
  }
579
+ function debugProtocolEnabled() {
580
+ const flag = process.env["ATTEST_DEBUG_PROTOCOL"];
581
+ return flag === "1" || flag === "true" || flag === "yes";
582
+ }
583
+ function envLoggerDefault() {
584
+ if (debugProtocolEnabled()) {
585
+ return {
586
+ warn: (msg, ...args) => console.warn(msg, ...args),
587
+ error: (msg, ...args) => console.error(msg, ...args)
588
+ };
589
+ }
590
+ return {
591
+ warn: () => {
592
+ },
593
+ error: () => {
594
+ }
595
+ };
596
+ }
502
597
  var AttestClient = class {
503
598
  engine;
504
599
  requestId = 0;
@@ -506,8 +601,34 @@ var AttestClient = class {
506
601
  writeChain = Promise.resolve();
507
602
  readerActive = false;
508
603
  lineHandler = null;
509
- constructor(engine) {
604
+ logger;
605
+ diagnosticBuffer;
606
+ diagnosticListeners = /* @__PURE__ */ new Set();
607
+ desyncThreshold;
608
+ desyncWindowMs;
609
+ desynced = false;
610
+ constructor(engine, options) {
510
611
  this.engine = engine;
612
+ this.logger = options?.logger ?? envLoggerDefault();
613
+ this.diagnosticBuffer = new ProtocolDiagnosticBuffer(
614
+ options?.diagnosticBufferSize ?? DEFAULT_DIAGNOSTIC_BUFFER
615
+ );
616
+ this.desyncThreshold = options?.desyncThreshold ?? DEFAULT_DESYNC_THRESHOLD;
617
+ this.desyncWindowMs = options?.desyncWindowMs ?? DEFAULT_DESYNC_WINDOW_MS;
618
+ if (options?.onDiagnostic !== void 0) {
619
+ this.diagnosticListeners.add(options.onDiagnostic);
620
+ }
621
+ }
622
+ /** Subscribe to protocol diagnostics. Returns an unsubscribe function. */
623
+ onProtocolDiagnostic(listener) {
624
+ this.diagnosticListeners.add(listener);
625
+ return () => {
626
+ this.diagnosticListeners.delete(listener);
627
+ };
628
+ }
629
+ /** Snapshot of the diagnostic ring buffer (most recent last). */
630
+ protocolDiagnostics() {
631
+ return this.diagnosticBuffer.snapshot();
511
632
  }
512
633
  startReader() {
513
634
  if (this.readerActive) return;
@@ -532,35 +653,101 @@ var AttestClient = class {
532
653
  this.lineHandler = null;
533
654
  this.readerActive = false;
534
655
  }
656
+ recordDiagnostic(kind, message, rawLine) {
657
+ const diagnostic = {
658
+ kind,
659
+ message,
660
+ rawLine: previewLine(rawLine),
661
+ timestampMs: Date.now()
662
+ };
663
+ this.diagnosticBuffer.push(diagnostic);
664
+ this.logger.warn(`[attest.protocol] ${kind}: ${message}`);
665
+ for (const listener of this.diagnosticListeners) {
666
+ try {
667
+ listener(diagnostic);
668
+ } catch (err) {
669
+ this.logger.error(
670
+ `[attest.protocol] diagnostic listener threw: ${err instanceof Error ? err.message : String(err)}`
671
+ );
672
+ }
673
+ }
674
+ this.maybeTriggerDesync(diagnostic.timestampMs);
675
+ }
676
+ maybeTriggerDesync(nowMs) {
677
+ if (this.desynced) return;
678
+ const recent = this.diagnosticBuffer.countWithin(this.desyncWindowMs, nowMs);
679
+ if (recent >= this.desyncThreshold) {
680
+ this.desynced = true;
681
+ const snapshot = this.diagnosticBuffer.snapshot();
682
+ const err = new ProtocolDesyncError(
683
+ `Engine protocol desync: ${recent} unroutable response lines within ${this.desyncWindowMs}ms.`,
684
+ snapshot
685
+ );
686
+ this.logger.error(`[attest.protocol] ${err.message}`);
687
+ this.failAll(err);
688
+ }
689
+ }
535
690
  handleLine(line) {
691
+ let response;
692
+ try {
693
+ response = decodeResponse(line);
694
+ } catch (err) {
695
+ this.handleDecodeFailure(line, err);
696
+ return;
697
+ }
536
698
  let reqId;
537
699
  try {
538
- const response = decodeResponse(line);
539
700
  reqId = extractId(response);
540
- const pending = this.pending.get(reqId);
541
- if (pending === void 0) return;
542
- this.pending.delete(reqId);
543
- try {
544
- const result = extractResult(response);
545
- pending.resolve(result);
546
- } catch (err) {
547
- pending.reject(err);
548
- }
549
701
  } catch (err) {
550
- if (err instanceof ProtocolError) {
551
- try {
552
- const raw = JSON.parse(line.trim());
553
- reqId = Number(raw.id ?? -1);
554
- } catch {
555
- reqId = -1;
556
- }
557
- const pending = this.pending.get(reqId);
558
- if (pending !== void 0) {
559
- this.pending.delete(reqId);
560
- pending.reject(err);
702
+ const message = err instanceof Error ? err.message : String(err);
703
+ this.recordDiagnostic("missing_id", message, line);
704
+ return;
705
+ }
706
+ const pending = this.pending.get(reqId);
707
+ if (pending === void 0) {
708
+ this.recordDiagnostic(
709
+ "unknown_id",
710
+ `no pending request for id=${reqId}`,
711
+ line
712
+ );
713
+ return;
714
+ }
715
+ this.pending.delete(reqId);
716
+ try {
717
+ const result = extractResult(response);
718
+ pending.resolve(result);
719
+ } catch (err) {
720
+ pending.reject(err);
721
+ }
722
+ }
723
+ handleDecodeFailure(line, err) {
724
+ if (err instanceof ProtocolError) {
725
+ let reqId = -1;
726
+ try {
727
+ const raw = JSON.parse(line.trim());
728
+ const idCandidate = raw["id"];
729
+ if (typeof idCandidate === "number" && Number.isFinite(idCandidate)) {
730
+ reqId = idCandidate;
561
731
  }
732
+ } catch {
733
+ reqId = -1;
734
+ }
735
+ const pending = this.pending.get(reqId);
736
+ if (pending !== void 0) {
737
+ this.pending.delete(reqId);
738
+ pending.reject(err);
739
+ return;
562
740
  }
741
+ this.recordDiagnostic(
742
+ "non_routable_error",
743
+ `engine error code=${err.code} (${err.errorMessage}) had no matching pending id`,
744
+ line
745
+ );
746
+ return;
563
747
  }
748
+ const message = err instanceof Error ? err.message : String(err);
749
+ const kind = classifyDecodeError(message);
750
+ this.recordDiagnostic(kind, message, line);
564
751
  }
565
752
  failAll(err) {
566
753
  for (const pending of this.pending.values()) {
@@ -569,6 +756,12 @@ var AttestClient = class {
569
756
  this.pending.clear();
570
757
  }
571
758
  async sendRequest(method, params, timeoutMs) {
759
+ if (this.desynced) {
760
+ throw new ProtocolDesyncError(
761
+ "Engine protocol is desynced; AttestClient will not send further requests.",
762
+ this.diagnosticBuffer.snapshot()
763
+ );
764
+ }
572
765
  const resolvedTimeout = resolveTimeoutMs(timeoutMs);
573
766
  if (!this.readerActive) {
574
767
  return Promise.race([
@@ -679,6 +872,13 @@ var AttestClient = class {
679
872
  return String(raw.message);
680
873
  }
681
874
  };
875
+ function classifyDecodeError(message) {
876
+ if (message.startsWith("malformed JSON")) return "malformed_json";
877
+ if (message.startsWith("expected JSON object")) return "non_object_response";
878
+ if (message.startsWith("invalid jsonrpc version")) return "invalid_jsonrpc_version";
879
+ if (message.startsWith("empty response line")) return "malformed_json";
880
+ return "malformed_json";
881
+ }
682
882
 
683
883
  // src/trace.ts
684
884
  import { randomUUID } from "crypto";
@@ -800,6 +1000,173 @@ var AgentResult = class {
800
1000
  }
801
1001
  };
802
1002
 
1003
+ // src/diagnostics.ts
1004
+ var ANSI_RESET = "\x1B[0m";
1005
+ var ANSI_RED = "\x1B[31m";
1006
+ var ANSI_YELLOW = "\x1B[33m";
1007
+ var ANSI_GREEN = "\x1B[32m";
1008
+ var ANSI_DIM = "\x1B[2m";
1009
+ var ANSI_BOLD = "\x1B[1m";
1010
+ var LAYER_NAMES = {
1011
+ 1: "schema",
1012
+ 2: "constraint",
1013
+ 3: "trace",
1014
+ 4: "content",
1015
+ 5: "embedding",
1016
+ 6: "llm_judge",
1017
+ 7: "trace_tree",
1018
+ 8: "plugin"
1019
+ };
1020
+ function layerLabel(layer, assertionType) {
1021
+ if (layer === 0 && assertionType) {
1022
+ return `L? ${assertionType}`;
1023
+ }
1024
+ const name = LAYER_NAMES[layer] ?? assertionType ?? "uncategorized";
1025
+ return `L${layer} ${name}`;
1026
+ }
1027
+ function statusGlyph(status, color) {
1028
+ let glyph;
1029
+ let c;
1030
+ if (status === STATUS_PASS) {
1031
+ glyph = "PASS";
1032
+ c = ANSI_GREEN;
1033
+ } else if (status === STATUS_SOFT_FAIL) {
1034
+ glyph = "SOFT";
1035
+ c = ANSI_YELLOW;
1036
+ } else if (status === STATUS_HARD_FAIL) {
1037
+ glyph = "FAIL";
1038
+ c = ANSI_RED;
1039
+ } else {
1040
+ glyph = status.toUpperCase() || "????";
1041
+ c = ANSI_DIM;
1042
+ }
1043
+ return color ? `${c}${glyph}${ANSI_RESET}` : glyph;
1044
+ }
1045
+ function bold(s, color) {
1046
+ return color ? `${ANSI_BOLD}${s}${ANSI_RESET}` : s;
1047
+ }
1048
+ function dim(s, color) {
1049
+ return color ? `${ANSI_DIM}${s}${ANSI_RESET}` : s;
1050
+ }
1051
+ function truncate(value, limit = 280) {
1052
+ if (value.length <= limit) {
1053
+ return value;
1054
+ }
1055
+ return value.slice(0, limit - 3) + "...";
1056
+ }
1057
+ function renderAssertionFailure(result, options = {}) {
1058
+ const color = options.color ?? false;
1059
+ const testFile = options.testFile;
1060
+ const layer = result.layer ?? 0;
1061
+ const label = layerLabel(layer, result.type);
1062
+ const lines = [];
1063
+ lines.push(
1064
+ ` ${statusGlyph(result.status, color)} ${bold(result.assertion_id, color)} ${dim(`\u2014 ${label}`, color)}`
1065
+ );
1066
+ if (result.trace_node_path) {
1067
+ lines.push(` trace path: ${result.trace_node_path}`);
1068
+ }
1069
+ lines.push(` score: ${result.score.toFixed(3)}`);
1070
+ if (result.expected) {
1071
+ lines.push(` expected: ${truncate(result.expected)}`);
1072
+ }
1073
+ if (result.actual) {
1074
+ lines.push(` actual: ${truncate(result.actual)}`);
1075
+ }
1076
+ if (result.explanation) {
1077
+ lines.push(` detail: ${truncate(result.explanation)}`);
1078
+ }
1079
+ if (result.threshold_source && result.threshold_source !== "static") {
1080
+ lines.push(` threshold: ${result.threshold_source}`);
1081
+ }
1082
+ if (result.judge_metadata) {
1083
+ appendJudgeMetadata(lines, result.judge_metadata);
1084
+ }
1085
+ if (result.suggested_action) {
1086
+ lines.push(` hint: ${truncate(result.suggested_action)}`);
1087
+ }
1088
+ if ((result.cost ?? 0) > 0 || (result.duration_ms ?? 0) > 0) {
1089
+ const cost = result.cost ?? 0;
1090
+ const durMs = result.duration_ms ?? 0;
1091
+ lines.push(` cost/lat: $${cost.toFixed(6)} / ${durMs}ms`);
1092
+ }
1093
+ if (testFile) {
1094
+ lines.push(` source: ${testFile}`);
1095
+ }
1096
+ return lines.join("\n") + "\n";
1097
+ }
1098
+ function appendJudgeMetadata(lines, meta) {
1099
+ if (meta.model) {
1100
+ let rubric = meta.rubric_name ?? "default";
1101
+ if (meta.rubric_version) {
1102
+ rubric = `${rubric} @ ${meta.rubric_version}`;
1103
+ }
1104
+ lines.push(` judge: ${meta.model} / ${rubric}`);
1105
+ }
1106
+ if (meta.prompt_hash) {
1107
+ lines.push(` prompt: #${meta.prompt_hash}`);
1108
+ }
1109
+ const samples = meta.sample_scores ?? [];
1110
+ if (samples.length > 1) {
1111
+ const formatted = samples.map((s) => s.toFixed(2)).join(", ");
1112
+ const mean = (meta.score_mean ?? 0).toFixed(2);
1113
+ const stddev = (meta.score_stddev ?? 0).toFixed(2);
1114
+ const flag = meta.high_variance ? " \u26A0 HIGH VARIANCE" : "";
1115
+ lines.push(
1116
+ ` samples: [${formatted}] mean=${mean} stddev=${stddev}${flag}`
1117
+ );
1118
+ }
1119
+ const probes = meta.bias_probes ?? [];
1120
+ if (probes.length > 0) {
1121
+ const formatted = probes.map((p) => `${p.name} \u0394${p.delta >= 0 ? "+" : ""}${p.delta.toFixed(2)}`).join(", ");
1122
+ lines.push(` bias: ${formatted}`);
1123
+ }
1124
+ if (meta.calibration) {
1125
+ const c = meta.calibration;
1126
+ lines.push(
1127
+ ` calibrated: ${c.label_count} labels, agreement=${c.agreement.toFixed(2)}, \u03BA=${c.cohen_kappa.toFixed(2)}`
1128
+ );
1129
+ }
1130
+ }
1131
+ function renderSummary(result, options = {}) {
1132
+ const color = options.color ?? false;
1133
+ const passN = result.passCount;
1134
+ const soft = result.assertionResults.filter(
1135
+ (r) => r.status === STATUS_SOFT_FAIL
1136
+ ).length;
1137
+ const hard = result.assertionResults.filter(
1138
+ (r) => r.status === STATUS_HARD_FAIL
1139
+ ).length;
1140
+ const parts = [
1141
+ `${statusGlyph(STATUS_PASS, color)} ${passN}`,
1142
+ `${statusGlyph(STATUS_SOFT_FAIL, color)} ${soft}`,
1143
+ `${statusGlyph(STATUS_HARD_FAIL, color)} ${hard}`,
1144
+ `cost $${result.totalCost.toFixed(6)}`,
1145
+ `dur ${result.totalDurationMs}ms`
1146
+ ];
1147
+ return parts.join(" | ");
1148
+ }
1149
+ function renderDiagnostics(result, options = {}) {
1150
+ const color = options.color ?? false;
1151
+ const includePassing = options.includePassing ?? false;
1152
+ const rows = includePassing ? result.assertionResults : result.failedAssertions;
1153
+ if (rows.length === 0) {
1154
+ return dim(" (no failures)\n", color);
1155
+ }
1156
+ const out = [""];
1157
+ out.push(
1158
+ bold(
1159
+ `Attest diagnostic \u2014 ${rows.length} of ${result.assertionResults.length} assertions failed:`,
1160
+ color
1161
+ )
1162
+ );
1163
+ for (const r of rows) {
1164
+ out.push(renderAssertionFailure(r, options));
1165
+ }
1166
+ out.push(dim(` Summary: ${renderSummary(result, { color })}`, color));
1167
+ return out.join("\n");
1168
+ }
1169
+
803
1170
  // src/trace-tree.ts
804
1171
  var TraceTree = class {
805
1172
  root;
@@ -2004,6 +2371,222 @@ var ContinuousEvalRunner = class {
2004
2371
  }
2005
2372
  };
2006
2373
 
2374
+ // src/calibration.ts
2375
+ import { createHash as createHash2 } from "crypto";
2376
+ function computeAgreement(pairs, threshold) {
2377
+ if (pairs.length === 0) {
2378
+ throw new Error("no labeled pairs available");
2379
+ }
2380
+ if (threshold <= 0 || threshold >= 1) {
2381
+ throw new Error("threshold must be in (0, 1)");
2382
+ }
2383
+ const n = pairs.length;
2384
+ let humanPos = 0;
2385
+ let judgePos = 0;
2386
+ let bothPos = 0;
2387
+ let bothNeg = 0;
2388
+ for (const p of pairs) {
2389
+ const hPos = p.human >= threshold;
2390
+ const jPos = p.judge >= threshold;
2391
+ if (hPos) humanPos++;
2392
+ if (jPos) judgePos++;
2393
+ if (hPos && jPos) bothPos++;
2394
+ if (!hPos && !jPos) bothNeg++;
2395
+ }
2396
+ const humanNeg = n - humanPos;
2397
+ const judgeNeg = n - judgePos;
2398
+ const agreement = (bothPos + bothNeg) / n;
2399
+ const expected = (humanPos * judgePos + humanNeg * judgeNeg) / (n * n);
2400
+ const cohenKappa = expected < 1 ? (agreement - expected) / (1 - expected) : 0;
2401
+ const rocAuc = rocAUC(pairs, threshold);
2402
+ return { threshold, n, agreement, cohenKappa, rocAuc };
2403
+ }
2404
+ function rocAUC(pairs, threshold) {
2405
+ const rows = pairs.map((p) => ({
2406
+ score: p.judge,
2407
+ pos: p.human >= threshold
2408
+ }));
2409
+ const pos = rows.filter((r) => r.pos).length;
2410
+ const neg = rows.length - pos;
2411
+ if (pos === 0 || neg === 0) return 0;
2412
+ rows.sort((a, b) => a.score - b.score);
2413
+ let rankSumPos = 0;
2414
+ let i = 0;
2415
+ while (i < rows.length) {
2416
+ let j = i;
2417
+ while (j < rows.length && rows[j].score === rows[i].score) {
2418
+ j++;
2419
+ }
2420
+ const avgRank = (i + j + 1) / 2;
2421
+ for (let k = i; k < j; k++) {
2422
+ if (rows[k].pos) rankSumPos += avgRank;
2423
+ }
2424
+ i = j;
2425
+ }
2426
+ const auc = (rankSumPos - pos * (pos + 1) / 2) / (pos * neg);
2427
+ if (Number.isNaN(auc) || !Number.isFinite(auc)) return 0;
2428
+ return auc;
2429
+ }
2430
+ function loadLabelsCSV(text) {
2431
+ const rows = parseCSV(text);
2432
+ if (rows.length === 0) {
2433
+ throw new Error("empty CSV");
2434
+ }
2435
+ let start = 0;
2436
+ if (rows[0].length >= 2) {
2437
+ const firstHuman = parseFloatStrict(rows[0][1]);
2438
+ if (firstHuman === null) start = 1;
2439
+ }
2440
+ const out = [];
2441
+ for (let idx = start; idx < rows.length; idx++) {
2442
+ const row = rows[idx];
2443
+ const lineNum = idx + 1;
2444
+ if (row.length === 0) continue;
2445
+ if (row[0].trimStart().startsWith("#")) continue;
2446
+ if (row.length < 2) {
2447
+ throw new Error(
2448
+ `CSV line ${lineNum}: want at least 2 columns (input, human_label)`
2449
+ );
2450
+ }
2451
+ const human = parseFloatStrict(row[1]);
2452
+ if (human === null) {
2453
+ throw new Error(`CSV line ${lineNum}: human_label not a float: ${row[1]}`);
2454
+ }
2455
+ const rec = {
2456
+ input: row[0],
2457
+ humanLabel: human,
2458
+ judgeScore: 0,
2459
+ judgeKnown: false
2460
+ };
2461
+ if (row.length >= 3 && row[2].trim() !== "") {
2462
+ const judge = parseFloatStrict(row[2]);
2463
+ if (judge === null) {
2464
+ throw new Error(
2465
+ `CSV line ${lineNum}: judge_score not a float: ${row[2]}`
2466
+ );
2467
+ }
2468
+ rec.judgeScore = judge;
2469
+ rec.judgeKnown = true;
2470
+ }
2471
+ out.push(rec);
2472
+ }
2473
+ if (out.length === 0) {
2474
+ throw new Error("CSV contained no labeled rows");
2475
+ }
2476
+ return out;
2477
+ }
2478
+ function loadLabelsJSONL(text) {
2479
+ const out = [];
2480
+ const lines = text.split(/\r?\n/);
2481
+ for (let i = 0; i < lines.length; i++) {
2482
+ const stripped = lines[i].trim();
2483
+ if (!stripped) continue;
2484
+ let obj;
2485
+ try {
2486
+ obj = JSON.parse(stripped);
2487
+ } catch (err) {
2488
+ throw new Error(`JSONL line ${i + 1}: ${err.message}`);
2489
+ }
2490
+ if (Object.keys(obj).length === 0) continue;
2491
+ if (obj["human_label"] === void 0) {
2492
+ throw new Error(`JSONL line ${i + 1}: missing human_label`);
2493
+ }
2494
+ const human = parseJSONNumber(obj["human_label"]);
2495
+ if (human === null) {
2496
+ throw new Error(`JSONL line ${i + 1}: human_label is not a number`);
2497
+ }
2498
+ const rec = {
2499
+ input: typeof obj["input"] === "string" ? obj["input"] : "",
2500
+ humanLabel: human,
2501
+ judgeScore: 0,
2502
+ judgeKnown: false
2503
+ };
2504
+ if (obj["judge_score"] !== void 0 && obj["judge_score"] !== null) {
2505
+ const judge = parseJSONNumber(obj["judge_score"]);
2506
+ if (judge === null) {
2507
+ throw new Error(`JSONL line ${i + 1}: judge_score is not a number`);
2508
+ }
2509
+ rec.judgeScore = judge;
2510
+ rec.judgeKnown = true;
2511
+ }
2512
+ out.push(rec);
2513
+ }
2514
+ if (out.length === 0) {
2515
+ throw new Error("JSONL contained no labeled rows");
2516
+ }
2517
+ return out;
2518
+ }
2519
+ function promptHash(text) {
2520
+ return createHash2("sha256").update(text).digest("hex").slice(0, 16);
2521
+ }
2522
+ function parseFloatStrict(raw) {
2523
+ const trimmed = raw.trim();
2524
+ if (trimmed === "") return null;
2525
+ const n = Number(trimmed);
2526
+ if (Number.isNaN(n)) return null;
2527
+ return n;
2528
+ }
2529
+ function parseJSONNumber(value) {
2530
+ if (typeof value !== "number") return null;
2531
+ if (Number.isNaN(value)) return null;
2532
+ return value;
2533
+ }
2534
+ function parseCSV(text) {
2535
+ const rows = [];
2536
+ let current = [];
2537
+ let field = "";
2538
+ let inQuotes = false;
2539
+ let i = 0;
2540
+ while (i < text.length) {
2541
+ const ch = text[i];
2542
+ if (inQuotes) {
2543
+ if (ch === '"') {
2544
+ if (text[i + 1] === '"') {
2545
+ field += '"';
2546
+ i += 2;
2547
+ continue;
2548
+ }
2549
+ inQuotes = false;
2550
+ i++;
2551
+ continue;
2552
+ }
2553
+ field += ch;
2554
+ i++;
2555
+ continue;
2556
+ }
2557
+ if (ch === '"') {
2558
+ inQuotes = true;
2559
+ i++;
2560
+ continue;
2561
+ }
2562
+ if (ch === ",") {
2563
+ current.push(field);
2564
+ field = "";
2565
+ i++;
2566
+ continue;
2567
+ }
2568
+ if (ch === "\n") {
2569
+ current.push(field);
2570
+ rows.push(current);
2571
+ current = [];
2572
+ field = "";
2573
+ i++;
2574
+ continue;
2575
+ }
2576
+ if (ch === "\r") {
2577
+ i++;
2578
+ continue;
2579
+ }
2580
+ field += ch;
2581
+ i++;
2582
+ }
2583
+ if (field.length > 0 || current.length > 0) {
2584
+ current.push(field);
2585
+ rows.push(current);
2586
+ }
2587
+ return rows;
2588
+ }
2589
+
2007
2590
  // src/personas.ts
2008
2591
  var FRIENDLY_USER = {
2009
2592
  name: "FriendlyUser",
@@ -2445,6 +3028,8 @@ export {
2445
3028
  OllamaAdapter,
2446
3029
  OpenAIAdapter,
2447
3030
  PluginRegistry,
3031
+ ProtocolDesyncError,
3032
+ ProtocolDiagnosticBuffer,
2448
3033
  ProtocolError,
2449
3034
  STATUS_HARD_FAIL,
2450
3035
  STATUS_PASS,
@@ -2473,6 +3058,7 @@ export {
2473
3058
  agentId,
2474
3059
  assertionId,
2475
3060
  attestExpect,
3061
+ computeAgreement,
2476
3062
  config,
2477
3063
  decodeResponse,
2478
3064
  delegate,
@@ -2482,6 +3068,14 @@ export {
2482
3068
  extractResult,
2483
3069
  getSampleRate,
2484
3070
  isSimulationMode,
3071
+ layerForType,
3072
+ loadLabelsCSV,
3073
+ loadLabelsJSONL,
3074
+ previewLine,
3075
+ promptHash,
3076
+ renderAssertionFailure,
3077
+ renderDiagnostics,
3078
+ renderSummary,
2485
3079
  resetConfig,
2486
3080
  simulationEvaluateBatch,
2487
3081
  tier,