@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.
@@ -57,6 +57,8 @@ __export(index_exports, {
57
57
  OllamaAdapter: () => OllamaAdapter,
58
58
  OpenAIAdapter: () => OpenAIAdapter,
59
59
  PluginRegistry: () => PluginRegistry,
60
+ ProtocolDesyncError: () => ProtocolDesyncError,
61
+ ProtocolDiagnosticBuffer: () => ProtocolDiagnosticBuffer,
60
62
  ProtocolError: () => ProtocolError,
61
63
  STATUS_HARD_FAIL: () => STATUS_HARD_FAIL,
62
64
  STATUS_PASS: () => STATUS_PASS,
@@ -85,6 +87,7 @@ __export(index_exports, {
85
87
  agentId: () => agentId,
86
88
  assertionId: () => assertionId,
87
89
  attestExpect: () => attestExpect,
90
+ computeAgreement: () => computeAgreement,
88
91
  config: () => config,
89
92
  decodeResponse: () => decodeResponse,
90
93
  delegate: () => delegate,
@@ -94,6 +97,14 @@ __export(index_exports, {
94
97
  extractResult: () => extractResult,
95
98
  getSampleRate: () => getSampleRate,
96
99
  isSimulationMode: () => isSimulationMode,
100
+ layerForType: () => layerForType,
101
+ loadLabelsCSV: () => loadLabelsCSV,
102
+ loadLabelsJSONL: () => loadLabelsJSONL,
103
+ previewLine: () => previewLine,
104
+ promptHash: () => promptHash,
105
+ renderAssertionFailure: () => renderAssertionFailure,
106
+ renderDiagnostics: () => renderDiagnostics,
107
+ renderSummary: () => renderSummary,
97
108
  resetConfig: () => resetConfig,
98
109
  simulationEvaluateBatch: () => simulationEvaluateBatch,
99
110
  tier: () => tier,
@@ -134,6 +145,27 @@ function assertionId(id) {
134
145
  function agentId(id) {
135
146
  return id;
136
147
  }
148
+ function layerForType(assertionType) {
149
+ switch (assertionType) {
150
+ case "schema":
151
+ return 1;
152
+ case "constraint":
153
+ return 2;
154
+ case "trace":
155
+ case "trace_tree":
156
+ return 3;
157
+ case "content":
158
+ return 4;
159
+ case "embedding":
160
+ return 5;
161
+ case "llm_judge":
162
+ return 6;
163
+ case "plugin":
164
+ return 8;
165
+ default:
166
+ return 0;
167
+ }
168
+ }
137
169
 
138
170
  // src/proto/errors.ts
139
171
  var ProtocolError = class extends Error {
@@ -160,6 +192,58 @@ var EngineTimeoutError = class extends Error {
160
192
  this.timeoutMs = timeoutMs;
161
193
  }
162
194
  };
195
+ var ProtocolDesyncError = class extends Error {
196
+ diagnostics;
197
+ constructor(message, diagnostics) {
198
+ super(message);
199
+ this.name = "ProtocolDesyncError";
200
+ this.diagnostics = diagnostics;
201
+ }
202
+ };
203
+
204
+ // src/proto/diagnostics.ts
205
+ var DEFAULT_BUFFER_SIZE = 32;
206
+ var MAX_LINE_PREVIEW = 512;
207
+ var ProtocolDiagnosticBuffer = class {
208
+ capacity;
209
+ entries = [];
210
+ constructor(capacity = DEFAULT_BUFFER_SIZE) {
211
+ if (capacity <= 0) {
212
+ throw new Error(
213
+ `ProtocolDiagnosticBuffer capacity must be positive, got ${capacity}`
214
+ );
215
+ }
216
+ this.capacity = capacity;
217
+ }
218
+ push(diagnostic) {
219
+ if (this.entries.length >= this.capacity) {
220
+ this.entries.shift();
221
+ }
222
+ this.entries.push(diagnostic);
223
+ }
224
+ snapshot() {
225
+ return this.entries.slice();
226
+ }
227
+ countWithin(windowMs, nowMs) {
228
+ const cutoff = nowMs - windowMs;
229
+ let count = 0;
230
+ for (let i = this.entries.length - 1; i >= 0; i -= 1) {
231
+ if (this.entries[i].timestampMs >= cutoff) {
232
+ count += 1;
233
+ } else {
234
+ break;
235
+ }
236
+ }
237
+ return count;
238
+ }
239
+ clear() {
240
+ this.entries.length = 0;
241
+ }
242
+ };
243
+ function previewLine(line) {
244
+ if (line.length <= MAX_LINE_PREVIEW) return line;
245
+ return line.slice(0, MAX_LINE_PREVIEW) + "\u2026";
246
+ }
163
247
 
164
248
  // src/proto/codec.ts
165
249
  function encodeRequest(id, method, params) {
@@ -222,8 +306,8 @@ function extractId(response) {
222
306
  }
223
307
 
224
308
  // src/version.ts
225
- var VERSION = "0.6.0";
226
- var ENGINE_VERSION = "0.6.0";
309
+ var VERSION = "0.9.0";
310
+ var ENGINE_VERSION = "0.9.0";
227
311
 
228
312
  // src/engine-manager.ts
229
313
  var import_node_child_process = require("child_process");
@@ -576,12 +660,16 @@ function simulationEvaluateBatch(assertions) {
576
660
  return {
577
661
  results,
578
662
  total_cost: 0,
579
- total_duration_ms: 0
663
+ total_duration_ms: 0,
664
+ simulated: true
580
665
  };
581
666
  }
582
667
 
583
668
  // src/client.ts
584
669
  var DEFAULT_TIMEOUT_MS = 3e4;
670
+ var DEFAULT_DIAGNOSTIC_BUFFER = 32;
671
+ var DEFAULT_DESYNC_THRESHOLD = 3;
672
+ var DEFAULT_DESYNC_WINDOW_MS = 1e3;
585
673
  function resolveTimeoutMs(override) {
586
674
  if (override !== void 0) return override;
587
675
  const env = process.env["ATTEST_ENGINE_TIMEOUT"];
@@ -603,6 +691,24 @@ function makeTimeoutPromise(method, timeoutMs) {
603
691
  }, timeoutMs);
604
692
  });
605
693
  }
694
+ function debugProtocolEnabled() {
695
+ const flag = process.env["ATTEST_DEBUG_PROTOCOL"];
696
+ return flag === "1" || flag === "true" || flag === "yes";
697
+ }
698
+ function envLoggerDefault() {
699
+ if (debugProtocolEnabled()) {
700
+ return {
701
+ warn: (msg, ...args) => console.warn(msg, ...args),
702
+ error: (msg, ...args) => console.error(msg, ...args)
703
+ };
704
+ }
705
+ return {
706
+ warn: () => {
707
+ },
708
+ error: () => {
709
+ }
710
+ };
711
+ }
606
712
  var AttestClient = class {
607
713
  engine;
608
714
  requestId = 0;
@@ -610,8 +716,34 @@ var AttestClient = class {
610
716
  writeChain = Promise.resolve();
611
717
  readerActive = false;
612
718
  lineHandler = null;
613
- constructor(engine) {
719
+ logger;
720
+ diagnosticBuffer;
721
+ diagnosticListeners = /* @__PURE__ */ new Set();
722
+ desyncThreshold;
723
+ desyncWindowMs;
724
+ desynced = false;
725
+ constructor(engine, options) {
614
726
  this.engine = engine;
727
+ this.logger = options?.logger ?? envLoggerDefault();
728
+ this.diagnosticBuffer = new ProtocolDiagnosticBuffer(
729
+ options?.diagnosticBufferSize ?? DEFAULT_DIAGNOSTIC_BUFFER
730
+ );
731
+ this.desyncThreshold = options?.desyncThreshold ?? DEFAULT_DESYNC_THRESHOLD;
732
+ this.desyncWindowMs = options?.desyncWindowMs ?? DEFAULT_DESYNC_WINDOW_MS;
733
+ if (options?.onDiagnostic !== void 0) {
734
+ this.diagnosticListeners.add(options.onDiagnostic);
735
+ }
736
+ }
737
+ /** Subscribe to protocol diagnostics. Returns an unsubscribe function. */
738
+ onProtocolDiagnostic(listener) {
739
+ this.diagnosticListeners.add(listener);
740
+ return () => {
741
+ this.diagnosticListeners.delete(listener);
742
+ };
743
+ }
744
+ /** Snapshot of the diagnostic ring buffer (most recent last). */
745
+ protocolDiagnostics() {
746
+ return this.diagnosticBuffer.snapshot();
615
747
  }
616
748
  startReader() {
617
749
  if (this.readerActive) return;
@@ -636,35 +768,101 @@ var AttestClient = class {
636
768
  this.lineHandler = null;
637
769
  this.readerActive = false;
638
770
  }
771
+ recordDiagnostic(kind, message, rawLine) {
772
+ const diagnostic = {
773
+ kind,
774
+ message,
775
+ rawLine: previewLine(rawLine),
776
+ timestampMs: Date.now()
777
+ };
778
+ this.diagnosticBuffer.push(diagnostic);
779
+ this.logger.warn(`[attest.protocol] ${kind}: ${message}`);
780
+ for (const listener of this.diagnosticListeners) {
781
+ try {
782
+ listener(diagnostic);
783
+ } catch (err) {
784
+ this.logger.error(
785
+ `[attest.protocol] diagnostic listener threw: ${err instanceof Error ? err.message : String(err)}`
786
+ );
787
+ }
788
+ }
789
+ this.maybeTriggerDesync(diagnostic.timestampMs);
790
+ }
791
+ maybeTriggerDesync(nowMs) {
792
+ if (this.desynced) return;
793
+ const recent = this.diagnosticBuffer.countWithin(this.desyncWindowMs, nowMs);
794
+ if (recent >= this.desyncThreshold) {
795
+ this.desynced = true;
796
+ const snapshot = this.diagnosticBuffer.snapshot();
797
+ const err = new ProtocolDesyncError(
798
+ `Engine protocol desync: ${recent} unroutable response lines within ${this.desyncWindowMs}ms.`,
799
+ snapshot
800
+ );
801
+ this.logger.error(`[attest.protocol] ${err.message}`);
802
+ this.failAll(err);
803
+ }
804
+ }
639
805
  handleLine(line) {
806
+ let response;
807
+ try {
808
+ response = decodeResponse(line);
809
+ } catch (err) {
810
+ this.handleDecodeFailure(line, err);
811
+ return;
812
+ }
640
813
  let reqId;
641
814
  try {
642
- const response = decodeResponse(line);
643
815
  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
816
  } catch (err) {
654
- if (err instanceof ProtocolError) {
655
- try {
656
- const raw = JSON.parse(line.trim());
657
- reqId = Number(raw.id ?? -1);
658
- } catch {
659
- reqId = -1;
660
- }
661
- const pending = this.pending.get(reqId);
662
- if (pending !== void 0) {
663
- this.pending.delete(reqId);
664
- pending.reject(err);
817
+ const message = err instanceof Error ? err.message : String(err);
818
+ this.recordDiagnostic("missing_id", message, line);
819
+ return;
820
+ }
821
+ const pending = this.pending.get(reqId);
822
+ if (pending === void 0) {
823
+ this.recordDiagnostic(
824
+ "unknown_id",
825
+ `no pending request for id=${reqId}`,
826
+ line
827
+ );
828
+ return;
829
+ }
830
+ this.pending.delete(reqId);
831
+ try {
832
+ const result = extractResult(response);
833
+ pending.resolve(result);
834
+ } catch (err) {
835
+ pending.reject(err);
836
+ }
837
+ }
838
+ handleDecodeFailure(line, err) {
839
+ if (err instanceof ProtocolError) {
840
+ let reqId = -1;
841
+ try {
842
+ const raw = JSON.parse(line.trim());
843
+ const idCandidate = raw["id"];
844
+ if (typeof idCandidate === "number" && Number.isFinite(idCandidate)) {
845
+ reqId = idCandidate;
665
846
  }
847
+ } catch {
848
+ reqId = -1;
849
+ }
850
+ const pending = this.pending.get(reqId);
851
+ if (pending !== void 0) {
852
+ this.pending.delete(reqId);
853
+ pending.reject(err);
854
+ return;
666
855
  }
856
+ this.recordDiagnostic(
857
+ "non_routable_error",
858
+ `engine error code=${err.code} (${err.errorMessage}) had no matching pending id`,
859
+ line
860
+ );
861
+ return;
667
862
  }
863
+ const message = err instanceof Error ? err.message : String(err);
864
+ const kind = classifyDecodeError(message);
865
+ this.recordDiagnostic(kind, message, line);
668
866
  }
669
867
  failAll(err) {
670
868
  for (const pending of this.pending.values()) {
@@ -673,6 +871,12 @@ var AttestClient = class {
673
871
  this.pending.clear();
674
872
  }
675
873
  async sendRequest(method, params, timeoutMs) {
874
+ if (this.desynced) {
875
+ throw new ProtocolDesyncError(
876
+ "Engine protocol is desynced; AttestClient will not send further requests.",
877
+ this.diagnosticBuffer.snapshot()
878
+ );
879
+ }
676
880
  const resolvedTimeout = resolveTimeoutMs(timeoutMs);
677
881
  if (!this.readerActive) {
678
882
  return Promise.race([
@@ -783,6 +987,13 @@ var AttestClient = class {
783
987
  return String(raw.message);
784
988
  }
785
989
  };
990
+ function classifyDecodeError(message) {
991
+ if (message.startsWith("malformed JSON")) return "malformed_json";
992
+ if (message.startsWith("expected JSON object")) return "non_object_response";
993
+ if (message.startsWith("invalid jsonrpc version")) return "invalid_jsonrpc_version";
994
+ if (message.startsWith("empty response line")) return "malformed_json";
995
+ return "malformed_json";
996
+ }
786
997
 
787
998
  // src/trace.ts
788
999
  var import_node_crypto = require("crypto");
@@ -904,6 +1115,173 @@ var AgentResult = class {
904
1115
  }
905
1116
  };
906
1117
 
1118
+ // src/diagnostics.ts
1119
+ var ANSI_RESET = "\x1B[0m";
1120
+ var ANSI_RED = "\x1B[31m";
1121
+ var ANSI_YELLOW = "\x1B[33m";
1122
+ var ANSI_GREEN = "\x1B[32m";
1123
+ var ANSI_DIM = "\x1B[2m";
1124
+ var ANSI_BOLD = "\x1B[1m";
1125
+ var LAYER_NAMES = {
1126
+ 1: "schema",
1127
+ 2: "constraint",
1128
+ 3: "trace",
1129
+ 4: "content",
1130
+ 5: "embedding",
1131
+ 6: "llm_judge",
1132
+ 7: "trace_tree",
1133
+ 8: "plugin"
1134
+ };
1135
+ function layerLabel(layer, assertionType) {
1136
+ if (layer === 0 && assertionType) {
1137
+ return `L? ${assertionType}`;
1138
+ }
1139
+ const name = LAYER_NAMES[layer] ?? assertionType ?? "uncategorized";
1140
+ return `L${layer} ${name}`;
1141
+ }
1142
+ function statusGlyph(status, color) {
1143
+ let glyph;
1144
+ let c;
1145
+ if (status === STATUS_PASS) {
1146
+ glyph = "PASS";
1147
+ c = ANSI_GREEN;
1148
+ } else if (status === STATUS_SOFT_FAIL) {
1149
+ glyph = "SOFT";
1150
+ c = ANSI_YELLOW;
1151
+ } else if (status === STATUS_HARD_FAIL) {
1152
+ glyph = "FAIL";
1153
+ c = ANSI_RED;
1154
+ } else {
1155
+ glyph = status.toUpperCase() || "????";
1156
+ c = ANSI_DIM;
1157
+ }
1158
+ return color ? `${c}${glyph}${ANSI_RESET}` : glyph;
1159
+ }
1160
+ function bold(s, color) {
1161
+ return color ? `${ANSI_BOLD}${s}${ANSI_RESET}` : s;
1162
+ }
1163
+ function dim(s, color) {
1164
+ return color ? `${ANSI_DIM}${s}${ANSI_RESET}` : s;
1165
+ }
1166
+ function truncate(value, limit = 280) {
1167
+ if (value.length <= limit) {
1168
+ return value;
1169
+ }
1170
+ return value.slice(0, limit - 3) + "...";
1171
+ }
1172
+ function renderAssertionFailure(result, options = {}) {
1173
+ const color = options.color ?? false;
1174
+ const testFile = options.testFile;
1175
+ const layer = result.layer ?? 0;
1176
+ const label = layerLabel(layer, result.type);
1177
+ const lines = [];
1178
+ lines.push(
1179
+ ` ${statusGlyph(result.status, color)} ${bold(result.assertion_id, color)} ${dim(`\u2014 ${label}`, color)}`
1180
+ );
1181
+ if (result.trace_node_path) {
1182
+ lines.push(` trace path: ${result.trace_node_path}`);
1183
+ }
1184
+ lines.push(` score: ${result.score.toFixed(3)}`);
1185
+ if (result.expected) {
1186
+ lines.push(` expected: ${truncate(result.expected)}`);
1187
+ }
1188
+ if (result.actual) {
1189
+ lines.push(` actual: ${truncate(result.actual)}`);
1190
+ }
1191
+ if (result.explanation) {
1192
+ lines.push(` detail: ${truncate(result.explanation)}`);
1193
+ }
1194
+ if (result.threshold_source && result.threshold_source !== "static") {
1195
+ lines.push(` threshold: ${result.threshold_source}`);
1196
+ }
1197
+ if (result.judge_metadata) {
1198
+ appendJudgeMetadata(lines, result.judge_metadata);
1199
+ }
1200
+ if (result.suggested_action) {
1201
+ lines.push(` hint: ${truncate(result.suggested_action)}`);
1202
+ }
1203
+ if ((result.cost ?? 0) > 0 || (result.duration_ms ?? 0) > 0) {
1204
+ const cost = result.cost ?? 0;
1205
+ const durMs = result.duration_ms ?? 0;
1206
+ lines.push(` cost/lat: $${cost.toFixed(6)} / ${durMs}ms`);
1207
+ }
1208
+ if (testFile) {
1209
+ lines.push(` source: ${testFile}`);
1210
+ }
1211
+ return lines.join("\n") + "\n";
1212
+ }
1213
+ function appendJudgeMetadata(lines, meta) {
1214
+ if (meta.model) {
1215
+ let rubric = meta.rubric_name ?? "default";
1216
+ if (meta.rubric_version) {
1217
+ rubric = `${rubric} @ ${meta.rubric_version}`;
1218
+ }
1219
+ lines.push(` judge: ${meta.model} / ${rubric}`);
1220
+ }
1221
+ if (meta.prompt_hash) {
1222
+ lines.push(` prompt: #${meta.prompt_hash}`);
1223
+ }
1224
+ const samples = meta.sample_scores ?? [];
1225
+ if (samples.length > 1) {
1226
+ const formatted = samples.map((s) => s.toFixed(2)).join(", ");
1227
+ const mean = (meta.score_mean ?? 0).toFixed(2);
1228
+ const stddev = (meta.score_stddev ?? 0).toFixed(2);
1229
+ const flag = meta.high_variance ? " \u26A0 HIGH VARIANCE" : "";
1230
+ lines.push(
1231
+ ` samples: [${formatted}] mean=${mean} stddev=${stddev}${flag}`
1232
+ );
1233
+ }
1234
+ const probes = meta.bias_probes ?? [];
1235
+ if (probes.length > 0) {
1236
+ const formatted = probes.map((p) => `${p.name} \u0394${p.delta >= 0 ? "+" : ""}${p.delta.toFixed(2)}`).join(", ");
1237
+ lines.push(` bias: ${formatted}`);
1238
+ }
1239
+ if (meta.calibration) {
1240
+ const c = meta.calibration;
1241
+ lines.push(
1242
+ ` calibrated: ${c.label_count} labels, agreement=${c.agreement.toFixed(2)}, \u03BA=${c.cohen_kappa.toFixed(2)}`
1243
+ );
1244
+ }
1245
+ }
1246
+ function renderSummary(result, options = {}) {
1247
+ const color = options.color ?? false;
1248
+ const passN = result.passCount;
1249
+ const soft = result.assertionResults.filter(
1250
+ (r) => r.status === STATUS_SOFT_FAIL
1251
+ ).length;
1252
+ const hard = result.assertionResults.filter(
1253
+ (r) => r.status === STATUS_HARD_FAIL
1254
+ ).length;
1255
+ const parts = [
1256
+ `${statusGlyph(STATUS_PASS, color)} ${passN}`,
1257
+ `${statusGlyph(STATUS_SOFT_FAIL, color)} ${soft}`,
1258
+ `${statusGlyph(STATUS_HARD_FAIL, color)} ${hard}`,
1259
+ `cost $${result.totalCost.toFixed(6)}`,
1260
+ `dur ${result.totalDurationMs}ms`
1261
+ ];
1262
+ return parts.join(" | ");
1263
+ }
1264
+ function renderDiagnostics(result, options = {}) {
1265
+ const color = options.color ?? false;
1266
+ const includePassing = options.includePassing ?? false;
1267
+ const rows = includePassing ? result.assertionResults : result.failedAssertions;
1268
+ if (rows.length === 0) {
1269
+ return dim(" (no failures)\n", color);
1270
+ }
1271
+ const out = [""];
1272
+ out.push(
1273
+ bold(
1274
+ `Attest diagnostic \u2014 ${rows.length} of ${result.assertionResults.length} assertions failed:`,
1275
+ color
1276
+ )
1277
+ );
1278
+ for (const r of rows) {
1279
+ out.push(renderAssertionFailure(r, options));
1280
+ }
1281
+ out.push(dim(` Summary: ${renderSummary(result, { color })}`, color));
1282
+ return out.join("\n");
1283
+ }
1284
+
907
1285
  // src/trace-tree.ts
908
1286
  var TraceTree = class {
909
1287
  root;
@@ -2108,6 +2486,222 @@ var ContinuousEvalRunner = class {
2108
2486
  }
2109
2487
  };
2110
2488
 
2489
+ // src/calibration.ts
2490
+ var import_node_crypto3 = require("crypto");
2491
+ function computeAgreement(pairs, threshold) {
2492
+ if (pairs.length === 0) {
2493
+ throw new Error("no labeled pairs available");
2494
+ }
2495
+ if (threshold <= 0 || threshold >= 1) {
2496
+ throw new Error("threshold must be in (0, 1)");
2497
+ }
2498
+ const n = pairs.length;
2499
+ let humanPos = 0;
2500
+ let judgePos = 0;
2501
+ let bothPos = 0;
2502
+ let bothNeg = 0;
2503
+ for (const p of pairs) {
2504
+ const hPos = p.human >= threshold;
2505
+ const jPos = p.judge >= threshold;
2506
+ if (hPos) humanPos++;
2507
+ if (jPos) judgePos++;
2508
+ if (hPos && jPos) bothPos++;
2509
+ if (!hPos && !jPos) bothNeg++;
2510
+ }
2511
+ const humanNeg = n - humanPos;
2512
+ const judgeNeg = n - judgePos;
2513
+ const agreement = (bothPos + bothNeg) / n;
2514
+ const expected = (humanPos * judgePos + humanNeg * judgeNeg) / (n * n);
2515
+ const cohenKappa = expected < 1 ? (agreement - expected) / (1 - expected) : 0;
2516
+ const rocAuc = rocAUC(pairs, threshold);
2517
+ return { threshold, n, agreement, cohenKappa, rocAuc };
2518
+ }
2519
+ function rocAUC(pairs, threshold) {
2520
+ const rows = pairs.map((p) => ({
2521
+ score: p.judge,
2522
+ pos: p.human >= threshold
2523
+ }));
2524
+ const pos = rows.filter((r) => r.pos).length;
2525
+ const neg = rows.length - pos;
2526
+ if (pos === 0 || neg === 0) return 0;
2527
+ rows.sort((a, b) => a.score - b.score);
2528
+ let rankSumPos = 0;
2529
+ let i = 0;
2530
+ while (i < rows.length) {
2531
+ let j = i;
2532
+ while (j < rows.length && rows[j].score === rows[i].score) {
2533
+ j++;
2534
+ }
2535
+ const avgRank = (i + j + 1) / 2;
2536
+ for (let k = i; k < j; k++) {
2537
+ if (rows[k].pos) rankSumPos += avgRank;
2538
+ }
2539
+ i = j;
2540
+ }
2541
+ const auc = (rankSumPos - pos * (pos + 1) / 2) / (pos * neg);
2542
+ if (Number.isNaN(auc) || !Number.isFinite(auc)) return 0;
2543
+ return auc;
2544
+ }
2545
+ function loadLabelsCSV(text) {
2546
+ const rows = parseCSV(text);
2547
+ if (rows.length === 0) {
2548
+ throw new Error("empty CSV");
2549
+ }
2550
+ let start = 0;
2551
+ if (rows[0].length >= 2) {
2552
+ const firstHuman = parseFloatStrict(rows[0][1]);
2553
+ if (firstHuman === null) start = 1;
2554
+ }
2555
+ const out = [];
2556
+ for (let idx = start; idx < rows.length; idx++) {
2557
+ const row = rows[idx];
2558
+ const lineNum = idx + 1;
2559
+ if (row.length === 0) continue;
2560
+ if (row[0].trimStart().startsWith("#")) continue;
2561
+ if (row.length < 2) {
2562
+ throw new Error(
2563
+ `CSV line ${lineNum}: want at least 2 columns (input, human_label)`
2564
+ );
2565
+ }
2566
+ const human = parseFloatStrict(row[1]);
2567
+ if (human === null) {
2568
+ throw new Error(`CSV line ${lineNum}: human_label not a float: ${row[1]}`);
2569
+ }
2570
+ const rec = {
2571
+ input: row[0],
2572
+ humanLabel: human,
2573
+ judgeScore: 0,
2574
+ judgeKnown: false
2575
+ };
2576
+ if (row.length >= 3 && row[2].trim() !== "") {
2577
+ const judge = parseFloatStrict(row[2]);
2578
+ if (judge === null) {
2579
+ throw new Error(
2580
+ `CSV line ${lineNum}: judge_score not a float: ${row[2]}`
2581
+ );
2582
+ }
2583
+ rec.judgeScore = judge;
2584
+ rec.judgeKnown = true;
2585
+ }
2586
+ out.push(rec);
2587
+ }
2588
+ if (out.length === 0) {
2589
+ throw new Error("CSV contained no labeled rows");
2590
+ }
2591
+ return out;
2592
+ }
2593
+ function loadLabelsJSONL(text) {
2594
+ const out = [];
2595
+ const lines = text.split(/\r?\n/);
2596
+ for (let i = 0; i < lines.length; i++) {
2597
+ const stripped = lines[i].trim();
2598
+ if (!stripped) continue;
2599
+ let obj;
2600
+ try {
2601
+ obj = JSON.parse(stripped);
2602
+ } catch (err) {
2603
+ throw new Error(`JSONL line ${i + 1}: ${err.message}`);
2604
+ }
2605
+ if (Object.keys(obj).length === 0) continue;
2606
+ if (obj["human_label"] === void 0) {
2607
+ throw new Error(`JSONL line ${i + 1}: missing human_label`);
2608
+ }
2609
+ const human = parseJSONNumber(obj["human_label"]);
2610
+ if (human === null) {
2611
+ throw new Error(`JSONL line ${i + 1}: human_label is not a number`);
2612
+ }
2613
+ const rec = {
2614
+ input: typeof obj["input"] === "string" ? obj["input"] : "",
2615
+ humanLabel: human,
2616
+ judgeScore: 0,
2617
+ judgeKnown: false
2618
+ };
2619
+ if (obj["judge_score"] !== void 0 && obj["judge_score"] !== null) {
2620
+ const judge = parseJSONNumber(obj["judge_score"]);
2621
+ if (judge === null) {
2622
+ throw new Error(`JSONL line ${i + 1}: judge_score is not a number`);
2623
+ }
2624
+ rec.judgeScore = judge;
2625
+ rec.judgeKnown = true;
2626
+ }
2627
+ out.push(rec);
2628
+ }
2629
+ if (out.length === 0) {
2630
+ throw new Error("JSONL contained no labeled rows");
2631
+ }
2632
+ return out;
2633
+ }
2634
+ function promptHash(text) {
2635
+ return (0, import_node_crypto3.createHash)("sha256").update(text).digest("hex").slice(0, 16);
2636
+ }
2637
+ function parseFloatStrict(raw) {
2638
+ const trimmed = raw.trim();
2639
+ if (trimmed === "") return null;
2640
+ const n = Number(trimmed);
2641
+ if (Number.isNaN(n)) return null;
2642
+ return n;
2643
+ }
2644
+ function parseJSONNumber(value) {
2645
+ if (typeof value !== "number") return null;
2646
+ if (Number.isNaN(value)) return null;
2647
+ return value;
2648
+ }
2649
+ function parseCSV(text) {
2650
+ const rows = [];
2651
+ let current = [];
2652
+ let field = "";
2653
+ let inQuotes = false;
2654
+ let i = 0;
2655
+ while (i < text.length) {
2656
+ const ch = text[i];
2657
+ if (inQuotes) {
2658
+ if (ch === '"') {
2659
+ if (text[i + 1] === '"') {
2660
+ field += '"';
2661
+ i += 2;
2662
+ continue;
2663
+ }
2664
+ inQuotes = false;
2665
+ i++;
2666
+ continue;
2667
+ }
2668
+ field += ch;
2669
+ i++;
2670
+ continue;
2671
+ }
2672
+ if (ch === '"') {
2673
+ inQuotes = true;
2674
+ i++;
2675
+ continue;
2676
+ }
2677
+ if (ch === ",") {
2678
+ current.push(field);
2679
+ field = "";
2680
+ i++;
2681
+ continue;
2682
+ }
2683
+ if (ch === "\n") {
2684
+ current.push(field);
2685
+ rows.push(current);
2686
+ current = [];
2687
+ field = "";
2688
+ i++;
2689
+ continue;
2690
+ }
2691
+ if (ch === "\r") {
2692
+ i++;
2693
+ continue;
2694
+ }
2695
+ field += ch;
2696
+ i++;
2697
+ }
2698
+ if (field.length > 0 || current.length > 0) {
2699
+ current.push(field);
2700
+ rows.push(current);
2701
+ }
2702
+ return rows;
2703
+ }
2704
+
2111
2705
  // src/personas.ts
2112
2706
  var FRIENDLY_USER = {
2113
2707
  name: "FriendlyUser",
@@ -2550,6 +3144,8 @@ var LangChainAdapter = class {
2550
3144
  OllamaAdapter,
2551
3145
  OpenAIAdapter,
2552
3146
  PluginRegistry,
3147
+ ProtocolDesyncError,
3148
+ ProtocolDiagnosticBuffer,
2553
3149
  ProtocolError,
2554
3150
  STATUS_HARD_FAIL,
2555
3151
  STATUS_PASS,
@@ -2578,6 +3174,7 @@ var LangChainAdapter = class {
2578
3174
  agentId,
2579
3175
  assertionId,
2580
3176
  attestExpect,
3177
+ computeAgreement,
2581
3178
  config,
2582
3179
  decodeResponse,
2583
3180
  delegate,
@@ -2587,6 +3184,14 @@ var LangChainAdapter = class {
2587
3184
  extractResult,
2588
3185
  getSampleRate,
2589
3186
  isSimulationMode,
3187
+ layerForType,
3188
+ loadLabelsCSV,
3189
+ loadLabelsJSONL,
3190
+ previewLine,
3191
+ promptHash,
3192
+ renderAssertionFailure,
3193
+ renderDiagnostics,
3194
+ renderSummary,
2590
3195
  resetConfig,
2591
3196
  simulationEvaluateBatch,
2592
3197
  tier,