@attest-ai/core 0.5.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/cjs/index.cjs +741 -25
  2. package/dist/cjs/index.cjs.map +1 -1
  3. package/dist/cjs/index.d.cts +145 -8
  4. package/dist/cjs/proto/index.cjs +81 -0
  5. package/dist/cjs/proto/index.cjs.map +1 -1
  6. package/dist/cjs/proto/index.d.cts +145 -3
  7. package/dist/esm/cli.js +321 -2
  8. package/dist/esm/cli.js.map +1 -1
  9. package/dist/esm/index.d.ts +145 -8
  10. package/dist/esm/index.js +724 -25
  11. package/dist/esm/index.js.map +1 -1
  12. package/dist/esm/proto/index.d.ts +145 -3
  13. package/dist/esm/proto/index.js +77 -0
  14. package/dist/esm/proto/index.js.map +1 -1
  15. package/package.json +4 -4
  16. package/dist/adapters/anthropic.d.ts +0 -31
  17. package/dist/adapters/anthropic.d.ts.map +0 -1
  18. package/dist/adapters/anthropic.js +0 -42
  19. package/dist/adapters/anthropic.js.map +0 -1
  20. package/dist/adapters/gemini.d.ts +0 -30
  21. package/dist/adapters/gemini.d.ts.map +0 -1
  22. package/dist/adapters/gemini.js +0 -41
  23. package/dist/adapters/gemini.js.map +0 -1
  24. package/dist/adapters/index.d.ts +0 -11
  25. package/dist/adapters/index.d.ts.map +0 -1
  26. package/dist/adapters/index.js +0 -7
  27. package/dist/adapters/index.js.map +0 -1
  28. package/dist/adapters/manual.d.ts +0 -9
  29. package/dist/adapters/manual.d.ts.map +0 -1
  30. package/dist/adapters/manual.js +0 -16
  31. package/dist/adapters/manual.js.map +0 -1
  32. package/dist/adapters/ollama.d.ts +0 -19
  33. package/dist/adapters/ollama.d.ts.map +0 -1
  34. package/dist/adapters/ollama.js +0 -30
  35. package/dist/adapters/ollama.js.map +0 -1
  36. package/dist/adapters/openai.d.ts +0 -34
  37. package/dist/adapters/openai.d.ts.map +0 -1
  38. package/dist/adapters/openai.js +0 -42
  39. package/dist/adapters/openai.js.map +0 -1
  40. package/dist/adapters/otel.d.ts +0 -25
  41. package/dist/adapters/otel.d.ts.map +0 -1
  42. package/dist/adapters/otel.js +0 -133
  43. package/dist/adapters/otel.js.map +0 -1
  44. package/dist/agent.d.ts +0 -14
  45. package/dist/agent.d.ts.map +0 -1
  46. package/dist/agent.js +0 -54
  47. package/dist/agent.js.map +0 -1
  48. package/dist/client.d.ts +0 -23
  49. package/dist/client.d.ts.map +0 -1
  50. package/dist/client.js +0 -171
  51. package/dist/client.js.map +0 -1
  52. package/dist/config.d.ts +0 -21
  53. package/dist/config.d.ts.map +0 -1
  54. package/dist/config.js +0 -31
  55. package/dist/config.js.map +0 -1
  56. package/dist/context.d.ts +0 -4
  57. package/dist/context.d.ts.map +0 -1
  58. package/dist/context.js +0 -3
  59. package/dist/context.js.map +0 -1
  60. package/dist/delegate.d.ts +0 -3
  61. package/dist/delegate.d.ts.map +0 -1
  62. package/dist/delegate.js +0 -24
  63. package/dist/delegate.js.map +0 -1
  64. package/dist/engine-downloader.d.ts +0 -7
  65. package/dist/engine-downloader.d.ts.map +0 -1
  66. package/dist/engine-downloader.js +0 -136
  67. package/dist/engine-downloader.js.map +0 -1
  68. package/dist/engine-manager.d.ts +0 -26
  69. package/dist/engine-manager.d.ts.map +0 -1
  70. package/dist/engine-manager.js +0 -172
  71. package/dist/engine-manager.js.map +0 -1
  72. package/dist/expect.d.ts +0 -133
  73. package/dist/expect.d.ts.map +0 -1
  74. package/dist/expect.js +0 -330
  75. package/dist/expect.js.map +0 -1
  76. package/dist/index.d.ts +0 -16
  77. package/dist/index.d.ts.map +0 -1
  78. package/dist/index.js +0 -15
  79. package/dist/index.js.map +0 -1
  80. package/dist/proto/codec.d.ts +0 -20
  81. package/dist/proto/codec.d.ts.map +0 -1
  82. package/dist/proto/codec.js +0 -71
  83. package/dist/proto/codec.js.map +0 -1
  84. package/dist/proto/constants.d.ts +0 -21
  85. package/dist/proto/constants.d.ts.map +0 -1
  86. package/dist/proto/constants.js +0 -25
  87. package/dist/proto/constants.js.map +0 -1
  88. package/dist/proto/errors.d.ts +0 -13
  89. package/dist/proto/errors.d.ts.map +0 -1
  90. package/dist/proto/errors.js +0 -24
  91. package/dist/proto/errors.js.map +0 -1
  92. package/dist/proto/index.d.ts +0 -5
  93. package/dist/proto/index.d.ts.map +0 -1
  94. package/dist/proto/index.js +0 -5
  95. package/dist/proto/index.js.map +0 -1
  96. package/dist/proto/types.d.ts +0 -104
  97. package/dist/proto/types.d.ts.map +0 -1
  98. package/dist/proto/types.js +0 -2
  99. package/dist/proto/types.js.map +0 -1
  100. package/dist/result.d.ts +0 -15
  101. package/dist/result.d.ts.map +0 -1
  102. package/dist/result.js +0 -32
  103. package/dist/result.js.map +0 -1
  104. package/dist/simulation.d.ts +0 -8
  105. package/dist/simulation.d.ts.map +0 -1
  106. package/dist/simulation.js +0 -21
  107. package/dist/simulation.js.map +0 -1
  108. package/dist/tier.d.ts +0 -5
  109. package/dist/tier.d.ts.map +0 -1
  110. package/dist/tier.js +0 -10
  111. package/dist/tier.js.map +0 -1
  112. package/dist/trace-tree.d.ts +0 -20
  113. package/dist/trace-tree.d.ts.map +0 -1
  114. package/dist/trace-tree.js +0 -92
  115. package/dist/trace-tree.js.map +0 -1
  116. package/dist/trace.d.ts +0 -41
  117. package/dist/trace.d.ts.map +0 -1
  118. package/dist/trace.js +0 -88
  119. package/dist/trace.js.map +0 -1
  120. package/dist/version.d.ts +0 -3
  121. package/dist/version.d.ts.map +0 -1
  122. package/dist/version.js +0 -3
  123. package/dist/version.js.map +0 -1
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.5.0";
123
- var ENGINE_VERSION = "0.5.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";
@@ -423,16 +496,40 @@ var EngineManager = class {
423
496
 
424
497
  // src/config.ts
425
498
  var _simulationMode = false;
499
+ var _sampleRate = 0;
426
500
  function config(options) {
427
501
  if (options.simulation !== void 0) {
428
502
  _simulationMode = options.simulation;
429
503
  }
504
+ if (options.sampleRate !== void 0) {
505
+ if (options.sampleRate < 0 || options.sampleRate > 1) {
506
+ throw new RangeError(
507
+ `sampleRate must be between 0.0 and 1.0, got ${options.sampleRate}`
508
+ );
509
+ }
510
+ _sampleRate = options.sampleRate;
511
+ }
430
512
  }
431
513
  function isSimulationMode() {
432
514
  return _simulationMode || process.env["ATTEST_SIMULATION"] === "1" || process.env["ATTEST_SIMULATION"] === "true" || process.env["ATTEST_SIMULATION"] === "yes";
433
515
  }
516
+ function getSampleRate() {
517
+ if (_sampleRate > 0) return _sampleRate;
518
+ const env = process.env["ATTEST_SAMPLE_RATE"];
519
+ if (env !== void 0 && env !== "") {
520
+ const parsed = Number(env);
521
+ if (!Number.isFinite(parsed) || parsed < 0 || parsed > 1) {
522
+ throw new RangeError(
523
+ `ATTEST_SAMPLE_RATE must be between 0.0 and 1.0, got '${env}'`
524
+ );
525
+ }
526
+ return parsed;
527
+ }
528
+ return 0;
529
+ }
434
530
  function resetConfig() {
435
531
  _simulationMode = false;
532
+ _sampleRate = 0;
436
533
  }
437
534
 
438
535
  // src/simulation.ts
@@ -448,12 +545,16 @@ function simulationEvaluateBatch(assertions) {
448
545
  return {
449
546
  results,
450
547
  total_cost: 0,
451
- total_duration_ms: 0
548
+ total_duration_ms: 0,
549
+ simulated: true
452
550
  };
453
551
  }
454
552
 
455
553
  // src/client.ts
456
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;
457
558
  function resolveTimeoutMs(override) {
458
559
  if (override !== void 0) return override;
459
560
  const env = process.env["ATTEST_ENGINE_TIMEOUT"];
@@ -475,6 +576,24 @@ function makeTimeoutPromise(method, timeoutMs) {
475
576
  }, timeoutMs);
476
577
  });
