@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
@@ -30,11 +30,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ ADVERSARIAL_USER: () => ADVERSARIAL_USER,
33
34
  Agent: () => Agent,
34
35
  AgentResult: () => AgentResult,
35
36
  AlertDispatcher: () => AlertDispatcher,
36
37
  AnthropicAdapter: () => AnthropicAdapter,
37
38
  AttestClient: () => AttestClient,
39
+ CONFUSED_USER: () => CONFUSED_USER,
40
+ COOPERATIVE_USER: () => COOPERATIVE_USER,
38
41
  ContinuousEvalRunner: () => ContinuousEvalRunner,
39
42
  ENGINE_VERSION: () => ENGINE_VERSION,
40
43
  ERR_ASSERTION_ERROR: () => ERR_ASSERTION_ERROR,
@@ -46,6 +49,7 @@ __export(index_exports, {
46
49
  EngineManager: () => EngineManager,
47
50
  EngineTimeoutError: () => EngineTimeoutError,
48
51
  ExpectChain: () => ExpectChain,
52
+ FRIENDLY_USER: () => FRIENDLY_USER,
49
53
  GeminiAdapter: () => GeminiAdapter,
50
54
  LangChainAdapter: () => LangChainAdapter,
51
55
  ManualAdapter: () => ManualAdapter,
@@ -53,6 +57,8 @@ __export(index_exports, {
53
57
  OllamaAdapter: () => OllamaAdapter,
54
58
  OpenAIAdapter: () => OpenAIAdapter,
55
59
  PluginRegistry: () => PluginRegistry,
60
+ ProtocolDesyncError: () => ProtocolDesyncError,
61
+ ProtocolDiagnosticBuffer: () => ProtocolDiagnosticBuffer,
56
62
  ProtocolError: () => ProtocolError,
57
63
  STATUS_HARD_FAIL: () => STATUS_HARD_FAIL,
58
64
  STATUS_PASS: () => STATUS_PASS,
@@ -81,6 +87,7 @@ __export(index_exports, {
81
87
  agentId: () => agentId,
82
88
  assertionId: () => assertionId,
83
89
  attestExpect: () => attestExpect,
90
+ computeAgreement: () => computeAgreement,
84
91
  config: () => config,
85
92
  decodeResponse: () => decodeResponse,
86
93
  delegate: () => delegate,
@@ -88,8 +95,18 @@ __export(index_exports, {
88
95
  executePluginAssertion: () => executePluginAssertion,
89
96
  extractId: () => extractId,
90
97
  extractResult: () => extractResult,
98
+ getSampleRate: () => getSampleRate,
91
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,
92
108
  resetConfig: () => resetConfig,
109
+ simulationEvaluateBatch: () => simulationEvaluateBatch,
93
110
  tier: () => tier,
94
111
  traceId: () => traceId
95
112
  });
@@ -128,6 +145,27 @@ function assertionId(id) {
128
145
  function agentId(id) {
129
146
  return id;
130
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
+ }
131
169
 
132
170
  // src/proto/errors.ts
133
171
  var ProtocolError = class extends Error {
@@ -154,6 +192,58 @@ var EngineTimeoutError = class extends Error {
154
192
  this.timeoutMs = timeoutMs;
155
193
  }
156
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
+ }
157
247
 
158
248
  // src/proto/codec.ts
159
249
  function encodeRequest(id, method, params) {
@@ -216,8 +306,8 @@ function extractId(response) {
216
306
  }
217
307
 
218
308
  // src/version.ts
219
- var VERSION = "0.5.0";
220
- var ENGINE_VERSION = "0.5.0";
309
+ var VERSION = "0.9.0";
310
+ var ENGINE_VERSION = "0.9.0";
221
311
 
222
312
  // src/engine-manager.ts
223
313
  var import_node_child_process = require("child_process");
@@ -521,16 +611,40 @@ var EngineManager = class {
521
611
 
522
612
  // src/config.ts
523
613
  var _simulationMode = false;
614
+ var _sampleRate = 0;
524
615
  function config(options) {
525
616
  if (options.simulation !== void 0) {
526
617
  _simulationMode = options.simulation;
527
618
  }
619
+ if (options.sampleRate !== void 0) {
620
+ if (options.sampleRate < 0 || options.sampleRate > 1) {
621
+ throw new RangeError(
622
+ `sampleRate must be between 0.0 and 1.0, got ${options.sampleRate}`
623
+ );
624
+ }
625
+ _sampleRate = options.sampleRate;
626
+ }
528
627
  }
529
628
  function isSimulationMode() {
530
629
  return _simulationMode || process.env["ATTEST_SIMULATION"] === "1" || process.env["ATTEST_SIMULATION"] === "true" || process.env["ATTEST_SIMULATION"] === "yes";
531
630
  }
631
+ function getSampleRate() {
632
+ if (_sampleRate > 0) return _sampleRate;
633
+ const env = process.env["ATTEST_SAMPLE_RATE"];
634
+ if (env !== void 0 && env !== "") {
635
+ const parsed = Number(env);
636
+ if (!Number.isFinite(parsed) || parsed < 0 || parsed > 1) {
637
+ throw new RangeError(
638
+ `ATTEST_SAMPLE_RATE must be between 0.0 and 1.0, got '${env}'`
639
+ );
640
+ }
641
+ return parsed;
642
+ }
643
+ return 0;
644
+ }
532
645
  function resetConfig() {
533
646
  _simulationMode = false;
647
+ _sampleRate = 0;
534
648
  }
535
649
 
536
650
  // src/simulation.ts
@@ -546,12 +660,16 @@ function simulationEvaluateBatch(assertions) {
546
660
  return {
547
661
  results,
548
662
  total_cost: 0,
549
- total_duration_ms: 0
663
+ total_duration_ms: 0,
664
+ simulated: true
550
665
  };
551
666
  }
552
667
 
553
668
  // src/client.ts
554
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;
555
673
  function resolveTimeoutMs(override) {
556
674
  if (override !== void 0) return override;
557
675
  const env = process.env["ATTEST_ENGINE_TIMEOUT"];
@@ -573,6 +691,24 @@ function makeTimeoutPromise(method, timeoutMs) {
573
691
  }, timeoutMs);
574
692
  });
575
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
+ }
576
712
  var AttestClient = class {
577
713
  engine;
578
714
  requestId = 0;
@@ -580,8 +716,34 @@ var AttestClient = class {
580
716
  writeChain = Promise.resolve();
581
717
  readerActive = false;
582
718
  lineHandler = null;
583
- constructor(engine) {
719
+ logger;
720
+ diagnosticBuffer;
721
+ diagnosticListeners = /* @__PURE__ */ new Set();
722
+ desyncThreshold;
723
+ desyncWindowMs;
724
+ desynced = false;
725
+ constructor(engine, options) {
584
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();
585
747
  }
586
748
  startReader() {
587
749
  if (this.readerActive) return;
@@ -606,35 +768,101 @@ var AttestClient = class {
606
768
  this.lineHandler = null;
607
769
  this.readerActive = false;
608
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
+ }
609
805
  handleLine(line) {
806
+ let response;
807
+ try {
808
+ response = decodeResponse(line);
809
+ } catch (err) {
810
+ this.handleDecodeFailure(line, err);
811
+ return;
812
+ }
610
813
  let reqId;
611
814
  try {
612
- const response = decodeResponse(line);
613
815
  reqId = extractId(response);
614
- const pending = this.pending.get(reqId);
615
- if (pending === void 0) return;
616
- this.pending.delete(reqId);
617
- try {
618
- const result = extractResult(response);
619
- pending.resolve(result);
620
- } catch (err) {
621
- pending.reject(err);
622
- }
623
816
  } catch (err) {
624
- if (err instanceof ProtocolError) {
625
- try {
626
- const raw = JSON.parse(line.trim());
627
- reqId = Number(raw.id ?? -1);
628
- } catch {
629
- reqId = -1;
630
- }
631
- const pending = this.pending.get(reqId);
632
- if (pending !== void 0) {
633
- this.pending.delete(reqId);
634
- 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;
635
846
  }
847
+ } catch {
848
+ reqId = -1;
636
849
  }
850
+ const pending = this.pending.get(reqId);
851
+ if (pending !== void 0) {
852
+ this.pending.delete(reqId);
853
+ pending.reject(err);
854
+ return;
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;
637
862
  }
863
+ const message = err instanceof Error ? err.message : String(err);
864
+ const kind = classifyDecodeError(message);
865
+ this.recordDiagnostic(kind, message, line);
638
866
  }
639
867
  failAll(err) {
640
868
  for (const pending of this.pending.values()) {
@@ -643,6 +871,12 @@ var AttestClient = class {
643
871
  this.pending.clear();
644
872
  }
645
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
+ }
646
880
  const resolvedTimeout = resolveTimeoutMs(timeoutMs);
647
881
  if (!this.readerActive) {
648
882
  return Promise.race([
@@ -715,7 +949,51 @@ var AttestClient = class {
715
949
  );
716
950
  return Boolean(raw?.accepted ?? false);
717
951
  }
952
+ async queryDrift(assertionId2, windowSize = 50, options) {
953
+ if (isSimulationMode()) {
954
+ return {
955
+ assertion_id: assertionId2,
956
+ mean: 1,
957
+ stddev: 0,
958
+ count: 0,
959
+ latest_score: 1,
960
+ deviation: 0,
961
+ status: "no_data"
962
+ };
963
+ }
964
+ const raw = await this.sendRequest(
965
+ "query_drift",
966
+ { assertion_id: assertionId2, window_size: windowSize },
967
+ options?.timeout
968
+ );
969
+ return raw.report;
970
+ }
971
+ async generateUserMessage(persona, conversationHistory, faultConfig, options) {
972
+ if (isSimulationMode()) {
973
+ return `[simulation] Hello from ${persona.name}`;
974
+ }
975
+ const params = {
976
+ persona,
977
+ conversation_history: conversationHistory
978
+ };
979
+ if (faultConfig !== void 0) {
980
+ params.fault_config = faultConfig;
981
+ }
982
+ const raw = await this.sendRequest(
983
+ "generate_user_message",
984
+ params,
985
+ options?.timeout
986
+ );
987
+ return String(raw.message);
988
+ }
718
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
+ }
719
997
 
720
998
  // src/trace.ts
721
999
  var import_node_crypto = require("crypto");
@@ -837,6 +1115,173 @@ var AgentResult = class {
837
1115
  }
838
1116
  };
839
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
+
840
1285
  // src/trace-tree.ts
841
1286
  var TraceTree = class {
842
1287
  root;
@@ -2041,6 +2486,260 @@ var ContinuousEvalRunner = class {
2041
2486
  }
2042
2487
  };
2043
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
+
2705
+ // src/personas.ts
2706
+ var FRIENDLY_USER = {
2707
+ name: "FriendlyUser",
2708
+ system_prompt: `You are a friendly, cooperative user interacting with an AI assistant.
2709
+ You make clear, well-formed requests. You respond positively to helpful answers and
2710
+ ask straightforward follow-up questions. Keep responses concise (1-3 sentences).`,
2711
+ style: "friendly",
2712
+ temperature: 0.7,
2713
+ max_tokens: 200
2714
+ };
2715
+ var ADVERSARIAL_USER = {
2716
+ name: "AdversarialUser",
2717
+ system_prompt: `You are an adversarial user testing the limits of an AI assistant.
2718
+ You ask edge case questions, make ambiguous requests, and probe boundary conditions.
2719
+ Try to find inconsistencies or unexpected behaviors. Keep responses concise (1-3 sentences).`,
2720
+ style: "adversarial",
2721
+ temperature: 0.9,
2722
+ max_tokens: 200
2723
+ };
2724
+ var CONFUSED_USER = {
2725
+ name: "ConfusedUser",
2726
+ system_prompt: `You are a confused user who has trouble articulating what you want.
2727
+ You make vague requests, sometimes contradict yourself, and frequently ask for clarification.
2728
+ You may misunderstand the assistant's responses. Keep responses concise (1-3 sentences).`,
2729
+ style: "confused",
2730
+ temperature: 0.8,
2731
+ max_tokens: 200
2732
+ };
2733
+ var COOPERATIVE_USER = {
2734
+ name: "CooperativeUser",
2735
+ system_prompt: `You are a cooperative user who actively helps the AI assistant succeed.
2736
+ You provide clear context, answer clarifying questions thoroughly, and confirm when
2737
+ the assistant's response meets your needs. Keep responses concise (1-3 sentences).`,
2738
+ style: "cooperative",
2739
+ temperature: 0.6,
2740
+ max_tokens: 200
2741
+ };
2742
+
2044
2743
  // src/adapters/manual.ts
2045
2744
  var ManualAdapter = class {
2046
2745
  agentId;
@@ -2418,11 +3117,14 @@ var LangChainAdapter = class {
2418
3117
  };
2419
3118
  // Annotate the CommonJS export names for ESM import in node:
2420
3119
  0 && (module.exports = {
3120
+ ADVERSARIAL_USER,
2421
3121
  Agent,
2422
3122
  AgentResult,
2423
3123
  AlertDispatcher,
2424
3124
  AnthropicAdapter,
2425
3125
  AttestClient,
3126
+ CONFUSED_USER,
3127
+ COOPERATIVE_USER,
2426
3128
  ContinuousEvalRunner,
2427
3129
  ENGINE_VERSION,
2428
3130
  ERR_ASSERTION_ERROR,
@@ -2434,6 +3136,7 @@ var LangChainAdapter = class {
2434
3136
  EngineManager,
2435
3137
  EngineTimeoutError,
2436
3138
  ExpectChain,
3139
+ FRIENDLY_USER,
2437
3140
  GeminiAdapter,
2438
3141
  LangChainAdapter,
2439
3142
  ManualAdapter,
@@ -2441,6 +3144,8 @@ var LangChainAdapter = class {
2441
3144
  OllamaAdapter,
2442
3145
  OpenAIAdapter,
2443
3146
  PluginRegistry,
3147
+ ProtocolDesyncError,
3148
+ ProtocolDiagnosticBuffer,
2444
3149
  ProtocolError,
2445
3150
  STATUS_HARD_FAIL,
2446
3151
  STATUS_PASS,
@@ -2469,6 +3174,7 @@ var LangChainAdapter = class {
2469
3174
  agentId,
2470
3175
  assertionId,
2471
3176
  attestExpect,
3177
+ computeAgreement,
2472
3178
  config,
2473
3179
  decodeResponse,
2474
3180
  delegate,
@@ -2476,8 +3182,18 @@ var LangChainAdapter = class {
2476
3182
  executePluginAssertion,
2477
3183
  extractId,
2478
3184
  extractResult,
3185
+ getSampleRate,
2479
3186
  isSimulationMode,
3187
+ layerForType,
3188
+ loadLabelsCSV,
3189
+ loadLabelsJSONL,
3190
+ previewLine,
3191
+ promptHash,
3192
+ renderAssertionFailure,
3193
+ renderDiagnostics,
3194
+ renderSummary,
2480
3195
  resetConfig,
3196
+ simulationEvaluateBatch,
2481
3197
  tier,
2482
3198
  traceId
2483
3199
  });