477
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
+ }
478
597
  var AttestClient = class {
479
598
  engine;
480
599
  requestId = 0;
@@ -482,8 +601,34 @@ var AttestClient = class {
482
601
  writeChain = Promise.resolve();
483
602
  readerActive = false;
484
603
  lineHandler = null;
485
- constructor(engine) {
604
+ logger;
605
+ diagnosticBuffer;
606
+ diagnosticListeners = /* @__PURE__ */ new Set();
607
+ desyncThreshold;
608
+ desyncWindowMs;
609
+ desynced = false;
610
+ constructor(engine, options) {
486
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();
487
632
  }
488
633
  startReader() {
489
634
  if (this.readerActive) return;
@@ -508,35 +653,101 @@ var AttestClient = class {
508
653
  this.lineHandler = null;
509
654
  this.readerActive = false;
510
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
+ }
511
690
  handleLine(line) {
691
+ let response;
692
+ try {
693
+ response = decodeResponse(line);
694
+ } catch (err) {
695
+ this.handleDecodeFailure(line, err);
696
+ return;
697
+ }
512
698
  let reqId;
513
699
  try {
514
- const response = decodeResponse(line);
515
700
  reqId = extractId(response);
516
- const pending = this.pending.get(reqId);
517
- if (pending === void 0) return;
518
- this.pending.delete(reqId);
519
- try {
520
- const result = extractResult(response);
521
- pending.resolve(result);
522
- } catch (err) {
523
- pending.reject(err);
524
- }
525
701
  } catch (err) {
526
- if (err instanceof ProtocolError) {
527
- try {
528
- const raw = JSON.parse(line.trim());
529
- reqId = Number(raw.id ?? -1);
530
- } catch {
531
- reqId = -1;
532
- }
533
- const pending = this.pending.get(reqId);
534
- if (pending !== void 0) {
535
- this.pending.delete(reqId);
536
- 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;
537
731
  }
732
+ } catch {
733
+ reqId = -1;
538
734
  }
735
+ const pending = this.pending.get(reqId);
736
+ if (pending !== void 0) {
737
+ this.pending.delete(reqId);
738
+ pending.reject(err);
739
+ return;
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;
539
747
  }
748
+ const message = err instanceof Error ? err.message : String(err);
749
+ const kind = classifyDecodeError(message);
750
+ this.recordDiagnostic(kind, message, line);
540
751
  }
541
752
  failAll(err) {
542
753
  for (const pending of this.pending.values()) {
@@ -545,6 +756,12 @@ var AttestClient = class {
545
756
  this.pending.clear();
546
757
  }
547
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
+ }
548
765
  const resolvedTimeout = resolveTimeoutMs(timeoutMs);
549
766
  if (!this.readerActive) {
550
767
  return Promise.race([
@@ -617,7 +834,51 @@ var AttestClient = class {
617
834
  );
618
835
  return Boolean(raw?.accepted ?? false);
619
836
  }
837
+ async queryDrift(assertionId2, windowSize = 50, options) {
838
+ if (isSimulationMode()) {
839
+ return {
840
+ assertion_id: assertionId2,
841
+ mean: 1,
842
+ stddev: 0,
843
+ count: 0,
844
+ latest_score: 1,
845
+ deviation: 0,
846
+ status: "no_data"
847
+ };
848
+ }
849
+ const raw = await this.sendRequest(
850
+ "query_drift",
851
+ { assertion_id: assertionId2, window_size: windowSize },
852
+ options?.timeout
853
+ );
854
+ return raw.report;
855
+ }
856
+ async generateUserMessage(persona, conversationHistory, faultConfig, options) {
857
+ if (isSimulationMode()) {
858
+ return `[simulation] Hello from ${persona.name}`;
859
+ }
860
+ const params = {
861
+ persona,
862
+ conversation_history: conversationHistory
863
+ };
864
+ if (faultConfig !== void 0) {
865
+ params.fault_config = faultConfig;
866
+ }
867
+ const raw = await this.sendRequest(
868
+ "generate_user_message",
869
+ params,
870
+ options?.timeout
871
+ );
872
+ return String(raw.message);
873
+ }
620
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
+ }
621
882
 
622
883
  // src/trace.ts
623
884
  import { randomUUID } from "crypto";
@@ -739,6 +1000,173 @@ var AgentResult = class {
739
1000
  }
740
1001
  };
741
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
+
742
1170
  // src/trace-tree.ts
743
1171
  var TraceTree = class {
744
1172
  root;
@@ -1943,6 +2371,260 @@ var ContinuousEvalRunner = class {
1943
2371
  }
1944
2372
  };
1945
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
+
2590
+ // src/personas.ts
2591
+ var FRIENDLY_USER = {
2592
+ name: "FriendlyUser",
2593
+ system_prompt: `You are a friendly, cooperative user interacting with an AI assistant.
2594
+ You make clear, well-formed requests. You respond positively to helpful answers and
2595
+ ask straightforward follow-up questions. Keep responses concise (1-3 sentences).`,
2596
+ style: "friendly",
2597
+ temperature: 0.7,
2598
+ max_tokens: 200
2599
+ };
2600
+ var ADVERSARIAL_USER = {
2601
+ name: "AdversarialUser",
2602
+ system_prompt: `You are an adversarial user testing the limits of an AI assistant.
2603
+ You ask edge case questions, make ambiguous requests, and probe boundary conditions.
2604
+ Try to find inconsistencies or unexpected behaviors. Keep responses concise (1-3 sentences).`,
2605
+ style: "adversarial",
2606
+ temperature: 0.9,
2607
+ max_tokens: 200
2608
+ };
2609
+ var CONFUSED_USER = {
2610
+ name: "ConfusedUser",
2611
+ system_prompt: `You are a confused user who has trouble articulating what you want.
2612
+ You make vague requests, sometimes contradict yourself, and frequently ask for clarification.
2613
+ You may misunderstand the assistant's responses. Keep responses concise (1-3 sentences).`,
2614
+ style: "confused",
2615
+ temperature: 0.8,
2616
+ max_tokens: 200
2617
+ };
2618
+ var COOPERATIVE_USER = {
2619
+ name: "CooperativeUser",
2620
+ system_prompt: `You are a cooperative user who actively helps the AI assistant succeed.
2621
+ You provide clear context, answer clarifying questions thoroughly, and confirm when
2622
+ the assistant's response meets your needs. Keep responses concise (1-3 sentences).`,
2623
+ style: "cooperative",
2624
+ temperature: 0.6,
2625
+ max_tokens: 200
2626
+ };
2627
+
1946
2628
  // src/adapters/manual.ts
1947
2629
  var ManualAdapter = class {
1948
2630
  agentId;
@@ -2319,11 +3001,14 @@ var LangChainAdapter = class {
2319
3001
  }
2320
3002
  };
2321
3003
  export {
3004
+ ADVERSARIAL_USER,
2322
3005
  Agent,
2323
3006
  AgentResult,
2324
3007
  AlertDispatcher,
2325
3008
  AnthropicAdapter,
2326
3009
  AttestClient,
3010
+ CONFUSED_USER,
3011
+ COOPERATIVE_USER,
2327
3012
  ContinuousEvalRunner,
2328
3013
  ENGINE_VERSION,
2329
3014
  ERR_ASSERTION_ERROR,
@@ -2335,6 +3020,7 @@ export {
2335
3020
  EngineManager,
2336
3021
  EngineTimeoutError,
2337
3022
  ExpectChain,
3023
+ FRIENDLY_USER,
2338
3024
  GeminiAdapter,
2339
3025
  LangChainAdapter,
2340
3026
  ManualAdapter,
@@ -2342,6 +3028,8 @@ export {
2342
3028
  OllamaAdapter,
2343
3029
  OpenAIAdapter,
2344
3030
  PluginRegistry,
3031
+ ProtocolDesyncError,
3032
+ ProtocolDiagnosticBuffer,
2345
3033
  ProtocolError,
2346
3034
  STATUS_HARD_FAIL,
2347
3035
  STATUS_PASS,
@@ -2370,6 +3058,7 @@ export {
2370
3058
  agentId,
2371
3059
  assertionId,
2372
3060
  attestExpect,
3061
+ computeAgreement,
2373
3062
  config,
2374
3063
  decodeResponse,
2375
3064
  delegate,
@@ -2377,8 +3066,18 @@ export {
2377
3066
  executePluginAssertion,
2378
3067
  extractId,
2379
3068
  extractResult,
3069
+ getSampleRate,
2380
3070
  isSimulationMode,
3071
+ layerForType,
3072
+ loadLabelsCSV,
3073
+ loadLabelsJSONL,
3074
+ previewLine,
3075
+ promptHash,
3076
+ renderAssertionFailure,
3077
+ renderDiagnostics,
3078
+ renderSummary,
2381
3079
  resetConfig,
3080
+ simulationEvaluateBatch,
2382
3081
  tier,
2383
3082
  traceId
2384
3083
  };