@tangle-network/agent-eval 0.86.0 → 0.90.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -1
- package/dist/adapters/http.d.ts +3 -3
- package/dist/adapters/langchain.d.ts +3 -3
- package/dist/adapters/otel.d.ts +6 -6
- package/dist/adversarial-DIVcDoI_.d.ts +88 -0
- package/dist/analyst/index.d.ts +11 -10
- package/dist/analyst/index.js +13 -8
- package/dist/analyst/index.js.map +1 -1
- package/dist/analyze-runs-DwCEkpO_.d.ts +81 -0
- package/dist/belief-state/index.d.ts +4 -4
- package/dist/belief-state/index.js +1 -1
- package/dist/benchmarks/index.d.ts +3 -3
- package/dist/campaign/index.d.ts +165 -18
- package/dist/campaign/index.js +289 -14
- package/dist/campaign/index.js.map +1 -1
- package/dist/chunk-45EEMHTC.js +35 -0
- package/dist/chunk-45EEMHTC.js.map +1 -0
- package/dist/{chunk-FZWAFVAA.js → chunk-4FBZZIYD.js} +2 -2
- package/dist/{chunk-YV7J7X5N.js → chunk-5HRORJQY.js} +22 -12
- package/dist/chunk-5HRORJQY.js.map +1 -0
- package/dist/{chunk-OTYQPHPL.js → chunk-6SOJM3VR.js} +5 -5
- package/dist/chunk-BOD4O7OF.js +40 -0
- package/dist/chunk-BOD4O7OF.js.map +1 -0
- package/dist/{chunk-Z7VFTS2J.js → chunk-CY6U5S3X.js} +2 -2
- package/dist/{chunk-VIDQF3F5.js → chunk-D3V5B42D.js} +5 -34
- package/dist/chunk-D3V5B42D.js.map +1 -0
- package/dist/{chunk-YGYXHNAQ.js → chunk-FIUKOSWI.js} +21 -8
- package/dist/chunk-FIUKOSWI.js.map +1 -0
- package/dist/{chunk-WJL2NJXN.js → chunk-GSH6QNNS.js} +2 -2
- package/dist/{chunk-RBNA5AZT.js → chunk-L3JOU6XM.js} +2 -2
- package/dist/{chunk-IDVBLYCY.js → chunk-LMZQ2Z4U.js} +56 -2
- package/dist/{chunk-IDVBLYCY.js.map → chunk-LMZQ2Z4U.js.map} +1 -1
- package/dist/{chunk-VUINJM5M.js → chunk-QAY5UIJO.js} +2 -193
- package/dist/chunk-QAY5UIJO.js.map +1 -0
- package/dist/{chunk-P2J6SOXT.js → chunk-QG2OVF2D.js} +5 -3
- package/dist/{chunk-P2J6SOXT.js.map → chunk-QG2OVF2D.js.map} +1 -1
- package/dist/chunk-REVYNR6C.js +100 -0
- package/dist/chunk-REVYNR6C.js.map +1 -0
- package/dist/chunk-STGVSCDH.js +202 -0
- package/dist/chunk-STGVSCDH.js.map +1 -0
- package/dist/{chunk-ZZ2HOPME.js → chunk-TWS7AZEY.js} +2 -2
- package/dist/chunk-UHMJT4T7.js +200 -0
- package/dist/chunk-UHMJT4T7.js.map +1 -0
- package/dist/chunk-UMMZHCPB.js +190 -0
- package/dist/chunk-UMMZHCPB.js.map +1 -0
- package/dist/chunk-VZSRQ272.js +149 -0
- package/dist/chunk-VZSRQ272.js.map +1 -0
- package/dist/{chunk-L5G7OUKD.js → chunk-XY4DDNEG.js} +8 -190
- package/dist/chunk-XY4DDNEG.js.map +1 -0
- package/dist/chunk-Y47J2LJ3.js +859 -0
- package/dist/chunk-Y47J2LJ3.js.map +1 -0
- package/dist/{chunk-BABOZOSN.js → chunk-ZFIBGEOL.js} +3 -3
- package/dist/chunk-ZFIBGEOL.js.map +1 -0
- package/dist/{code-agent-session-BRXmavYv.d.ts → code-agent-session-BO8nCnv3.d.ts} +1 -1
- package/dist/contract/index.d.ts +24 -95
- package/dist/contract/index.js +25 -764
- package/dist/contract/index.js.map +1 -1
- package/dist/{control-GeE8OhpN.d.ts → control-_Qb7skHX.d.ts} +2 -2
- package/dist/control.d.ts +5 -5
- package/dist/corpus-BoR-041R.d.ts +560 -0
- package/dist/cost-ledger-DuSqlw5B.d.ts +113 -0
- package/dist/counterfactual-Dwibr5IW.d.ts +85 -0
- package/dist/{dataset-B2kL-fSM.d.ts → dataset-BbGkaN2I.d.ts} +1 -1
- package/dist/{registry-DrEQ3Luj.d.ts → default-registry-zoGHUQEH.d.ts} +29 -2
- package/dist/diagnose.d.ts +251 -0
- package/dist/diagnose.js +381 -0
- package/dist/diagnose.js.map +1 -0
- package/dist/{errors-Dwqw-T_m.d.ts → errors-CzMUYo7b.d.ts} +1 -1
- package/dist/{feedback-trajectory-B3rErRsh.d.ts → feedback-trajectory-D9OVLrg9.d.ts} +1 -1
- package/dist/fuzz.d.ts +484 -0
- package/dist/fuzz.js +613 -0
- package/dist/fuzz.js.map +1 -0
- package/dist/governance/index.d.ts +4 -4
- package/dist/hosted/index.d.ts +6 -6
- package/dist/{index-DE3RXAXD.d.ts → index-Bx3gZ8xl.d.ts} +1 -1
- package/dist/index.d.ts +718 -455
- package/dist/index.js +1604 -793
- package/dist/index.js.map +1 -1
- package/dist/{insight-report-3ADTfClO.d.ts → insight-report-BBwvOh6x.d.ts} +2 -2
- package/dist/{integrity-CJzrpUua.d.ts → integrity-VJ9A7aST.d.ts} +1 -1
- package/dist/{judge-calibration-DilmB3Ml.d.ts → judge-calibration-0p2QcWNE.d.ts} +1 -1
- package/dist/{kind-factory-CVecZZG_.d.ts → kind-factory-5b7xXXOr.d.ts} +2 -2
- package/dist/{llm-client-CuUg2Mn3.d.ts → llm-client-BeEcAokY.d.ts} +1 -1
- package/dist/matrix/index.d.ts +2 -2
- package/dist/meta-eval/index.d.ts +177 -3
- package/dist/meta-eval/index.js +260 -1
- package/dist/meta-eval/index.js.map +1 -1
- package/dist/{multi-layer-verifier-DlWCXuxL.d.ts → multi-layer-verifier-DUZXrPDA.d.ts} +7 -1
- package/dist/multishot/index.d.ts +25 -11
- package/dist/multishot/index.js +36 -7
- package/dist/multishot/index.js.map +1 -1
- package/dist/openapi.json +1 -1
- package/dist/perf/index.d.ts +123 -0
- package/dist/perf/index.js +18 -0
- package/dist/pipelines/index.js +2 -2
- package/dist/{agent-profile-D0PBIWlV.d.ts → pre-registration-DELOEJ8v.d.ts} +144 -4
- package/dist/{provenance-DPpNIOJD.d.ts → provenance-LnqRT0sS.d.ts} +5 -5
- package/dist/{red-team-DW9Ca_tj.d.ts → red-team-BXHil6c8.d.ts} +1 -1
- package/dist/{release-report-hlNtD12q.d.ts → release-report-euXIV_Sk.d.ts} +3 -3
- package/dist/reporting.d.ts +8 -8
- package/dist/reporting.js +3 -3
- package/dist/{researcher-BLPHBbNV.d.ts → researcher-DE6Gpnb4.d.ts} +4 -4
- package/dist/rl.d.ts +194 -656
- package/dist/rl.js +231 -149
- package/dist/rl.js.map +1 -1
- package/dist/{rubric-predictive-validity-CnEl9Jc8.d.ts → rubric-predictive-validity-Cy_W-hWZ.d.ts} +1 -1
- package/dist/{run-campaign-4Y5V5CN3.js → run-campaign-RDGAM5KJ.js} +3 -3
- package/dist/run-campaign-RDGAM5KJ.js.map +1 -0
- package/dist/{run-improvement-loop-CNqQckTj.d.ts → run-improvement-loop-5z_l5zDz.d.ts} +2 -2
- package/dist/{run-record-De9VarXR.d.ts → run-record-e7vj1uZQ.d.ts} +1 -1
- package/dist/{runtime-trajectory-BLRiaifm.d.ts → runtime-trajectory-BDgfGZSr.d.ts} +1 -1
- package/dist/{semantic-concept-judge-DIEgr_6v.d.ts → semantic-concept-judge-Dn8Z6KEG.d.ts} +5 -31
- package/dist/series-convergence-D5OWMBg6.d.ts +33 -0
- package/dist/{statistics-CnC1FMbx.d.ts → statistics-C7PozGrZ.d.ts} +71 -2
- package/dist/{summary-report-Db0dDSWP.d.ts → summary-report-DGmUucwQ.d.ts} +1 -1
- package/dist/traces.d.ts +3 -3
- package/dist/traces.js +8 -6
- package/dist/{types-Cu3u_x59.d.ts → types-2VVIL04s.d.ts} +2 -2
- package/dist/{types-D7lLRYe9.d.ts → types-BU-7W85F.d.ts} +21 -1
- package/dist/{types-CqPax19X.d.ts → types-mn5Aqk7x.d.ts} +1 -1
- package/dist/{verdict-CeEgtjyI.d.ts → verdict-C9MlYujm.d.ts} +3 -0
- package/dist/wire/index.d.ts +3 -3
- package/dist/workflow/index.d.ts +12 -11
- package/dist/workflow/index.js +1 -1
- package/package.json +16 -1
- package/dist/chunk-BABOZOSN.js.map +0 -1
- package/dist/chunk-L5G7OUKD.js.map +0 -1
- package/dist/chunk-SHTXZ4O2.js +0 -113
- package/dist/chunk-SHTXZ4O2.js.map +0 -1
- package/dist/chunk-VIDQF3F5.js.map +0 -1
- package/dist/chunk-VUINJM5M.js.map +0 -1
- package/dist/chunk-YGYXHNAQ.js.map +0 -1
- package/dist/chunk-YV7J7X5N.js.map +0 -1
- /package/dist/{chunk-FZWAFVAA.js.map → chunk-4FBZZIYD.js.map} +0 -0
- /package/dist/{chunk-OTYQPHPL.js.map → chunk-6SOJM3VR.js.map} +0 -0
- /package/dist/{chunk-Z7VFTS2J.js.map → chunk-CY6U5S3X.js.map} +0 -0
- /package/dist/{chunk-WJL2NJXN.js.map → chunk-GSH6QNNS.js.map} +0 -0
- /package/dist/{chunk-RBNA5AZT.js.map → chunk-L3JOU6XM.js.map} +0 -0
- /package/dist/{chunk-ZZ2HOPME.js.map → chunk-TWS7AZEY.js.map} +0 -0
- /package/dist/{run-campaign-4Y5V5CN3.js.map → perf/index.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,22 +1,47 @@
|
|
|
1
|
-
import {
|
|
2
|
-
agentProfileHash,
|
|
3
|
-
createLlmCorrectnessChecker,
|
|
4
|
-
createTokenRecallChecker,
|
|
5
|
-
extractProducedState,
|
|
6
|
-
parseCorrectnessResponse,
|
|
7
|
-
verifyCompletion
|
|
8
|
-
} from "./chunk-YGYXHNAQ.js";
|
|
9
1
|
import {
|
|
10
2
|
parseRuntimeTrajectoryHookEvent,
|
|
11
3
|
projectRuntimeTrajectoryEvidence
|
|
12
4
|
} from "./chunk-T4SQEITX.js";
|
|
13
5
|
import {
|
|
14
6
|
HoldoutAuditor,
|
|
7
|
+
analyzeRuns,
|
|
15
8
|
canaryLeakView,
|
|
16
9
|
checkBehavioralCanary,
|
|
17
10
|
checkCanaries,
|
|
18
11
|
runBehavioralCanaries
|
|
19
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-Y47J2LJ3.js";
|
|
13
|
+
import {
|
|
14
|
+
classifyEuAiRisk,
|
|
15
|
+
euAiActReport,
|
|
16
|
+
nistAiRmfReport,
|
|
17
|
+
renderMarkdown,
|
|
18
|
+
soc2Report,
|
|
19
|
+
summarize
|
|
20
|
+
} from "./chunk-KKHDIONI.js";
|
|
21
|
+
import {
|
|
22
|
+
acquisitionPlansForKnowledgeGaps,
|
|
23
|
+
blockingKnowledgeEval,
|
|
24
|
+
knowledgeReadinessTracePayload,
|
|
25
|
+
scoreKnowledgeReadiness,
|
|
26
|
+
userQuestionsForKnowledgeGaps
|
|
27
|
+
} from "./chunk-3CKU6VGU.js";
|
|
28
|
+
import {
|
|
29
|
+
assertRecordIntegrity,
|
|
30
|
+
checkRecordIntegrity,
|
|
31
|
+
expandMatrix,
|
|
32
|
+
gatePerf,
|
|
33
|
+
scenarioKey,
|
|
34
|
+
summarizeRecords
|
|
35
|
+
} from "./chunk-STGVSCDH.js";
|
|
36
|
+
import {
|
|
37
|
+
agentProfileHash,
|
|
38
|
+
completionVerdict,
|
|
39
|
+
createLlmCorrectnessChecker,
|
|
40
|
+
createTokenRecallChecker,
|
|
41
|
+
extractProducedState,
|
|
42
|
+
parseCorrectnessResponse,
|
|
43
|
+
verifyCompletion
|
|
44
|
+
} from "./chunk-FIUKOSWI.js";
|
|
20
45
|
import {
|
|
21
46
|
DEFAULT_MUTATION_PRIMITIVES,
|
|
22
47
|
DEFAULT_RED_TEAM_CORPUS,
|
|
@@ -40,12 +65,12 @@ import {
|
|
|
40
65
|
scoreRedTeamOutput,
|
|
41
66
|
surfaceContentHash,
|
|
42
67
|
toolNamesForRun
|
|
43
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-ZFIBGEOL.js";
|
|
44
69
|
import {
|
|
45
70
|
BackendIntegrityError,
|
|
46
71
|
assertRealBackend,
|
|
47
72
|
summarizeBackendIntegrity
|
|
48
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-TWS7AZEY.js";
|
|
49
74
|
import {
|
|
50
75
|
MODEL_PRICING,
|
|
51
76
|
MetricsCollector,
|
|
@@ -67,14 +92,14 @@ import {
|
|
|
67
92
|
computeToolUseMetrics,
|
|
68
93
|
iqr,
|
|
69
94
|
welchsTTest
|
|
70
|
-
} from "./chunk-
|
|
95
|
+
} from "./chunk-L3JOU6XM.js";
|
|
96
|
+
import {
|
|
97
|
+
analyzeSeries
|
|
98
|
+
} from "./chunk-BOD4O7OF.js";
|
|
71
99
|
import {
|
|
72
100
|
exportTrainingData,
|
|
73
101
|
toNdjson
|
|
74
102
|
} from "./chunk-KMPRBJK4.js";
|
|
75
|
-
import {
|
|
76
|
-
buildTrajectory
|
|
77
|
-
} from "./chunk-RZTMDUO7.js";
|
|
78
103
|
import {
|
|
79
104
|
DockerSandboxDriver,
|
|
80
105
|
SandboxHarness,
|
|
@@ -85,21 +110,6 @@ import {
|
|
|
85
110
|
runTestGradedScenario,
|
|
86
111
|
vitestTestParser
|
|
87
112
|
} from "./chunk-T375SUOZ.js";
|
|
88
|
-
import {
|
|
89
|
-
classifyEuAiRisk,
|
|
90
|
-
euAiActReport,
|
|
91
|
-
nistAiRmfReport,
|
|
92
|
-
renderMarkdown,
|
|
93
|
-
soc2Report,
|
|
94
|
-
summarize
|
|
95
|
-
} from "./chunk-KKHDIONI.js";
|
|
96
|
-
import {
|
|
97
|
-
acquisitionPlansForKnowledgeGaps,
|
|
98
|
-
blockingKnowledgeEval,
|
|
99
|
-
knowledgeReadinessTracePayload,
|
|
100
|
-
scoreKnowledgeReadiness,
|
|
101
|
-
userQuestionsForKnowledgeGaps
|
|
102
|
-
} from "./chunk-3CKU6VGU.js";
|
|
103
113
|
import {
|
|
104
114
|
DEFAULT_COMPLEXITY_WEIGHTS,
|
|
105
115
|
DEFAULT_RUN_SCORE_WEIGHTS,
|
|
@@ -111,9 +121,7 @@ import {
|
|
|
111
121
|
SKILL_USAGE_ANALYST,
|
|
112
122
|
SkillUsageAnalyst,
|
|
113
123
|
aggregateRunScore,
|
|
114
|
-
buildDefaultAnalystRegistry,
|
|
115
124
|
clamp01,
|
|
116
|
-
computeTraceMetrics,
|
|
117
125
|
createAnalystAi,
|
|
118
126
|
createChatClient,
|
|
119
127
|
createSemanticConceptJudge,
|
|
@@ -121,7 +129,11 @@ import {
|
|
|
121
129
|
diffFindings,
|
|
122
130
|
resetLockedAppendersForTesting,
|
|
123
131
|
runSemanticConceptJudge
|
|
124
|
-
} from "./chunk-
|
|
132
|
+
} from "./chunk-XY4DDNEG.js";
|
|
133
|
+
import {
|
|
134
|
+
buildDefaultAnalystRegistry,
|
|
135
|
+
computeTraceMetrics
|
|
136
|
+
} from "./chunk-UMMZHCPB.js";
|
|
125
137
|
import {
|
|
126
138
|
AnalystRegistry,
|
|
127
139
|
DEFAULT_TRACE_ANALYST_KINDS,
|
|
@@ -129,11 +141,9 @@ import {
|
|
|
129
141
|
IMPROVEMENT_KIND_SPEC,
|
|
130
142
|
KNOWLEDGE_GAP_KIND_SPEC,
|
|
131
143
|
KNOWLEDGE_POISONING_KIND_SPEC,
|
|
132
|
-
computeFindingId,
|
|
133
144
|
createTraceAnalystKind,
|
|
134
|
-
makeFinding,
|
|
135
145
|
renderPriorFindings
|
|
136
|
-
} from "./chunk-
|
|
146
|
+
} from "./chunk-D3V5B42D.js";
|
|
137
147
|
import {
|
|
138
148
|
controlFailureClassFromVerification,
|
|
139
149
|
controlRunToRunRecord,
|
|
@@ -159,10 +169,10 @@ import {
|
|
|
159
169
|
evaluateReleaseConfidence,
|
|
160
170
|
judgeReplayGate,
|
|
161
171
|
renderReleaseReport
|
|
162
|
-
} from "./chunk-
|
|
172
|
+
} from "./chunk-4FBZZIYD.js";
|
|
163
173
|
import {
|
|
164
174
|
runEvalCampaign
|
|
165
|
-
} from "./chunk-
|
|
175
|
+
} from "./chunk-GSH6QNNS.js";
|
|
166
176
|
import {
|
|
167
177
|
LlmCallError,
|
|
168
178
|
LlmClient,
|
|
@@ -185,7 +195,18 @@ import {
|
|
|
185
195
|
paretoChart,
|
|
186
196
|
researchReport,
|
|
187
197
|
summaryTable
|
|
188
|
-
} from "./chunk-
|
|
198
|
+
} from "./chunk-CY6U5S3X.js";
|
|
199
|
+
import {
|
|
200
|
+
attributeCounterfactuals,
|
|
201
|
+
runCounterfactual
|
|
202
|
+
} from "./chunk-REVYNR6C.js";
|
|
203
|
+
import {
|
|
204
|
+
buildTrajectory
|
|
205
|
+
} from "./chunk-RZTMDUO7.js";
|
|
206
|
+
import {
|
|
207
|
+
computeFindingId,
|
|
208
|
+
makeFinding
|
|
209
|
+
} from "./chunk-45EEMHTC.js";
|
|
189
210
|
import {
|
|
190
211
|
benjaminiHochberg,
|
|
191
212
|
bonferroni,
|
|
@@ -197,9 +218,11 @@ import {
|
|
|
197
218
|
continuousAgreement,
|
|
198
219
|
corpusInterRaterAgreement,
|
|
199
220
|
corpusInterRaterAgreementFromJudgeScores,
|
|
221
|
+
eProcess,
|
|
200
222
|
interRaterReliability,
|
|
201
223
|
interpretCliffs,
|
|
202
224
|
mannWhitneyU,
|
|
225
|
+
mulberry32,
|
|
203
226
|
normalizeScores,
|
|
204
227
|
pairedBootstrap,
|
|
205
228
|
pairedMde,
|
|
@@ -212,7 +235,7 @@ import {
|
|
|
212
235
|
weightedComposite,
|
|
213
236
|
weightedMean,
|
|
214
237
|
wilcoxonSignedRank
|
|
215
|
-
} from "./chunk-
|
|
238
|
+
} from "./chunk-LMZQ2Z4U.js";
|
|
216
239
|
import {
|
|
217
240
|
FileSystemTraceStore,
|
|
218
241
|
InMemoryTraceStore,
|
|
@@ -243,7 +266,7 @@ import {
|
|
|
243
266
|
scoreTraceInsightReadiness,
|
|
244
267
|
tokenizeDomainWords,
|
|
245
268
|
traceAnalystOnRunComplete
|
|
246
|
-
} from "./chunk-
|
|
269
|
+
} from "./chunk-QG2OVF2D.js";
|
|
247
270
|
import {
|
|
248
271
|
DEFAULT_REDACTION_RULES,
|
|
249
272
|
REDACTION_VERSION,
|
|
@@ -270,16 +293,18 @@ import {
|
|
|
270
293
|
isToolSpan
|
|
271
294
|
} from "./chunk-5BKGXME7.js";
|
|
272
295
|
import {
|
|
273
|
-
DEFAULT_TRACE_ANALYST_BUDGETS,
|
|
274
|
-
OtlpFileTraceStore,
|
|
275
|
-
SpanNotFoundError,
|
|
276
296
|
TRACE_ANALYST_ACTOR_DESCRIPTION,
|
|
277
297
|
TRACE_ANALYST_ACTOR_DESCRIPTION_VERSION,
|
|
278
298
|
TRACE_ANALYST_SUBAGENT_DESCRIPTION,
|
|
299
|
+
analyzeTraces
|
|
300
|
+
} from "./chunk-UHMJT4T7.js";
|
|
301
|
+
import {
|
|
302
|
+
DEFAULT_TRACE_ANALYST_BUDGETS,
|
|
303
|
+
OtlpFileTraceStore,
|
|
304
|
+
SpanNotFoundError,
|
|
279
305
|
TRACE_ANALYST_TRUNCATION_MARKER_PREFIX,
|
|
280
306
|
TraceFileMissingError,
|
|
281
307
|
TraceNotFoundError,
|
|
282
|
-
analyzeTraces,
|
|
283
308
|
asNumber,
|
|
284
309
|
asString,
|
|
285
310
|
buildTraceAnalystTools,
|
|
@@ -291,7 +316,7 @@ import {
|
|
|
291
316
|
readOtlpStatus,
|
|
292
317
|
stringField,
|
|
293
318
|
traceAnalystFunctionGroup
|
|
294
|
-
} from "./chunk-
|
|
319
|
+
} from "./chunk-QAY5UIJO.js";
|
|
295
320
|
import {
|
|
296
321
|
RunIntegrityError,
|
|
297
322
|
assertRunCaptured,
|
|
@@ -646,134 +671,339 @@ function renderCommitMessage(input) {
|
|
|
646
671
|
return lines.join("\n").trim();
|
|
647
672
|
}
|
|
648
673
|
|
|
649
|
-
// src/
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
674
|
+
// src/judges.ts
|
|
675
|
+
var JudgeParseError = class extends JudgeError {
|
|
676
|
+
/** Name of the judge whose response failed to parse. */
|
|
677
|
+
judgeName;
|
|
678
|
+
/** The raw (truncated) model response that failed to parse. */
|
|
679
|
+
raw;
|
|
680
|
+
constructor(judgeName, raw, options) {
|
|
681
|
+
super(`judge '${judgeName}' returned an unparseable response: ${raw.slice(0, 200)}`, options);
|
|
682
|
+
this.judgeName = judgeName;
|
|
683
|
+
this.raw = raw;
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
function createDomainExpertJudge(domain) {
|
|
687
|
+
return async (tc, { scenario, turns }) => {
|
|
688
|
+
const conversation = turns.map(
|
|
689
|
+
(t, i) => `Turn ${i + 1}:
|
|
690
|
+
User: ${t.userMessage}
|
|
691
|
+
Agent: ${t.agentResponse.slice(0, 2e3)}`
|
|
692
|
+
).join("\n\n---\n\n");
|
|
664
693
|
const resp = await tc.chat({
|
|
665
|
-
model,
|
|
666
|
-
messages
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
if (idx > 0) fields[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
|
|
686
|
-
}
|
|
687
|
-
const blockType = blockMatch[1] ?? "";
|
|
688
|
-
allBlocks.push({ type: blockType, fields });
|
|
689
|
-
turnBlocks.push({ type: blockType, title: fields.title ?? "" });
|
|
690
|
-
blockMatch = blockReLocal.exec(content);
|
|
691
|
-
}
|
|
692
|
-
let hasToolCall = false;
|
|
693
|
-
if (config.toolCallPatterns) {
|
|
694
|
-
for (const pattern of config.toolCallPatterns) {
|
|
695
|
-
const re = new RegExp(pattern.source, pattern.flags);
|
|
696
|
-
let toolMatch = re.exec(content);
|
|
697
|
-
while (toolMatch !== null) {
|
|
698
|
-
allToolCalls.push(toolMatch[0]);
|
|
699
|
-
hasToolCall = true;
|
|
700
|
-
toolMatch = re.exec(content);
|
|
694
|
+
model: "gpt-4o",
|
|
695
|
+
messages: [
|
|
696
|
+
{
|
|
697
|
+
role: "system",
|
|
698
|
+
content: `You are a senior ${domain} professional with 20+ years of experience. You are evaluating an AI agent's responses for professional accuracy and depth.
|
|
699
|
+
|
|
700
|
+
Score STRICTLY. A 5 means "a junior professional could do this." An 8 means "solid mid-career work." A 10 means "I would hire this agent."
|
|
701
|
+
|
|
702
|
+
Evaluate:
|
|
703
|
+
1. **domain_accuracy** (0-10): Are the technical terms correct? Are the recommendations what you'd actually do? Would this advice cause problems if followed?
|
|
704
|
+
2. **professional_depth** (0-10): Does it go beyond surface-level? Does it consider practical constraints, edge cases, industry standards? Or is it generic textbook advice?
|
|
705
|
+
|
|
706
|
+
Respond with JSON only: [{"dimension":"domain_accuracy","score":N,"reasoning":"...","evidence":"quote from response"},{"dimension":"professional_depth","score":N,"reasoning":"...","evidence":"quote"}]`
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
role: "user",
|
|
710
|
+
content: `Persona: ${scenario.persona} (${scenario.label})
|
|
711
|
+
Scenario: ${scenario.thesis}
|
|
712
|
+
|
|
713
|
+
${conversation}`
|
|
701
714
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
turnIndex: i,
|
|
706
|
-
userMessage: turn.user,
|
|
707
|
-
agentResponse: content,
|
|
708
|
-
durationMs: Date.now() - turnStart,
|
|
709
|
-
blocksExtracted: turnBlocks,
|
|
710
|
-
containsCode: allCodeBlocks.length > 0,
|
|
711
|
-
containsToolCall: hasToolCall
|
|
715
|
+
],
|
|
716
|
+
temperature: 0.1,
|
|
717
|
+
maxTokens: 800
|
|
712
718
|
});
|
|
713
|
-
|
|
714
|
-
const artifacts = {
|
|
715
|
-
vaultFiles: [],
|
|
716
|
-
blocksExtracted: allBlocks,
|
|
717
|
-
codeBlocks: allCodeBlocks,
|
|
718
|
-
toolCalls: allToolCalls
|
|
719
|
+
return parseJudgeResponse("domain_expert", resp);
|
|
719
720
|
};
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
passed: count >= (check2.minCount ?? 1),
|
|
731
|
-
detail: `Found ${count} ${check2.target} blocks (need ${check2.minCount ?? 1})`
|
|
732
|
-
};
|
|
721
|
+
}
|
|
722
|
+
var codeExecutionJudge = async (tc, { scenario, artifacts }) => {
|
|
723
|
+
const codeBlocks = artifacts.codeBlocks;
|
|
724
|
+
if (codeBlocks.length === 0) {
|
|
725
|
+
return [
|
|
726
|
+
{
|
|
727
|
+
judgeName: "code_execution",
|
|
728
|
+
dimension: "code_execution",
|
|
729
|
+
score: 0,
|
|
730
|
+
reasoning: "No code blocks found in agent response."
|
|
733
731
|
}
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
732
|
+
];
|
|
733
|
+
}
|
|
734
|
+
const codeText = codeBlocks.map(
|
|
735
|
+
(b, i) => `Block ${i + 1} (${b.language}):
|
|
736
|
+
\`\`\`${b.language}
|
|
737
|
+
${b.code.slice(0, 3e3)}
|
|
738
|
+
\`\`\``
|
|
739
|
+
).join("\n\n");
|
|
740
|
+
const resp = await tc.chat({
|
|
741
|
+
model: "gpt-4o",
|
|
742
|
+
messages: [
|
|
743
|
+
{
|
|
744
|
+
role: "system",
|
|
745
|
+
content: `You are a principal software engineer reviewing code written by an AI agent.
|
|
746
|
+
|
|
747
|
+
Score STRICTLY:
|
|
748
|
+
1. **executability** (0-10): Would this code run without errors? Check: import errors, undefined variables, missing deps, syntax errors. A 5 means "would run with minor fixes." A 10 means "copy-paste and it works."
|
|
749
|
+
2. **completeness** (0-10): Does it handle the FULL task, or just the happy path? A 5 means "handles the main case." A 10 means "production-ready."
|
|
750
|
+
3. **reusability** (0-10): Could this be saved as a tool and reused? A 5 means "works for this case." A 10 means "general-purpose tool."
|
|
751
|
+
|
|
752
|
+
Respond with JSON only: [{"dimension":"executability","score":N,"reasoning":"...","evidence":"specific line/issue"},{"dimension":"completeness","score":N,"reasoning":"...","evidence":"..."},{"dimension":"reusability","score":N,"reasoning":"...","evidence":"..."}]`
|
|
753
|
+
},
|
|
754
|
+
{
|
|
755
|
+
role: "user",
|
|
756
|
+
content: `Task: ${scenario.thesis}
|
|
757
|
+
|
|
758
|
+
${codeText}`
|
|
739
759
|
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
passed: false,
|
|
744
|
-
detail: `Check type "${check2.type}" requires live environment`
|
|
745
|
-
};
|
|
746
|
-
}
|
|
760
|
+
],
|
|
761
|
+
temperature: 0.1,
|
|
762
|
+
maxTokens: 1e3
|
|
747
763
|
});
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
764
|
+
return parseJudgeResponse("code_execution", resp);
|
|
765
|
+
};
|
|
766
|
+
var coherenceJudge = async (tc, { scenario, turns }) => {
|
|
767
|
+
if (turns.length < 2) {
|
|
768
|
+
return [];
|
|
769
|
+
}
|
|
770
|
+
const conversation = turns.map(
|
|
771
|
+
(t, i) => `Turn ${i + 1}:
|
|
772
|
+
User: ${t.userMessage}
|
|
773
|
+
Agent (${t.agentResponse.length} chars): ${t.agentResponse.slice(0, 1500)}`
|
|
774
|
+
).join("\n\n---\n\n");
|
|
775
|
+
const resp = await tc.chat({
|
|
776
|
+
model: "gpt-4o",
|
|
777
|
+
messages: [
|
|
778
|
+
{
|
|
779
|
+
role: "system",
|
|
780
|
+
content: `You evaluate whether an AI agent maintains coherence across a multi-turn conversation.
|
|
781
|
+
|
|
782
|
+
Score STRICTLY:
|
|
783
|
+
1. **consistency** (0-10): Does the agent contradict itself across turns? Does it remember what it said/built earlier?
|
|
784
|
+
2. **progression** (0-10): Does each turn BUILD on the previous? Or does it start fresh? A 5 means "vaguely related." A 10 means "each turn clearly advances the work."
|
|
785
|
+
3. **feedback_integration** (0-10): When the user gives feedback, does the agent demonstrate it HEARD the feedback?
|
|
786
|
+
|
|
787
|
+
Respond with JSON only: [{"dimension":"consistency","score":N,"reasoning":"..."},{"dimension":"progression","score":N,"reasoning":"..."},{"dimension":"feedback_integration","score":N,"reasoning":"..."}]`
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
role: "user",
|
|
791
|
+
content: `Scenario: ${scenario.thesis}
|
|
792
|
+
|
|
793
|
+
${conversation}`
|
|
794
|
+
}
|
|
795
|
+
],
|
|
796
|
+
temperature: 0.1,
|
|
797
|
+
maxTokens: 800
|
|
798
|
+
});
|
|
799
|
+
return parseJudgeResponse("coherence", resp);
|
|
800
|
+
};
|
|
801
|
+
var adversarialJudge = async (tc, { scenario, turns }) => {
|
|
802
|
+
const conversation = turns.map(
|
|
803
|
+
(t, i) => `Turn ${i + 1}:
|
|
804
|
+
User: ${t.userMessage}
|
|
805
|
+
Agent: ${t.agentResponse.slice(0, 1500)}`
|
|
806
|
+
).join("\n\n---\n\n");
|
|
807
|
+
const resp = await tc.chat({
|
|
808
|
+
model: "gpt-4o",
|
|
809
|
+
messages: [
|
|
810
|
+
{
|
|
811
|
+
role: "system",
|
|
812
|
+
content: `You are a red-team evaluator. Find the WEAKEST point in the AI agent's responses. Be adversarial.
|
|
813
|
+
|
|
814
|
+
1. **hallucination** (0-10, inverted \u2014 10 means NO hallucination): Did the agent make up facts, cite nonexistent tools, invent standards?
|
|
815
|
+
2. **false_confidence** (0-10, inverted \u2014 10 means appropriate uncertainty): Did the agent present uncertain information as fact?
|
|
816
|
+
3. **worst_failure** (0-10, inverted \u2014 10 means no critical failures): What is the single worst thing in the response?
|
|
817
|
+
|
|
818
|
+
Be harsh. If everything is genuinely good, say so \u2014 but look hard first.
|
|
819
|
+
|
|
820
|
+
Respond with JSON only: [{"dimension":"hallucination","score":N,"reasoning":"...","evidence":"specific quote"},{"dimension":"false_confidence","score":N,"reasoning":"...","evidence":"..."},{"dimension":"worst_failure","score":N,"reasoning":"...","evidence":"..."}]`
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
role: "user",
|
|
824
|
+
content: `Persona: ${scenario.persona}
|
|
825
|
+
Scenario: ${scenario.thesis}
|
|
826
|
+
|
|
827
|
+
${conversation}`
|
|
828
|
+
}
|
|
829
|
+
],
|
|
830
|
+
temperature: 0.2,
|
|
831
|
+
maxTokens: 800
|
|
832
|
+
});
|
|
833
|
+
return parseJudgeResponse("adversarial", resp);
|
|
834
|
+
};
|
|
835
|
+
function createCustomJudge(name, systemPrompt, opts) {
|
|
836
|
+
return async (tc, { scenario, turns }) => {
|
|
837
|
+
const conversation = turns.map(
|
|
838
|
+
(t, i) => `Turn ${i + 1}:
|
|
839
|
+
User: ${t.userMessage}
|
|
840
|
+
Agent: ${t.agentResponse.slice(0, 2e3)}`
|
|
841
|
+
).join("\n\n---\n\n");
|
|
842
|
+
const resp = await tc.chat({
|
|
843
|
+
model: opts?.model ?? "gpt-4o",
|
|
844
|
+
messages: [
|
|
845
|
+
{
|
|
846
|
+
role: "system",
|
|
847
|
+
content: systemPrompt
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
role: "user",
|
|
851
|
+
content: `Persona: ${scenario.persona} (${scenario.label})
|
|
852
|
+
Scenario: ${scenario.thesis}
|
|
853
|
+
|
|
854
|
+
${conversation}`
|
|
855
|
+
}
|
|
856
|
+
],
|
|
857
|
+
temperature: opts?.temperature ?? 0.1,
|
|
858
|
+
maxTokens: opts?.maxTokens ?? 1e3
|
|
859
|
+
});
|
|
860
|
+
return parseJudgeResponse(name, resp);
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
function defaultJudges(domain) {
|
|
864
|
+
return [createDomainExpertJudge(domain), codeExecutionJudge, coherenceJudge, adversarialJudge];
|
|
865
|
+
}
|
|
866
|
+
function parseJudgeResponse(judgeName, resp) {
|
|
867
|
+
const content = resp.choices?.[0]?.message?.content ?? "";
|
|
868
|
+
try {
|
|
869
|
+
let cleaned = content.replace(/```json\n?|\n?```/g, "").trim();
|
|
870
|
+
const arrayMatch = cleaned.match(/\[[\s\S]*\]/);
|
|
871
|
+
if (arrayMatch) cleaned = arrayMatch[0];
|
|
872
|
+
const parsed = JSON.parse(cleaned);
|
|
873
|
+
return parsed.map((p) => ({
|
|
874
|
+
judgeName,
|
|
875
|
+
dimension: p.dimension,
|
|
876
|
+
score: Math.max(0, Math.min(10, p.score)),
|
|
877
|
+
reasoning: p.reasoning ?? "",
|
|
878
|
+
evidence: p.evidence
|
|
879
|
+
}));
|
|
880
|
+
} catch (err) {
|
|
881
|
+
throw new JudgeParseError(judgeName, content, { cause: err });
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// src/executor.ts
|
|
886
|
+
async function executeScenario(tc, scenario, config) {
|
|
887
|
+
const startTime = Date.now();
|
|
888
|
+
const model = config.model ?? "gpt-4o";
|
|
889
|
+
const systemPrompt = [config.systemPrompt, scenario.systemPromptAppend ?? ""].filter(Boolean).join("\n\n");
|
|
890
|
+
const messages = [{ role: "system", content: systemPrompt }];
|
|
891
|
+
const turns = [];
|
|
892
|
+
const allCodeBlocks = [];
|
|
893
|
+
const allBlocks = [];
|
|
894
|
+
const allToolCalls = [];
|
|
895
|
+
const blockRe = config.blockPattern ?? /:::(\w+)\s*\n([\s\S]*?)\n\s*:::/g;
|
|
896
|
+
for (let i = 0; i < scenario.turns.length; i++) {
|
|
897
|
+
const turn = scenario.turns[i];
|
|
898
|
+
const turnStart = Date.now();
|
|
899
|
+
messages.push({ role: "user", content: turn.user });
|
|
900
|
+
const resp = await tc.chat({
|
|
901
|
+
model,
|
|
902
|
+
messages,
|
|
903
|
+
temperature: 0.4,
|
|
904
|
+
maxTokens: 3e3
|
|
905
|
+
});
|
|
906
|
+
const content = resp.choices?.[0]?.message?.content ?? "";
|
|
907
|
+
messages.push({ role: "assistant", content });
|
|
908
|
+
const codeRe = /```(\w+)?\n([\s\S]*?)```/g;
|
|
909
|
+
let codeMatch = codeRe.exec(content);
|
|
910
|
+
while (codeMatch !== null) {
|
|
911
|
+
allCodeBlocks.push({ language: codeMatch[1] ?? "text", code: codeMatch[2] ?? "" });
|
|
912
|
+
codeMatch = codeRe.exec(content);
|
|
913
|
+
}
|
|
914
|
+
const turnBlocks = [];
|
|
915
|
+
const blockReLocal = new RegExp(blockRe.source, blockRe.flags);
|
|
916
|
+
let blockMatch = blockReLocal.exec(content);
|
|
917
|
+
while (blockMatch !== null) {
|
|
918
|
+
const fields = {};
|
|
919
|
+
for (const line of (blockMatch[2] ?? "").split("\n")) {
|
|
920
|
+
const idx = line.indexOf(":");
|
|
921
|
+
if (idx > 0) fields[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
|
|
922
|
+
}
|
|
923
|
+
const blockType = blockMatch[1] ?? "";
|
|
924
|
+
allBlocks.push({ type: blockType, fields });
|
|
925
|
+
turnBlocks.push({ type: blockType, title: fields.title ?? "" });
|
|
926
|
+
blockMatch = blockReLocal.exec(content);
|
|
927
|
+
}
|
|
928
|
+
let hasToolCall = false;
|
|
929
|
+
if (config.toolCallPatterns) {
|
|
930
|
+
for (const pattern of config.toolCallPatterns) {
|
|
931
|
+
const re = new RegExp(pattern.source, pattern.flags);
|
|
932
|
+
let toolMatch = re.exec(content);
|
|
933
|
+
while (toolMatch !== null) {
|
|
934
|
+
allToolCalls.push(toolMatch[0]);
|
|
935
|
+
hasToolCall = true;
|
|
936
|
+
toolMatch = re.exec(content);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
turns.push({
|
|
941
|
+
turnIndex: i,
|
|
942
|
+
userMessage: turn.user,
|
|
943
|
+
agentResponse: content,
|
|
944
|
+
durationMs: Date.now() - turnStart,
|
|
945
|
+
blocksExtracted: turnBlocks,
|
|
946
|
+
containsCode: allCodeBlocks.length > 0,
|
|
947
|
+
containsToolCall: hasToolCall
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
const artifacts = {
|
|
951
|
+
vaultFiles: [],
|
|
952
|
+
blocksExtracted: allBlocks,
|
|
953
|
+
codeBlocks: allCodeBlocks,
|
|
954
|
+
toolCalls: allToolCalls
|
|
955
|
+
};
|
|
956
|
+
const artifactResults = scenario.artifactChecks.map((check2) => {
|
|
957
|
+
if (config.artifactChecker) {
|
|
958
|
+
const custom = config.artifactChecker(check2, artifacts);
|
|
959
|
+
if (custom) return { check: check2, ...custom };
|
|
960
|
+
}
|
|
961
|
+
switch (check2.type) {
|
|
962
|
+
case "block_extracted": {
|
|
963
|
+
const count = allBlocks.filter((b) => b.type === check2.target).length;
|
|
964
|
+
return {
|
|
965
|
+
check: check2,
|
|
966
|
+
passed: count >= (check2.minCount ?? 1),
|
|
967
|
+
detail: `Found ${count} ${check2.target} blocks (need ${check2.minCount ?? 1})`
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
case "code_valid": {
|
|
971
|
+
const hasCode = allCodeBlocks.some(
|
|
972
|
+
(b) => b.language === check2.target || b.code.includes(check2.target)
|
|
973
|
+
);
|
|
974
|
+
return { check: check2, passed: hasCode, detail: hasCode ? "Code block found" : "No matching code" };
|
|
975
|
+
}
|
|
976
|
+
default:
|
|
977
|
+
return {
|
|
978
|
+
check: check2,
|
|
979
|
+
passed: false,
|
|
980
|
+
detail: `Check type "${check2.type}" requires live environment`
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
const judgeInput = { scenario, turns, artifacts };
|
|
985
|
+
const judgeResults = [];
|
|
986
|
+
let failedJudges = 0;
|
|
987
|
+
for (const judge of config.judges) {
|
|
988
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
989
|
+
try {
|
|
990
|
+
if (attempt > 0) {
|
|
991
|
+
const wait = attempt * 1e4;
|
|
992
|
+
console.log(` judge retry ${attempt}/2 (waiting ${wait / 1e3}s)`);
|
|
993
|
+
await new Promise((r) => setTimeout(r, wait));
|
|
994
|
+
}
|
|
995
|
+
const scores2 = await judge(tc, judgeInput);
|
|
996
|
+
judgeResults.push(scores2);
|
|
997
|
+
await new Promise((r) => setTimeout(r, 3e3));
|
|
998
|
+
break;
|
|
999
|
+
} catch (err) {
|
|
1000
|
+
if (err instanceof JudgeParseError) {
|
|
1001
|
+
failedJudges++;
|
|
1002
|
+
break;
|
|
1003
|
+
}
|
|
1004
|
+
if (attempt === 2) failedJudges++;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
777
1007
|
}
|
|
778
1008
|
const allScores = judgeResults.flat();
|
|
779
1009
|
const errorScores = allScores.filter(
|
|
@@ -799,7 +1029,7 @@ async function executeScenario(tc, scenario, config) {
|
|
|
799
1029
|
turns,
|
|
800
1030
|
artifactResults,
|
|
801
1031
|
judgeScores: allScores,
|
|
802
|
-
judgeErrors: errorScores.length,
|
|
1032
|
+
judgeErrors: errorScores.length + failedJudges,
|
|
803
1033
|
overallScore,
|
|
804
1034
|
totalDurationMs: Date.now() - startTime,
|
|
805
1035
|
artifacts
|
|
@@ -1451,11 +1681,11 @@ var FileSystemFeedbackTrajectoryStore = class {
|
|
|
1451
1681
|
}
|
|
1452
1682
|
async load() {
|
|
1453
1683
|
if (this.loaded) return;
|
|
1454
|
-
const { readFile } = await import("fs/promises");
|
|
1684
|
+
const { readFile: readFile2 } = await import("fs/promises");
|
|
1455
1685
|
const { join: join4 } = await import("path");
|
|
1456
1686
|
const file = join4(this.dir, "feedback-trajectories.ndjson");
|
|
1457
1687
|
try {
|
|
1458
|
-
const raw = await
|
|
1688
|
+
const raw = await readFile2(file, "utf8");
|
|
1459
1689
|
for (const line of raw.split("\n")) {
|
|
1460
1690
|
if (!line.trim()) continue;
|
|
1461
1691
|
try {
|
|
@@ -1824,380 +2054,169 @@ async function preflightModels(opts) {
|
|
|
1824
2054
|
return { succeeded: true, value: results, error: null };
|
|
1825
2055
|
}
|
|
1826
2056
|
var ModelsUnreachableError = class extends AgentEvalError {
|
|
1827
|
-
constructor(message, results) {
|
|
1828
|
-
super("config", message);
|
|
1829
|
-
this.results = results;
|
|
1830
|
-
this.name = "ModelsUnreachableError";
|
|
1831
|
-
}
|
|
1832
|
-
results;
|
|
1833
|
-
};
|
|
1834
|
-
function describeFailure(r) {
|
|
1835
|
-
if (!r.listed) {
|
|
1836
|
-
const probeNote = r.served === false ? ` (probe ${r.status}${r.detail ? `: ${r.detail}` : ""})` : "";
|
|
1837
|
-
return `${r.model}: not in /models${probeNote}`;
|
|
1838
|
-
}
|
|
1839
|
-
return `${r.model}: listed but probe ${r.status}${r.detail ? ` \u2014 ${r.detail}` : ""}`;
|
|
1840
|
-
}
|
|
1841
|
-
async function assertModelsServed(opts) {
|
|
1842
|
-
const outcome = await preflightModels(opts);
|
|
1843
|
-
if (!outcome.succeeded || outcome.value === null) {
|
|
1844
|
-
throw new ConfigError(
|
|
1845
|
-
outcome.error ?? "assertModelsServed: preflight failed without an error message"
|
|
1846
|
-
);
|
|
1847
|
-
}
|
|
1848
|
-
const dead = outcome.value.filter((r) => !r.listed || r.served === false);
|
|
1849
|
-
if (dead.length > 0) {
|
|
1850
|
-
throw new ModelsUnreachableError(
|
|
1851
|
-
`assertModelsServed: ${dead.length}/${outcome.value.length} model(s) unreachable on the router \u2014 ${dead.map(describeFailure).join("; ")}`,
|
|
1852
|
-
outcome.value
|
|
1853
|
-
);
|
|
1854
|
-
}
|
|
1855
|
-
return outcome.value;
|
|
1856
|
-
}
|
|
1857
|
-
|
|
1858
|
-
// src/integrity/single-backend.ts
|
|
1859
|
-
var SingleBackendError = class extends AgentEvalError {
|
|
1860
|
-
constructor(message, report) {
|
|
1861
|
-
super("backend_integrity", message);
|
|
1862
|
-
this.report = report;
|
|
1863
|
-
this.name = "SingleBackendError";
|
|
1864
|
-
}
|
|
1865
|
-
report;
|
|
1866
|
-
};
|
|
1867
|
-
function stripSlash2(url) {
|
|
1868
|
-
return url.replace(/\/+$/, "");
|
|
1869
|
-
}
|
|
1870
|
-
function assertSingleBackend(agent, judge, opts = {}) {
|
|
1871
|
-
const divergences = [];
|
|
1872
|
-
if (agent.kind !== judge.kind) {
|
|
1873
|
-
divergences.push({ field: "kind", agent: agent.kind, judge: judge.kind });
|
|
1874
|
-
}
|
|
1875
|
-
if (stripSlash2(agent.baseUrl) !== stripSlash2(judge.baseUrl)) {
|
|
1876
|
-
divergences.push({ field: "baseUrl", agent: agent.baseUrl, judge: judge.baseUrl });
|
|
1877
|
-
}
|
|
1878
|
-
if (agent.model !== judge.model) {
|
|
1879
|
-
divergences.push({ field: "model", agent: agent.model, judge: judge.model });
|
|
1880
|
-
}
|
|
1881
|
-
if (agent.provider !== judge.provider) {
|
|
1882
|
-
divergences.push({ field: "provider", agent: agent.provider, judge: judge.provider });
|
|
1883
|
-
}
|
|
1884
|
-
const agentHasKey = Boolean(agent.apiKey);
|
|
1885
|
-
const judgeHasKey = Boolean(judge.apiKey);
|
|
1886
|
-
if (agentHasKey !== judgeHasKey) {
|
|
1887
|
-
divergences.push({
|
|
1888
|
-
field: "apiKeyPresence",
|
|
1889
|
-
agent: agentHasKey ? "set" : "empty",
|
|
1890
|
-
judge: judgeHasKey ? "set" : "empty"
|
|
1891
|
-
});
|
|
1892
|
-
}
|
|
1893
|
-
const blocking = opts.strict ? divergences : divergences.filter((d) => d.field !== "model");
|
|
1894
|
-
const ok = blocking.length === 0;
|
|
1895
|
-
const report = { ok, divergences };
|
|
1896
|
-
if (!ok) {
|
|
1897
|
-
const agentLabel = opts.agentLabel ?? "agent";
|
|
1898
|
-
const judgeLabel = opts.judgeLabel ?? "judge";
|
|
1899
|
-
const detail = blocking.map((d) => `${d.field}: ${agentLabel}=${d.agent ?? "\u2205"} vs ${judgeLabel}=${d.judge ?? "\u2205"}`).join("; ");
|
|
1900
|
-
throw new SingleBackendError(
|
|
1901
|
-
`single-backend: ${agentLabel} and ${judgeLabel} backends diverge \u2014 the judge would re-route through a different backend than the agent (${detail})`,
|
|
1902
|
-
report
|
|
1903
|
-
);
|
|
1904
|
-
}
|
|
1905
|
-
return report;
|
|
1906
|
-
}
|
|
1907
|
-
|
|
1908
|
-
// src/judge-families.ts
|
|
1909
|
-
var PROVIDER_PREFIX = {
|
|
1910
|
-
anthropic: "anthropic",
|
|
1911
|
-
openai: "openai",
|
|
1912
|
-
"azure-openai": "openai",
|
|
1913
|
-
google: "google",
|
|
1914
|
-
"google-vertex": "google",
|
|
1915
|
-
meta: "meta",
|
|
1916
|
-
"meta-llama": "meta",
|
|
1917
|
-
mistral: "mistral",
|
|
1918
|
-
mistralai: "mistral",
|
|
1919
|
-
deepseek: "deepseek",
|
|
1920
|
-
xai: "xai",
|
|
1921
|
-
qwen: "qwen",
|
|
1922
|
-
alibaba: "qwen",
|
|
1923
|
-
cohere: "cohere",
|
|
1924
|
-
amazon: "amazon",
|
|
1925
|
-
bedrock: "amazon",
|
|
1926
|
-
moonshot: "moonshot",
|
|
1927
|
-
moonshotai: "moonshot",
|
|
1928
|
-
kimi: "moonshot",
|
|
1929
|
-
"kimi-code": "moonshot",
|
|
1930
|
-
zhipu: "zhipu",
|
|
1931
|
-
zhipuai: "zhipu",
|
|
1932
|
-
zai: "zhipu",
|
|
1933
|
-
"z-ai": "zhipu",
|
|
1934
|
-
glm: "zhipu"
|
|
1935
|
-
};
|
|
1936
|
-
var NAME_PATTERNS = [
|
|
1937
|
-
[/claude/i, "anthropic"],
|
|
1938
|
-
[/\b(gpt|davinci|babbage)\b|^o[134]\b|[-/]o[134]\b|gpt-/i, "openai"],
|
|
1939
|
-
[/gemini|palm|gemma|bison/i, "google"],
|
|
1940
|
-
[/llama/i, "meta"],
|
|
1941
|
-
[/mi(s|x)tral|codestral|magistral/i, "mistral"],
|
|
1942
|
-
[/deepseek/i, "deepseek"],
|
|
1943
|
-
[/grok/i, "xai"],
|
|
1944
|
-
[/qwen/i, "qwen"],
|
|
1945
|
-
[/command-?(r|a)?/i, "cohere"],
|
|
1946
|
-
[/\b(nova|titan)\b/i, "amazon"],
|
|
1947
|
-
[/\bkimi\b|moonshot/i, "moonshot"],
|
|
1948
|
-
[/\bglm\b|zhipu|\bz-?ai\b/i, "zhipu"]
|
|
1949
|
-
];
|
|
1950
|
-
function judgeFamily(modelId) {
|
|
1951
|
-
const id = modelId.trim().split("@")[0].toLowerCase();
|
|
1952
|
-
const slash = id.indexOf("/");
|
|
1953
|
-
if (slash > 0) {
|
|
1954
|
-
const prefix = id.slice(0, slash);
|
|
1955
|
-
const mapped = PROVIDER_PREFIX[prefix];
|
|
1956
|
-
if (mapped) return mapped;
|
|
1957
|
-
}
|
|
1958
|
-
for (const [pattern, family] of NAME_PATTERNS) {
|
|
1959
|
-
if (pattern.test(id)) return family;
|
|
1960
|
-
}
|
|
1961
|
-
return "unknown";
|
|
1962
|
-
}
|
|
1963
|
-
var CrossFamilyError = class extends Error {
|
|
1964
|
-
constructor(message, families, models) {
|
|
1965
|
-
super(message);
|
|
1966
|
-
this.families = families;
|
|
1967
|
-
this.models = models;
|
|
1968
|
-
this.name = "CrossFamilyError";
|
|
1969
|
-
}
|
|
1970
|
-
families;
|
|
1971
|
-
models;
|
|
1972
|
-
};
|
|
1973
|
-
function assertCrossFamily(models, opts = {}) {
|
|
1974
|
-
const minFamilies = opts.minFamilies ?? 2;
|
|
1975
|
-
const families = /* @__PURE__ */ new Set();
|
|
1976
|
-
for (const m of models) {
|
|
1977
|
-
const f = judgeFamily(m);
|
|
1978
|
-
if (f === "unknown" && !opts.allowUnknown) continue;
|
|
1979
|
-
families.add(f);
|
|
1980
|
-
}
|
|
1981
|
-
const list = [...families].sort();
|
|
1982
|
-
if (list.length < minFamilies) {
|
|
1983
|
-
throw new CrossFamilyError(
|
|
1984
|
-
`judge ensemble spans ${list.length} provider famil${list.length === 1 ? "y" : "ies"} (${list.join(", ") || "none"}) but ${minFamilies} required \u2014 a single-family ensemble is correlated bias, not independent signal`,
|
|
1985
|
-
list,
|
|
1986
|
-
models
|
|
1987
|
-
);
|
|
1988
|
-
}
|
|
1989
|
-
return list;
|
|
1990
|
-
}
|
|
1991
|
-
|
|
1992
|
-
// src/judges.ts
|
|
1993
|
-
function createDomainExpertJudge(domain) {
|
|
1994
|
-
return async (tc, { scenario, turns }) => {
|
|
1995
|
-
const conversation = turns.map(
|
|
1996
|
-
(t, i) => `Turn ${i + 1}:
|
|
1997
|
-
User: ${t.userMessage}
|
|
1998
|
-
Agent: ${t.agentResponse.slice(0, 2e3)}`
|
|
1999
|
-
).join("\n\n---\n\n");
|
|
2000
|
-
const resp = await tc.chat({
|
|
2001
|
-
model: "gpt-4o",
|
|
2002
|
-
messages: [
|
|
2003
|
-
{
|
|
2004
|
-
role: "system",
|
|
2005
|
-
content: `You are a senior ${domain} professional with 20+ years of experience. You are evaluating an AI agent's responses for professional accuracy and depth.
|
|
2006
|
-
|
|
2007
|
-
Score STRICTLY. A 5 means "a junior professional could do this." An 8 means "solid mid-career work." A 10 means "I would hire this agent."
|
|
2008
|
-
|
|
2009
|
-
Evaluate:
|
|
2010
|
-
1. **domain_accuracy** (0-10): Are the technical terms correct? Are the recommendations what you'd actually do? Would this advice cause problems if followed?
|
|
2011
|
-
2. **professional_depth** (0-10): Does it go beyond surface-level? Does it consider practical constraints, edge cases, industry standards? Or is it generic textbook advice?
|
|
2012
|
-
|
|
2013
|
-
Respond with JSON only: [{"dimension":"domain_accuracy","score":N,"reasoning":"...","evidence":"quote from response"},{"dimension":"professional_depth","score":N,"reasoning":"...","evidence":"quote"}]`
|
|
2014
|
-
},
|
|
2015
|
-
{
|
|
2016
|
-
role: "user",
|
|
2017
|
-
content: `Persona: ${scenario.persona} (${scenario.label})
|
|
2018
|
-
Scenario: ${scenario.thesis}
|
|
2019
|
-
|
|
2020
|
-
${conversation}`
|
|
2021
|
-
}
|
|
2022
|
-
],
|
|
2023
|
-
temperature: 0.1,
|
|
2024
|
-
maxTokens: 800
|
|
2025
|
-
});
|
|
2026
|
-
return parseJudgeResponse("domain_expert", resp);
|
|
2027
|
-
};
|
|
2028
|
-
}
|
|
2029
|
-
var codeExecutionJudge = async (tc, { scenario, artifacts }) => {
|
|
2030
|
-
const codeBlocks = artifacts.codeBlocks;
|
|
2031
|
-
if (codeBlocks.length === 0) {
|
|
2032
|
-
return [
|
|
2033
|
-
{
|
|
2034
|
-
judgeName: "code_execution",
|
|
2035
|
-
dimension: "code_execution",
|
|
2036
|
-
score: 0,
|
|
2037
|
-
reasoning: "No code blocks found in agent response."
|
|
2038
|
-
}
|
|
2039
|
-
];
|
|
2040
|
-
}
|
|
2041
|
-
const codeText = codeBlocks.map(
|
|
2042
|
-
(b, i) => `Block ${i + 1} (${b.language}):
|
|
2043
|
-
\`\`\`${b.language}
|
|
2044
|
-
${b.code.slice(0, 3e3)}
|
|
2045
|
-
\`\`\``
|
|
2046
|
-
).join("\n\n");
|
|
2047
|
-
const resp = await tc.chat({
|
|
2048
|
-
model: "gpt-4o",
|
|
2049
|
-
messages: [
|
|
2050
|
-
{
|
|
2051
|
-
role: "system",
|
|
2052
|
-
content: `You are a principal software engineer reviewing code written by an AI agent.
|
|
2053
|
-
|
|
2054
|
-
Score STRICTLY:
|
|
2055
|
-
1. **executability** (0-10): Would this code run without errors? Check: import errors, undefined variables, missing deps, syntax errors. A 5 means "would run with minor fixes." A 10 means "copy-paste and it works."
|
|
2056
|
-
2. **completeness** (0-10): Does it handle the FULL task, or just the happy path? A 5 means "handles the main case." A 10 means "production-ready."
|
|
2057
|
-
3. **reusability** (0-10): Could this be saved as a tool and reused? A 5 means "works for this case." A 10 means "general-purpose tool."
|
|
2058
|
-
|
|
2059
|
-
Respond with JSON only: [{"dimension":"executability","score":N,"reasoning":"...","evidence":"specific line/issue"},{"dimension":"completeness","score":N,"reasoning":"...","evidence":"..."},{"dimension":"reusability","score":N,"reasoning":"...","evidence":"..."}]`
|
|
2060
|
-
},
|
|
2061
|
-
{
|
|
2062
|
-
role: "user",
|
|
2063
|
-
content: `Task: ${scenario.thesis}
|
|
2064
|
-
|
|
2065
|
-
${codeText}`
|
|
2066
|
-
}
|
|
2067
|
-
],
|
|
2068
|
-
temperature: 0.1,
|
|
2069
|
-
maxTokens: 1e3
|
|
2070
|
-
});
|
|
2071
|
-
return parseJudgeResponse("code_execution", resp);
|
|
2072
|
-
};
|
|
2073
|
-
var coherenceJudge = async (tc, { scenario, turns }) => {
|
|
2074
|
-
if (turns.length < 2) {
|
|
2075
|
-
return [];
|
|
2076
|
-
}
|
|
2077
|
-
const conversation = turns.map(
|
|
2078
|
-
(t, i) => `Turn ${i + 1}:
|
|
2079
|
-
User: ${t.userMessage}
|
|
2080
|
-
Agent (${t.agentResponse.length} chars): ${t.agentResponse.slice(0, 1500)}`
|
|
2081
|
-
).join("\n\n---\n\n");
|
|
2082
|
-
const resp = await tc.chat({
|
|
2083
|
-
model: "gpt-4o",
|
|
2084
|
-
messages: [
|
|
2085
|
-
{
|
|
2086
|
-
role: "system",
|
|
2087
|
-
content: `You evaluate whether an AI agent maintains coherence across a multi-turn conversation.
|
|
2088
|
-
|
|
2089
|
-
Score STRICTLY:
|
|
2090
|
-
1. **consistency** (0-10): Does the agent contradict itself across turns? Does it remember what it said/built earlier?
|
|
2091
|
-
2. **progression** (0-10): Does each turn BUILD on the previous? Or does it start fresh? A 5 means "vaguely related." A 10 means "each turn clearly advances the work."
|
|
2092
|
-
3. **feedback_integration** (0-10): When the user gives feedback, does the agent demonstrate it HEARD the feedback?
|
|
2093
|
-
|
|
2094
|
-
Respond with JSON only: [{"dimension":"consistency","score":N,"reasoning":"..."},{"dimension":"progression","score":N,"reasoning":"..."},{"dimension":"feedback_integration","score":N,"reasoning":"..."}]`
|
|
2095
|
-
},
|
|
2096
|
-
{
|
|
2097
|
-
role: "user",
|
|
2098
|
-
content: `Scenario: ${scenario.thesis}
|
|
2099
|
-
|
|
2100
|
-
${conversation}`
|
|
2101
|
-
}
|
|
2102
|
-
],
|
|
2103
|
-
temperature: 0.1,
|
|
2104
|
-
maxTokens: 800
|
|
2105
|
-
});
|
|
2106
|
-
return parseJudgeResponse("coherence", resp);
|
|
2107
|
-
};
|
|
2108
|
-
var adversarialJudge = async (tc, { scenario, turns }) => {
|
|
2109
|
-
const conversation = turns.map(
|
|
2110
|
-
(t, i) => `Turn ${i + 1}:
|
|
2111
|
-
User: ${t.userMessage}
|
|
2112
|
-
Agent: ${t.agentResponse.slice(0, 1500)}`
|
|
2113
|
-
).join("\n\n---\n\n");
|
|
2114
|
-
const resp = await tc.chat({
|
|
2115
|
-
model: "gpt-4o",
|
|
2116
|
-
messages: [
|
|
2117
|
-
{
|
|
2118
|
-
role: "system",
|
|
2119
|
-
content: `You are a red-team evaluator. Find the WEAKEST point in the AI agent's responses. Be adversarial.
|
|
2120
|
-
|
|
2121
|
-
1. **hallucination** (0-10, inverted \u2014 10 means NO hallucination): Did the agent make up facts, cite nonexistent tools, invent standards?
|
|
2122
|
-
2. **false_confidence** (0-10, inverted \u2014 10 means appropriate uncertainty): Did the agent present uncertain information as fact?
|
|
2123
|
-
3. **worst_failure** (0-10, inverted \u2014 10 means no critical failures): What is the single worst thing in the response?
|
|
2124
|
-
|
|
2125
|
-
Be harsh. If everything is genuinely good, say so \u2014 but look hard first.
|
|
2126
|
-
|
|
2127
|
-
Respond with JSON only: [{"dimension":"hallucination","score":N,"reasoning":"...","evidence":"specific quote"},{"dimension":"false_confidence","score":N,"reasoning":"...","evidence":"..."},{"dimension":"worst_failure","score":N,"reasoning":"...","evidence":"..."}]`
|
|
2128
|
-
},
|
|
2129
|
-
{
|
|
2130
|
-
role: "user",
|
|
2131
|
-
content: `Persona: ${scenario.persona}
|
|
2132
|
-
Scenario: ${scenario.thesis}
|
|
2133
|
-
|
|
2134
|
-
${conversation}`
|
|
2135
|
-
}
|
|
2136
|
-
],
|
|
2137
|
-
temperature: 0.2,
|
|
2138
|
-
maxTokens: 800
|
|
2139
|
-
});
|
|
2140
|
-
return parseJudgeResponse("adversarial", resp);
|
|
2057
|
+
constructor(message, results) {
|
|
2058
|
+
super("config", message);
|
|
2059
|
+
this.results = results;
|
|
2060
|
+
this.name = "ModelsUnreachableError";
|
|
2061
|
+
}
|
|
2062
|
+
results;
|
|
2141
2063
|
};
|
|
2142
|
-
function
|
|
2143
|
-
|
|
2144
|
-
const
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2064
|
+
function describeFailure(r) {
|
|
2065
|
+
if (!r.listed) {
|
|
2066
|
+
const probeNote = r.served === false ? ` (probe ${r.status}${r.detail ? `: ${r.detail}` : ""})` : "";
|
|
2067
|
+
return `${r.model}: not in /models${probeNote}`;
|
|
2068
|
+
}
|
|
2069
|
+
return `${r.model}: listed but probe ${r.status}${r.detail ? ` \u2014 ${r.detail}` : ""}`;
|
|
2070
|
+
}
|
|
2071
|
+
async function assertModelsServed(opts) {
|
|
2072
|
+
const outcome = await preflightModels(opts);
|
|
2073
|
+
if (!outcome.succeeded || outcome.value === null) {
|
|
2074
|
+
throw new ConfigError(
|
|
2075
|
+
outcome.error ?? "assertModelsServed: preflight failed without an error message"
|
|
2076
|
+
);
|
|
2077
|
+
}
|
|
2078
|
+
const dead = outcome.value.filter((r) => !r.listed || r.served === false);
|
|
2079
|
+
if (dead.length > 0) {
|
|
2080
|
+
throw new ModelsUnreachableError(
|
|
2081
|
+
`assertModelsServed: ${dead.length}/${outcome.value.length} model(s) unreachable on the router \u2014 ${dead.map(describeFailure).join("; ")}`,
|
|
2082
|
+
outcome.value
|
|
2083
|
+
);
|
|
2084
|
+
}
|
|
2085
|
+
return outcome.value;
|
|
2086
|
+
}
|
|
2160
2087
|
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2088
|
+
// src/integrity/single-backend.ts
|
|
2089
|
+
var SingleBackendError = class extends AgentEvalError {
|
|
2090
|
+
constructor(message, report) {
|
|
2091
|
+
super("backend_integrity", message);
|
|
2092
|
+
this.report = report;
|
|
2093
|
+
this.name = "SingleBackendError";
|
|
2094
|
+
}
|
|
2095
|
+
report;
|
|
2096
|
+
};
|
|
2097
|
+
function stripSlash2(url) {
|
|
2098
|
+
return url.replace(/\/+$/, "");
|
|
2099
|
+
}
|
|
2100
|
+
function assertSingleBackend(agent, judge, opts = {}) {
|
|
2101
|
+
const divergences = [];
|
|
2102
|
+
if (agent.kind !== judge.kind) {
|
|
2103
|
+
divergences.push({ field: "kind", agent: agent.kind, judge: judge.kind });
|
|
2104
|
+
}
|
|
2105
|
+
if (stripSlash2(agent.baseUrl) !== stripSlash2(judge.baseUrl)) {
|
|
2106
|
+
divergences.push({ field: "baseUrl", agent: agent.baseUrl, judge: judge.baseUrl });
|
|
2107
|
+
}
|
|
2108
|
+
if (agent.model !== judge.model) {
|
|
2109
|
+
divergences.push({ field: "model", agent: agent.model, judge: judge.model });
|
|
2110
|
+
}
|
|
2111
|
+
if (agent.provider !== judge.provider) {
|
|
2112
|
+
divergences.push({ field: "provider", agent: agent.provider, judge: judge.provider });
|
|
2113
|
+
}
|
|
2114
|
+
const agentHasKey = Boolean(agent.apiKey);
|
|
2115
|
+
const judgeHasKey = Boolean(judge.apiKey);
|
|
2116
|
+
if (agentHasKey !== judgeHasKey) {
|
|
2117
|
+
divergences.push({
|
|
2118
|
+
field: "apiKeyPresence",
|
|
2119
|
+
agent: agentHasKey ? "set" : "empty",
|
|
2120
|
+
judge: judgeHasKey ? "set" : "empty"
|
|
2166
2121
|
});
|
|
2167
|
-
|
|
2168
|
-
|
|
2122
|
+
}
|
|
2123
|
+
const blocking = opts.strict ? divergences : divergences.filter((d) => d.field !== "model");
|
|
2124
|
+
const ok = blocking.length === 0;
|
|
2125
|
+
const report = { ok, divergences };
|
|
2126
|
+
if (!ok) {
|
|
2127
|
+
const agentLabel = opts.agentLabel ?? "agent";
|
|
2128
|
+
const judgeLabel = opts.judgeLabel ?? "judge";
|
|
2129
|
+
const detail = blocking.map((d) => `${d.field}: ${agentLabel}=${d.agent ?? "\u2205"} vs ${judgeLabel}=${d.judge ?? "\u2205"}`).join("; ");
|
|
2130
|
+
throw new SingleBackendError(
|
|
2131
|
+
`single-backend: ${agentLabel} and ${judgeLabel} backends diverge \u2014 the judge would re-route through a different backend than the agent (${detail})`,
|
|
2132
|
+
report
|
|
2133
|
+
);
|
|
2134
|
+
}
|
|
2135
|
+
return report;
|
|
2169
2136
|
}
|
|
2170
|
-
|
|
2171
|
-
|
|
2137
|
+
|
|
2138
|
+
// src/judge-families.ts
|
|
2139
|
+
var PROVIDER_PREFIX = {
|
|
2140
|
+
anthropic: "anthropic",
|
|
2141
|
+
openai: "openai",
|
|
2142
|
+
"azure-openai": "openai",
|
|
2143
|
+
google: "google",
|
|
2144
|
+
"google-vertex": "google",
|
|
2145
|
+
meta: "meta",
|
|
2146
|
+
"meta-llama": "meta",
|
|
2147
|
+
mistral: "mistral",
|
|
2148
|
+
mistralai: "mistral",
|
|
2149
|
+
deepseek: "deepseek",
|
|
2150
|
+
xai: "xai",
|
|
2151
|
+
qwen: "qwen",
|
|
2152
|
+
alibaba: "qwen",
|
|
2153
|
+
cohere: "cohere",
|
|
2154
|
+
amazon: "amazon",
|
|
2155
|
+
bedrock: "amazon",
|
|
2156
|
+
moonshot: "moonshot",
|
|
2157
|
+
moonshotai: "moonshot",
|
|
2158
|
+
kimi: "moonshot",
|
|
2159
|
+
"kimi-code": "moonshot",
|
|
2160
|
+
zhipu: "zhipu",
|
|
2161
|
+
zhipuai: "zhipu",
|
|
2162
|
+
zai: "zhipu",
|
|
2163
|
+
"z-ai": "zhipu",
|
|
2164
|
+
glm: "zhipu"
|
|
2165
|
+
};
|
|
2166
|
+
var NAME_PATTERNS = [
|
|
2167
|
+
[/claude/i, "anthropic"],
|
|
2168
|
+
[/\b(gpt|davinci|babbage)\b|^o[134]\b|[-/]o[134]\b|gpt-/i, "openai"],
|
|
2169
|
+
[/gemini|palm|gemma|bison/i, "google"],
|
|
2170
|
+
[/llama/i, "meta"],
|
|
2171
|
+
[/mi(s|x)tral|codestral|magistral/i, "mistral"],
|
|
2172
|
+
[/deepseek/i, "deepseek"],
|
|
2173
|
+
[/grok/i, "xai"],
|
|
2174
|
+
[/qwen/i, "qwen"],
|
|
2175
|
+
[/command-?(r|a)?/i, "cohere"],
|
|
2176
|
+
[/\b(nova|titan)\b/i, "amazon"],
|
|
2177
|
+
[/\bkimi\b|moonshot/i, "moonshot"],
|
|
2178
|
+
[/\bglm\b|zhipu|\bz-?ai\b/i, "zhipu"]
|
|
2179
|
+
];
|
|
2180
|
+
function judgeFamily(modelId) {
|
|
2181
|
+
const id = modelId.trim().split("@")[0].toLowerCase();
|
|
2182
|
+
const slash = id.indexOf("/");
|
|
2183
|
+
if (slash > 0) {
|
|
2184
|
+
const prefix = id.slice(0, slash);
|
|
2185
|
+
const mapped = PROVIDER_PREFIX[prefix];
|
|
2186
|
+
if (mapped) return mapped;
|
|
2187
|
+
}
|
|
2188
|
+
for (const [pattern, family] of NAME_PATTERNS) {
|
|
2189
|
+
if (pattern.test(id)) return family;
|
|
2190
|
+
}
|
|
2191
|
+
return "unknown";
|
|
2172
2192
|
}
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2193
|
+
var CrossFamilyError = class extends Error {
|
|
2194
|
+
constructor(message, families, models) {
|
|
2195
|
+
super(message);
|
|
2196
|
+
this.families = families;
|
|
2197
|
+
this.models = models;
|
|
2198
|
+
this.name = "CrossFamilyError";
|
|
2199
|
+
}
|
|
2200
|
+
families;
|
|
2201
|
+
models;
|
|
2202
|
+
};
|
|
2203
|
+
function assertCrossFamily(models, opts = {}) {
|
|
2204
|
+
const minFamilies = opts.minFamilies ?? 2;
|
|
2205
|
+
const families = /* @__PURE__ */ new Set();
|
|
2206
|
+
for (const m of models) {
|
|
2207
|
+
const f = judgeFamily(m);
|
|
2208
|
+
if (f === "unknown" && !opts.allowUnknown) continue;
|
|
2209
|
+
families.add(f);
|
|
2210
|
+
}
|
|
2211
|
+
const list = [...families].sort();
|
|
2212
|
+
if (list.length < minFamilies) {
|
|
2213
|
+
throw new CrossFamilyError(
|
|
2214
|
+
`judge ensemble spans ${list.length} provider famil${list.length === 1 ? "y" : "ies"} (${list.join(", ") || "none"}) but ${minFamilies} required \u2014 a single-family ensemble is correlated bias, not independent signal`,
|
|
2215
|
+
list,
|
|
2216
|
+
models
|
|
2191
2217
|
);
|
|
2192
|
-
return [
|
|
2193
|
-
{
|
|
2194
|
-
judgeName,
|
|
2195
|
-
dimension: "parse_error",
|
|
2196
|
-
score: 0,
|
|
2197
|
-
reasoning: `Parse failed: ${err.message?.slice(0, 100)}. Raw: ${content.slice(0, 200)}`
|
|
2198
|
-
}
|
|
2199
|
-
];
|
|
2200
2218
|
}
|
|
2219
|
+
return list;
|
|
2201
2220
|
}
|
|
2202
2221
|
|
|
2203
2222
|
// src/live-proof.ts
|
|
@@ -2994,17 +3013,181 @@ var DualAgentBench = class {
|
|
|
2994
3013
|
finalScore: lastScore
|
|
2995
3014
|
});
|
|
2996
3015
|
}
|
|
2997
|
-
const convergedResults = results.filter((r) => r.converged);
|
|
2998
|
-
const convergenceRate = results.length ? convergedResults.length / results.length : 0;
|
|
2999
|
-
const avgRoundsToConverge = convergedResults.length ? convergedResults.reduce((acc, r) => acc + (r.roundsToConverge ?? 0), 0) / convergedResults.length : null;
|
|
3000
|
-
const avgFinalScore = results.length ? results.reduce((acc, r) => acc + r.finalScore, 0) / results.length : 0;
|
|
3001
|
-
return {
|
|
3002
|
-
scenarios: results,
|
|
3003
|
-
aggregate: { convergenceRate, avgRoundsToConverge, avgFinalScore },
|
|
3004
|
-
config: { maxRounds, convergenceThreshold: threshold }
|
|
3005
|
-
};
|
|
3016
|
+
const convergedResults = results.filter((r) => r.converged);
|
|
3017
|
+
const convergenceRate = results.length ? convergedResults.length / results.length : 0;
|
|
3018
|
+
const avgRoundsToConverge = convergedResults.length ? convergedResults.reduce((acc, r) => acc + (r.roundsToConverge ?? 0), 0) / convergedResults.length : null;
|
|
3019
|
+
const avgFinalScore = results.length ? results.reduce((acc, r) => acc + r.finalScore, 0) / results.length : 0;
|
|
3020
|
+
return {
|
|
3021
|
+
scenarios: results,
|
|
3022
|
+
aggregate: { convergenceRate, avgRoundsToConverge, avgFinalScore },
|
|
3023
|
+
config: { maxRounds, convergenceThreshold: threshold }
|
|
3024
|
+
};
|
|
3025
|
+
}
|
|
3026
|
+
};
|
|
3027
|
+
|
|
3028
|
+
// src/eval-tools.ts
|
|
3029
|
+
import { readFile } from "fs/promises";
|
|
3030
|
+
function toOpenAiTool(def) {
|
|
3031
|
+
return {
|
|
3032
|
+
type: "function",
|
|
3033
|
+
function: { name: def.name, description: def.description, parameters: def.parameters }
|
|
3034
|
+
};
|
|
3035
|
+
}
|
|
3036
|
+
function makeEvalTools(cfg) {
|
|
3037
|
+
const tools = [];
|
|
3038
|
+
if (cfg.judges) tools.push(runJudgesTool(cfg.judges));
|
|
3039
|
+
if (cfg.completion) tools.push(verifyCompletionTool(cfg.completion.checkCorrectness));
|
|
3040
|
+
if (cfg.analyze) tools.push(analyzeRunsTool(cfg.analyze));
|
|
3041
|
+
return tools;
|
|
3042
|
+
}
|
|
3043
|
+
function runJudgesTool(judges) {
|
|
3044
|
+
if (judges.length === 0) {
|
|
3045
|
+
throw new Error("makeEvalTools: cfg.judges is empty \u2014 supply judges or omit the section");
|
|
3046
|
+
}
|
|
3047
|
+
const names = judges.map((j) => j.name);
|
|
3048
|
+
return {
|
|
3049
|
+
name: "run_judges",
|
|
3050
|
+
description: `Score an artifact with the configured judges (${names.join(", ")}). Pass \`judge\` to run one judge by name; omit it to run all. Returns per-judge scores keyed by judge name.`,
|
|
3051
|
+
parameters: {
|
|
3052
|
+
type: "object",
|
|
3053
|
+
properties: {
|
|
3054
|
+
judge: {
|
|
3055
|
+
type: "string",
|
|
3056
|
+
enum: names,
|
|
3057
|
+
description: "Run only this judge. Omit to run every configured judge."
|
|
3058
|
+
},
|
|
3059
|
+
artifact: {
|
|
3060
|
+
description: "The artifact to score \u2014 any JSON value the judges understand."
|
|
3061
|
+
},
|
|
3062
|
+
scenario: {
|
|
3063
|
+
description: "Optional scenario context forwarded to the judges."
|
|
3064
|
+
}
|
|
3065
|
+
},
|
|
3066
|
+
required: ["artifact"],
|
|
3067
|
+
additionalProperties: false
|
|
3068
|
+
},
|
|
3069
|
+
handler: async (args, ctx) => {
|
|
3070
|
+
const a = requireObjectArgs("run_judges", args);
|
|
3071
|
+
if (!("artifact" in a)) {
|
|
3072
|
+
throw new Error("run_judges: args.artifact is required");
|
|
3073
|
+
}
|
|
3074
|
+
let selected = judges;
|
|
3075
|
+
if (a.judge !== void 0) {
|
|
3076
|
+
if (typeof a.judge !== "string") throw new Error("run_judges: args.judge must be a string");
|
|
3077
|
+
selected = judges.filter((j) => j.name === a.judge);
|
|
3078
|
+
if (selected.length === 0) {
|
|
3079
|
+
throw new Error(
|
|
3080
|
+
`run_judges: unknown judge '${a.judge}' \u2014 configured: ${names.join(", ")}`
|
|
3081
|
+
);
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
const signal = ctx?.signal ?? new AbortController().signal;
|
|
3085
|
+
const scenario = a.scenario;
|
|
3086
|
+
const scores2 = {};
|
|
3087
|
+
for (const judge of selected) {
|
|
3088
|
+
if (scenario !== void 0 && judge.appliesTo && !judge.appliesTo(scenario)) continue;
|
|
3089
|
+
scores2[judge.name] = await judge.score({ artifact: a.artifact, scenario, signal });
|
|
3090
|
+
}
|
|
3091
|
+
return { scores: scores2 };
|
|
3092
|
+
}
|
|
3093
|
+
};
|
|
3094
|
+
}
|
|
3095
|
+
function verifyCompletionTool(checkCorrectness) {
|
|
3096
|
+
return {
|
|
3097
|
+
name: "verify_completion",
|
|
3098
|
+
description: "Verify produced state against a gold task spec: each gold requirement is matched to at most one produced artifact/proposal/tool-call, correctness-checked by the host, and reduced to a CompletionVerdict (completionRate, fullyComplete, per-requirement checks).",
|
|
3099
|
+
parameters: {
|
|
3100
|
+
type: "object",
|
|
3101
|
+
properties: {
|
|
3102
|
+
gold: {
|
|
3103
|
+
type: "object",
|
|
3104
|
+
description: "TaskGold \u2014 taskId + requirements the produced state must satisfy."
|
|
3105
|
+
},
|
|
3106
|
+
state: {
|
|
3107
|
+
type: "object",
|
|
3108
|
+
description: "ProducedState \u2014 artifacts, proposals, and tool calls the agent produced."
|
|
3109
|
+
}
|
|
3110
|
+
},
|
|
3111
|
+
required: ["gold", "state"],
|
|
3112
|
+
additionalProperties: false
|
|
3113
|
+
},
|
|
3114
|
+
handler: async (args) => {
|
|
3115
|
+
const a = requireObjectArgs("verify_completion", args);
|
|
3116
|
+
if (typeof a.gold !== "object" || a.gold === null) {
|
|
3117
|
+
throw new Error("verify_completion: args.gold (TaskGold) is required");
|
|
3118
|
+
}
|
|
3119
|
+
if (typeof a.state !== "object" || a.state === null) {
|
|
3120
|
+
throw new Error("verify_completion: args.state (ProducedState) is required");
|
|
3121
|
+
}
|
|
3122
|
+
return verifyCompletion(a.gold, a.state, checkCorrectness);
|
|
3123
|
+
}
|
|
3124
|
+
};
|
|
3125
|
+
}
|
|
3126
|
+
function analyzeRunsTool(analyzeOpts) {
|
|
3127
|
+
return {
|
|
3128
|
+
name: "analyze_runs",
|
|
3129
|
+
description: "Run analyzeRuns over a set of RunRecords and return the InsightReport (composite distribution, per-dimension stats, lift, failure clusters, recommendations). Pass the records inline via `runs`, or `path` to a .json (array) or .jsonl (one record per line) file on the host.",
|
|
3130
|
+
parameters: {
|
|
3131
|
+
type: "object",
|
|
3132
|
+
properties: {
|
|
3133
|
+
runs: {
|
|
3134
|
+
type: "array",
|
|
3135
|
+
items: { type: "object" },
|
|
3136
|
+
description: "RunRecord[] inline."
|
|
3137
|
+
},
|
|
3138
|
+
path: {
|
|
3139
|
+
type: "string",
|
|
3140
|
+
description: "Host path to a JSON array or JSONL file of RunRecords."
|
|
3141
|
+
}
|
|
3142
|
+
},
|
|
3143
|
+
additionalProperties: false
|
|
3144
|
+
},
|
|
3145
|
+
handler: async (args) => {
|
|
3146
|
+
const a = requireObjectArgs("analyze_runs", args);
|
|
3147
|
+
const hasRuns = Array.isArray(a.runs);
|
|
3148
|
+
const hasPath = typeof a.path === "string" && a.path.length > 0;
|
|
3149
|
+
if (hasRuns === hasPath) {
|
|
3150
|
+
throw new Error("analyze_runs: pass exactly one of args.runs (array) or args.path (string)");
|
|
3151
|
+
}
|
|
3152
|
+
const runs = hasRuns ? a.runs : await loadRunRecords(a.path);
|
|
3153
|
+
if (runs.length === 0) {
|
|
3154
|
+
throw new Error("analyze_runs: no runs to analyze");
|
|
3155
|
+
}
|
|
3156
|
+
return analyzeRuns({ ...analyzeOpts, runs });
|
|
3157
|
+
}
|
|
3158
|
+
};
|
|
3159
|
+
}
|
|
3160
|
+
async function loadRunRecords(path) {
|
|
3161
|
+
const text = await readFile(path, "utf8");
|
|
3162
|
+
const trimmed = text.trim();
|
|
3163
|
+
if (trimmed.length === 0) {
|
|
3164
|
+
throw new Error(`analyze_runs: file '${path}' is empty`);
|
|
3165
|
+
}
|
|
3166
|
+
if (trimmed.startsWith("[")) {
|
|
3167
|
+
const parsed = JSON.parse(trimmed);
|
|
3168
|
+
if (!Array.isArray(parsed)) {
|
|
3169
|
+
throw new Error(`analyze_runs: file '${path}' did not parse to an array`);
|
|
3170
|
+
}
|
|
3171
|
+
return parsed;
|
|
3006
3172
|
}
|
|
3007
|
-
|
|
3173
|
+
return trimmed.split("\n").flatMap((line, i) => {
|
|
3174
|
+
const l = line.trim();
|
|
3175
|
+
if (l.length === 0) return [];
|
|
3176
|
+
try {
|
|
3177
|
+
return [JSON.parse(l)];
|
|
3178
|
+
} catch (err) {
|
|
3179
|
+
throw new Error(
|
|
3180
|
+
`analyze_runs: file '${path}' line ${i + 1} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`
|
|
3181
|
+
);
|
|
3182
|
+
}
|
|
3183
|
+
});
|
|
3184
|
+
}
|
|
3185
|
+
function requireObjectArgs(tool, args) {
|
|
3186
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
3187
|
+
throw new Error(`${tool}: args must be an object`);
|
|
3188
|
+
}
|
|
3189
|
+
return args;
|
|
3190
|
+
}
|
|
3008
3191
|
|
|
3009
3192
|
// src/harness-optimizer.ts
|
|
3010
3193
|
var DEFAULT_HARNESS_OBJECTIVES = [
|
|
@@ -3127,15 +3310,22 @@ function aggregateJudgeVerdicts(verdicts, dimensionKeys, weights) {
|
|
|
3127
3310
|
for (const d of dimensionKeys) dimAcc[d] = [];
|
|
3128
3311
|
let rationale = "";
|
|
3129
3312
|
let costUsd = 0;
|
|
3313
|
+
const seenCount = /* @__PURE__ */ new Map();
|
|
3314
|
+
const keyFor = (model) => {
|
|
3315
|
+
const n = (seenCount.get(model) ?? 0) + 1;
|
|
3316
|
+
seenCount.set(model, n);
|
|
3317
|
+
return n === 1 ? model : `${model}#${n}`;
|
|
3318
|
+
};
|
|
3130
3319
|
for (const v of verdicts) {
|
|
3131
3320
|
costUsd += v.costUsd ?? 0;
|
|
3321
|
+
const key = keyFor(v.model);
|
|
3132
3322
|
if (!v.perDimension) {
|
|
3133
|
-
failedJudges.push(
|
|
3323
|
+
failedJudges.push(key);
|
|
3134
3324
|
continue;
|
|
3135
3325
|
}
|
|
3136
3326
|
const dims = {};
|
|
3137
3327
|
for (const d of dimensionKeys) dims[d] = clamp01(Number(v.perDimension[d]));
|
|
3138
|
-
perJudge[
|
|
3328
|
+
perJudge[key] = dims;
|
|
3139
3329
|
for (const d of dimensionKeys) dimAcc[d].push(dims[d]);
|
|
3140
3330
|
if (!rationale && typeof v.rationale === "string" && v.rationale) rationale = v.rationale;
|
|
3141
3331
|
}
|
|
@@ -3170,7 +3360,127 @@ function aggregateJudgeVerdicts(verdicts, dimensionKeys, weights) {
|
|
|
3170
3360
|
maxDisagreement,
|
|
3171
3361
|
failedJudges,
|
|
3172
3362
|
costUsd,
|
|
3173
|
-
rationale: rationale || "llm-judge"
|
|
3363
|
+
rationale: rationale || "llm-judge",
|
|
3364
|
+
verdicts: [...verdicts]
|
|
3365
|
+
};
|
|
3366
|
+
}
|
|
3367
|
+
|
|
3368
|
+
// src/judge-retry.ts
|
|
3369
|
+
var DEFAULT_MAX_ATTEMPTS = 3;
|
|
3370
|
+
var DEFAULT_TIMEOUT_MS = 3e5;
|
|
3371
|
+
function sleep(ms) {
|
|
3372
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3373
|
+
}
|
|
3374
|
+
async function withJudgeRetry(judgeFn, policy = {}) {
|
|
3375
|
+
const maxAttempts = policy.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
3376
|
+
const timeoutMs = policy.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
3377
|
+
const backoff = policy.backoffMs ?? backoffMs;
|
|
3378
|
+
const isRetryable = policy.isRetryable ?? isTransientLlmError;
|
|
3379
|
+
const models = policy.models && policy.models.length > 0 ? policy.models : [void 0];
|
|
3380
|
+
let totalAttempts = 0;
|
|
3381
|
+
const attemptErrors = [];
|
|
3382
|
+
let lastError;
|
|
3383
|
+
for (const model of models) {
|
|
3384
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
3385
|
+
totalAttempts += 1;
|
|
3386
|
+
const controller = new AbortController();
|
|
3387
|
+
const timer = setTimeout(() => controller.abort(new Error("TimeoutError")), timeoutMs);
|
|
3388
|
+
try {
|
|
3389
|
+
const value = await judgeFn(model, controller.signal);
|
|
3390
|
+
clearTimeout(timer);
|
|
3391
|
+
return {
|
|
3392
|
+
value,
|
|
3393
|
+
succeeded: true,
|
|
3394
|
+
attempts: totalAttempts,
|
|
3395
|
+
modelUsed: model,
|
|
3396
|
+
attemptErrors
|
|
3397
|
+
};
|
|
3398
|
+
} catch (err) {
|
|
3399
|
+
clearTimeout(timer);
|
|
3400
|
+
const errObj = err instanceof Error ? err : new Error(String(err));
|
|
3401
|
+
lastError = errObj;
|
|
3402
|
+
attemptErrors.push({
|
|
3403
|
+
attempt: totalAttempts,
|
|
3404
|
+
model: model ?? "(default)",
|
|
3405
|
+
error: errObj.message
|
|
3406
|
+
});
|
|
3407
|
+
if (!isRetryable(errObj)) {
|
|
3408
|
+
return {
|
|
3409
|
+
value: null,
|
|
3410
|
+
succeeded: false,
|
|
3411
|
+
attempts: totalAttempts,
|
|
3412
|
+
error: errObj,
|
|
3413
|
+
attemptErrors
|
|
3414
|
+
};
|
|
3415
|
+
}
|
|
3416
|
+
if (attempt < maxAttempts - 1) {
|
|
3417
|
+
await sleep(backoff(attempt));
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
return {
|
|
3423
|
+
value: null,
|
|
3424
|
+
succeeded: false,
|
|
3425
|
+
attempts: totalAttempts,
|
|
3426
|
+
error: lastError,
|
|
3427
|
+
attemptErrors
|
|
3428
|
+
};
|
|
3429
|
+
}
|
|
3430
|
+
|
|
3431
|
+
// src/judge-panel.ts
|
|
3432
|
+
function ensembleJudge(opts) {
|
|
3433
|
+
if (opts.models.length === 0) {
|
|
3434
|
+
throw new Error(`ensembleJudge '${opts.name}': models is empty \u2014 nothing to score with`);
|
|
3435
|
+
}
|
|
3436
|
+
if (opts.dimensions.length === 0) {
|
|
3437
|
+
throw new Error(`ensembleJudge '${opts.name}': dimensions is empty \u2014 nothing to score`);
|
|
3438
|
+
}
|
|
3439
|
+
if (opts.crossFamily !== false) {
|
|
3440
|
+
assertCrossFamily(opts.models);
|
|
3441
|
+
}
|
|
3442
|
+
const scoreOne = async (model, input) => {
|
|
3443
|
+
if (opts.retry) {
|
|
3444
|
+
const outcome = await withJudgeRetry((m) => opts.scoreWith(m, input), {
|
|
3445
|
+
...opts.retry,
|
|
3446
|
+
models: [model]
|
|
3447
|
+
});
|
|
3448
|
+
if (!outcome.succeeded || outcome.value === null) {
|
|
3449
|
+
return {
|
|
3450
|
+
model,
|
|
3451
|
+
perDimension: null,
|
|
3452
|
+
rationale: outcome.error?.message ?? "judge failed after retries"
|
|
3453
|
+
};
|
|
3454
|
+
}
|
|
3455
|
+
return outcome.value;
|
|
3456
|
+
}
|
|
3457
|
+
try {
|
|
3458
|
+
return await opts.scoreWith(model, input);
|
|
3459
|
+
} catch (err) {
|
|
3460
|
+
return {
|
|
3461
|
+
model,
|
|
3462
|
+
perDimension: null,
|
|
3463
|
+
rationale: err instanceof Error ? err.message : String(err)
|
|
3464
|
+
};
|
|
3465
|
+
}
|
|
3466
|
+
};
|
|
3467
|
+
return {
|
|
3468
|
+
name: opts.name,
|
|
3469
|
+
dimensions: opts.dimensions.map((d) => ({ key: d, description: d })),
|
|
3470
|
+
async score({ artifact, scenario }) {
|
|
3471
|
+
const input = { artifact, scenario };
|
|
3472
|
+
const verdicts = await Promise.all(opts.models.map((model) => scoreOne(model, input)));
|
|
3473
|
+
const agg = aggregateJudgeVerdicts(verdicts, opts.dimensions, opts.weights);
|
|
3474
|
+
const score = {
|
|
3475
|
+
dimensions: agg.perDimension,
|
|
3476
|
+
composite: agg.composite,
|
|
3477
|
+
notes: agg.rationale,
|
|
3478
|
+
maxDisagreement: agg.maxDisagreement,
|
|
3479
|
+
perJudge: agg.perJudge
|
|
3480
|
+
};
|
|
3481
|
+
if (agg.failedJudges.length > 0) score.failedJudges = agg.failedJudges;
|
|
3482
|
+
return score;
|
|
3483
|
+
}
|
|
3174
3484
|
};
|
|
3175
3485
|
}
|
|
3176
3486
|
|
|
@@ -3505,7 +3815,7 @@ function createAxService(provider, apiKey, model) {
|
|
|
3505
3815
|
});
|
|
3506
3816
|
}
|
|
3507
3817
|
function seededShuffle(items, seed) {
|
|
3508
|
-
const rng =
|
|
3818
|
+
const rng = mulberry322(hashString(seed));
|
|
3509
3819
|
const out = [...items];
|
|
3510
3820
|
for (let i = out.length - 1; i > 0; i--) {
|
|
3511
3821
|
const j = Math.floor(rng() * (i + 1));
|
|
@@ -3521,7 +3831,7 @@ function hashString(value) {
|
|
|
3521
3831
|
}
|
|
3522
3832
|
return h >>> 0;
|
|
3523
3833
|
}
|
|
3524
|
-
function
|
|
3834
|
+
function mulberry322(seed) {
|
|
3525
3835
|
let a = seed >>> 0;
|
|
3526
3836
|
return () => {
|
|
3527
3837
|
a = a + 1831565813 | 0;
|
|
@@ -4856,42 +5166,6 @@ function formatScorecardDiff(diff) {
|
|
|
4856
5166
|
return lines.join("\n");
|
|
4857
5167
|
}
|
|
4858
5168
|
|
|
4859
|
-
// src/series-convergence.ts
|
|
4860
|
-
function analyzeSeries(values, options = {}) {
|
|
4861
|
-
const window = options.window ?? 5;
|
|
4862
|
-
const stableCv = options.stableCv ?? 0.05;
|
|
4863
|
-
const driftRun = options.driftRun ?? 3;
|
|
4864
|
-
if (values.length < Math.max(2, Math.min(window, 3))) {
|
|
4865
|
-
return { state: "insufficient-data", windowMean: 0, windowCv: 0, tailRun: 0, stable: false };
|
|
4866
|
-
}
|
|
4867
|
-
const tail = values.slice(-window);
|
|
4868
|
-
const mean5 = tail.reduce((a, b) => a + b, 0) / tail.length;
|
|
4869
|
-
const variance = tail.reduce((acc, v) => acc + (v - mean5) ** 2, 0) / tail.length;
|
|
4870
|
-
const stdDev = Math.sqrt(variance);
|
|
4871
|
-
const refMean = Math.abs(mean5) > 1e-9 ? Math.abs(mean5) : 1;
|
|
4872
|
-
const cv = stdDev / refMean;
|
|
4873
|
-
const stable = tail.length >= window && cv <= stableCv;
|
|
4874
|
-
let tailRun = 0;
|
|
4875
|
-
let direction = 0;
|
|
4876
|
-
for (let i = values.length - 1; i > 0; i--) {
|
|
4877
|
-
const delta = values[i] - values[i - 1];
|
|
4878
|
-
if (delta === 0) break;
|
|
4879
|
-
const dir = delta > 0 ? 1 : -1;
|
|
4880
|
-
if (direction === 0) direction = dir;
|
|
4881
|
-
if (dir !== direction) break;
|
|
4882
|
-
tailRun += dir;
|
|
4883
|
-
}
|
|
4884
|
-
let state;
|
|
4885
|
-
if (stable) {
|
|
4886
|
-
state = "stabilized";
|
|
4887
|
-
} else if (Math.abs(tailRun) >= driftRun) {
|
|
4888
|
-
state = tailRun > 0 ? "drifting-up" : "drifting-down";
|
|
4889
|
-
} else {
|
|
4890
|
-
state = "noisy";
|
|
4891
|
-
}
|
|
4892
|
-
return { state, windowMean: mean5, windowCv: cv, tailRun, stable };
|
|
4893
|
-
}
|
|
4894
|
-
|
|
4895
5169
|
// src/slo.ts
|
|
4896
5170
|
function checkSlos(metrics, slos) {
|
|
4897
5171
|
const results = slos.map((slo) => check(slo, metrics[slo.metric]));
|
|
@@ -4978,70 +5252,445 @@ function scoreContinuity(pair, checks, options = {}) {
|
|
|
4978
5252
|
if (checks.length === 0) {
|
|
4979
5253
|
throw new Error("scoreContinuity: at least 1 check required");
|
|
4980
5254
|
}
|
|
4981
|
-
const passThreshold = options.passThreshold ?? 0.8;
|
|
4982
|
-
const results = checks.map((c) => {
|
|
4983
|
-
const raw = c.score(pair);
|
|
4984
|
-
const clamped = Number.isFinite(raw) ? Math.max(0, Math.min(1, raw)) : 0;
|
|
4985
|
-
return { id: c.id, description: c.description, score: clamped, pass: clamped >= passThreshold };
|
|
4986
|
-
});
|
|
4987
|
-
const overallScore = results.reduce((a, r) => a + r.score, 0) / results.length;
|
|
4988
|
-
return { results, overallScore, pass: results.every((r) => r.pass) };
|
|
5255
|
+
const passThreshold = options.passThreshold ?? 0.8;
|
|
5256
|
+
const results = checks.map((c) => {
|
|
5257
|
+
const raw = c.score(pair);
|
|
5258
|
+
const clamped = Number.isFinite(raw) ? Math.max(0, Math.min(1, raw)) : 0;
|
|
5259
|
+
return { id: c.id, description: c.description, score: clamped, pass: clamped >= passThreshold };
|
|
5260
|
+
});
|
|
5261
|
+
const overallScore = results.reduce((a, r) => a + r.score, 0) / results.length;
|
|
5262
|
+
return { results, overallScore, pass: results.every((r) => r.pass) };
|
|
5263
|
+
}
|
|
5264
|
+
function keyPreserved(key) {
|
|
5265
|
+
return {
|
|
5266
|
+
id: `preserved(${key})`,
|
|
5267
|
+
description: `"${key}" unchanged from before to after`,
|
|
5268
|
+
score: ({ before, after }) => before[key] !== void 0 && before[key] === after[key] ? 1 : 0
|
|
5269
|
+
};
|
|
5270
|
+
}
|
|
5271
|
+
function collectionPreserved(key, minRatio = 1) {
|
|
5272
|
+
return {
|
|
5273
|
+
id: `collection-preserved(${key})`,
|
|
5274
|
+
description: `"${key}" length \u2265 ${minRatio} \xD7 prior length`,
|
|
5275
|
+
score: ({ before, after }) => {
|
|
5276
|
+
const b = before[key];
|
|
5277
|
+
const a = after[key];
|
|
5278
|
+
if (!Array.isArray(b) || !Array.isArray(a)) return 0;
|
|
5279
|
+
if (b.length === 0) return a.length === 0 ? 1 : 1;
|
|
5280
|
+
return Math.min(1, a.length / (b.length * minRatio));
|
|
5281
|
+
}
|
|
5282
|
+
};
|
|
5283
|
+
}
|
|
5284
|
+
function statusAdvanced(key, progression) {
|
|
5285
|
+
return {
|
|
5286
|
+
id: `status-advanced(${key})`,
|
|
5287
|
+
description: `"${key}" progressed along ${progression.join("\u2192")}`,
|
|
5288
|
+
score: ({ before, after }) => {
|
|
5289
|
+
const bi = progression.indexOf(String(before[key]));
|
|
5290
|
+
const ai2 = progression.indexOf(String(after[key]));
|
|
5291
|
+
if (bi === -1 || ai2 === -1) return 0;
|
|
5292
|
+
return ai2 >= bi ? 1 : 0;
|
|
5293
|
+
}
|
|
5294
|
+
};
|
|
5295
|
+
}
|
|
5296
|
+
|
|
5297
|
+
// src/ui-finding.ts
|
|
5298
|
+
var UI_LENSES = [
|
|
5299
|
+
"consistency",
|
|
5300
|
+
"hierarchy",
|
|
5301
|
+
"layout",
|
|
5302
|
+
"ux-flow",
|
|
5303
|
+
"duplication",
|
|
5304
|
+
"accessibility",
|
|
5305
|
+
"responsive",
|
|
5306
|
+
"states",
|
|
5307
|
+
"content",
|
|
5308
|
+
"interaction",
|
|
5309
|
+
"performance-perceived",
|
|
5310
|
+
"other"
|
|
5311
|
+
];
|
|
5312
|
+
var UI_FINDING_SEVERITIES = [
|
|
5313
|
+
"critical",
|
|
5314
|
+
"high",
|
|
5315
|
+
"med",
|
|
5316
|
+
"low"
|
|
5317
|
+
];
|
|
5318
|
+
|
|
5319
|
+
// src/trace-contracts.ts
|
|
5320
|
+
function isSerializedRegex(v) {
|
|
5321
|
+
return typeof v === "object" && v !== null && typeof v.$regex === "string" && typeof v.flags === "string";
|
|
5322
|
+
}
|
|
5323
|
+
function matchText(actual, matcher) {
|
|
5324
|
+
if (typeof actual !== "string") return false;
|
|
5325
|
+
if (typeof matcher === "string") return actual === matcher;
|
|
5326
|
+
if (matcher instanceof RegExp) return matcher.test(actual);
|
|
5327
|
+
return new RegExp(matcher.$regex, matcher.flags).test(actual);
|
|
5328
|
+
}
|
|
5329
|
+
function resolveToolName(span) {
|
|
5330
|
+
if (typeof span.toolName === "string") return span.toolName;
|
|
5331
|
+
const fromAttr = span.attributes?.["tool.name"] ?? span.attributes?.["toolName"];
|
|
5332
|
+
if (typeof fromAttr === "string") return fromAttr;
|
|
5333
|
+
if (span.kind === "tool" && typeof span.name === "string") return span.name;
|
|
5334
|
+
return void 0;
|
|
5335
|
+
}
|
|
5336
|
+
function assertPredicate(value, where) {
|
|
5337
|
+
if (value === null || typeof value !== "object") {
|
|
5338
|
+
throw new ValidationError(`${where}: predicate must be an object, got ${typeof value}`);
|
|
5339
|
+
}
|
|
5340
|
+
const p = value;
|
|
5341
|
+
for (const field of ["name", "tool"]) {
|
|
5342
|
+
const m = p[field];
|
|
5343
|
+
if (m !== void 0 && typeof m !== "string" && !(m instanceof RegExp) && !isSerializedRegex(m)) {
|
|
5344
|
+
throw new ValidationError(`${where}: "${field}" must be string | RegExp | SerializedRegex`);
|
|
5345
|
+
}
|
|
5346
|
+
}
|
|
5347
|
+
if (p.attr !== void 0 && (p.attr === null || typeof p.attr !== "object" || Array.isArray(p.attr))) {
|
|
5348
|
+
throw new ValidationError(`${where}: "attr" must be a plain object`);
|
|
5349
|
+
}
|
|
5350
|
+
if (p.requiresCustom && typeof p.custom !== "function") {
|
|
5351
|
+
throw new ValidationError(
|
|
5352
|
+
`${where}: rule was built with a custom predicate function, which does not survive JSON serialization \u2014 re-attach \`custom\` after deserializing or drop the rule`
|
|
5353
|
+
);
|
|
5354
|
+
}
|
|
5355
|
+
const hasAttr = p.attr !== void 0 && Object.keys(p.attr).length > 0;
|
|
5356
|
+
if (p.name === void 0 && p.tool === void 0 && !hasAttr && typeof p.custom !== "function") {
|
|
5357
|
+
throw new ValidationError(
|
|
5358
|
+
`${where}: empty predicate would match every span \u2014 specify name, tool, attr, or custom`
|
|
5359
|
+
);
|
|
5360
|
+
}
|
|
5361
|
+
}
|
|
5362
|
+
function matchSpan(span, predicate) {
|
|
5363
|
+
assertPredicate(predicate, "matchSpan");
|
|
5364
|
+
if (predicate.name !== void 0 && !matchText(span.name, predicate.name)) return false;
|
|
5365
|
+
if (predicate.tool !== void 0 && !matchText(resolveToolName(span), predicate.tool))
|
|
5366
|
+
return false;
|
|
5367
|
+
if (predicate.attr !== void 0) {
|
|
5368
|
+
for (const [key, expected] of Object.entries(predicate.attr)) {
|
|
5369
|
+
const actual = span.attributes?.[key];
|
|
5370
|
+
if (expected instanceof RegExp || isSerializedRegex(expected)) {
|
|
5371
|
+
if (!matchText(typeof actual === "string" ? actual : void 0, expected)) {
|
|
5372
|
+
return false;
|
|
5373
|
+
}
|
|
5374
|
+
} else if (actual !== expected) {
|
|
5375
|
+
return false;
|
|
5376
|
+
}
|
|
5377
|
+
}
|
|
5378
|
+
}
|
|
5379
|
+
if (predicate.custom !== void 0 && !predicate.custom(span)) return false;
|
|
5380
|
+
return true;
|
|
5381
|
+
}
|
|
5382
|
+
function describeMatcher(m) {
|
|
5383
|
+
if (typeof m === "string") return m;
|
|
5384
|
+
if (m instanceof RegExp) return `/${m.source}/${m.flags}`;
|
|
5385
|
+
return `/${m.$regex}/${m.flags}`;
|
|
5386
|
+
}
|
|
5387
|
+
function describePredicate(p) {
|
|
5388
|
+
const parts = [];
|
|
5389
|
+
if (p.name !== void 0) parts.push(`name=${describeMatcher(p.name)}`);
|
|
5390
|
+
if (p.tool !== void 0) parts.push(`tool=${describeMatcher(p.tool)}`);
|
|
5391
|
+
if (p.attr !== void 0) {
|
|
5392
|
+
for (const [k, v] of Object.entries(p.attr)) {
|
|
5393
|
+
parts.push(
|
|
5394
|
+
`attr.${k}=${v instanceof RegExp || isSerializedRegex(v) ? describeMatcher(v) : JSON.stringify(v)}`
|
|
5395
|
+
);
|
|
5396
|
+
}
|
|
5397
|
+
}
|
|
5398
|
+
if (typeof p.custom === "function") parts.push(`custom=${p.custom.name || "fn"}`);
|
|
5399
|
+
return parts.join(",");
|
|
5400
|
+
}
|
|
5401
|
+
function normalizeMatcher(m) {
|
|
5402
|
+
return m instanceof RegExp ? { $regex: m.source, flags: m.flags } : m;
|
|
5403
|
+
}
|
|
5404
|
+
function normalizePredicate(p, where) {
|
|
5405
|
+
assertPredicate(p, where);
|
|
5406
|
+
const out = {};
|
|
5407
|
+
if (p.name !== void 0) out.name = normalizeMatcher(p.name);
|
|
5408
|
+
if (p.tool !== void 0) out.tool = normalizeMatcher(p.tool);
|
|
5409
|
+
if (p.attr !== void 0) {
|
|
5410
|
+
out.attr = Object.fromEntries(
|
|
5411
|
+
Object.entries(p.attr).map(([k, v]) => [k, v instanceof RegExp ? normalizeMatcher(v) : v])
|
|
5412
|
+
);
|
|
5413
|
+
}
|
|
5414
|
+
if (typeof p.custom === "function") {
|
|
5415
|
+
out.custom = p.custom;
|
|
5416
|
+
out.requiresCustom = true;
|
|
5417
|
+
}
|
|
5418
|
+
return out;
|
|
5419
|
+
}
|
|
5420
|
+
var TraceContractBuilder = class {
|
|
5421
|
+
constructor(name) {
|
|
5422
|
+
this.name = name;
|
|
5423
|
+
}
|
|
5424
|
+
name;
|
|
5425
|
+
rules = [];
|
|
5426
|
+
/** Every span in the trace must satisfy `p`. */
|
|
5427
|
+
always(p, label) {
|
|
5428
|
+
const np = normalizePredicate(p, `traceContract("${this.name}").always`);
|
|
5429
|
+
return this.add({ kind: "always", label: label ?? `always(${describePredicate(np)})`, p: np });
|
|
5430
|
+
}
|
|
5431
|
+
/** No span in the trace may satisfy `p`. */
|
|
5432
|
+
never(p, label) {
|
|
5433
|
+
const np = normalizePredicate(p, `traceContract("${this.name}").never`);
|
|
5434
|
+
return this.add({ kind: "never", label: label ?? `never(${describePredicate(np)})`, p: np });
|
|
5435
|
+
}
|
|
5436
|
+
/** At least one span in the trace must satisfy `p`. */
|
|
5437
|
+
eventually(p, label) {
|
|
5438
|
+
const np = normalizePredicate(p, `traceContract("${this.name}").eventually`);
|
|
5439
|
+
return this.add({
|
|
5440
|
+
kind: "eventually",
|
|
5441
|
+
label: label ?? `eventually(${describePredicate(np)})`,
|
|
5442
|
+
p: np
|
|
5443
|
+
});
|
|
5444
|
+
}
|
|
5445
|
+
/** Every `b`-match must have a strictly earlier `a`-match. */
|
|
5446
|
+
precedes(a, b, label) {
|
|
5447
|
+
const na = normalizePredicate(a, `traceContract("${this.name}").precedes (a)`);
|
|
5448
|
+
const nb = normalizePredicate(b, `traceContract("${this.name}").precedes (b)`);
|
|
5449
|
+
return this.add({
|
|
5450
|
+
kind: "precedes",
|
|
5451
|
+
label: label ?? `precedes(${describePredicate(na)} -> ${describePredicate(nb)})`,
|
|
5452
|
+
a: na,
|
|
5453
|
+
b: nb
|
|
5454
|
+
});
|
|
5455
|
+
}
|
|
5456
|
+
/** Every `p`-match is a violation unless a strictly earlier `prior`-match exists. */
|
|
5457
|
+
neverUnless(p, prior, label) {
|
|
5458
|
+
const np = normalizePredicate(p, `traceContract("${this.name}").neverUnless (p)`);
|
|
5459
|
+
const nprior = normalizePredicate(prior, `traceContract("${this.name}").neverUnless (prior)`);
|
|
5460
|
+
return this.add({
|
|
5461
|
+
kind: "neverUnless",
|
|
5462
|
+
label: label ?? `neverUnless(${describePredicate(np)} unless ${describePredicate(nprior)})`,
|
|
5463
|
+
p: np,
|
|
5464
|
+
prior: nprior
|
|
5465
|
+
});
|
|
5466
|
+
}
|
|
5467
|
+
build() {
|
|
5468
|
+
if (this.rules.length === 0) {
|
|
5469
|
+
throw new ValidationError(
|
|
5470
|
+
`traceContract("${this.name}").build(): no rules \u2014 an empty contract would vacuously pass`
|
|
5471
|
+
);
|
|
5472
|
+
}
|
|
5473
|
+
return { name: this.name, rules: [...this.rules] };
|
|
5474
|
+
}
|
|
5475
|
+
add(rule) {
|
|
5476
|
+
let label = rule.label;
|
|
5477
|
+
let n = 2;
|
|
5478
|
+
while (this.rules.some((r) => r.label === label)) {
|
|
5479
|
+
label = `${rule.label} #${n}`;
|
|
5480
|
+
n += 1;
|
|
5481
|
+
}
|
|
5482
|
+
this.rules.push({ ...rule, label });
|
|
5483
|
+
return this;
|
|
5484
|
+
}
|
|
5485
|
+
};
|
|
5486
|
+
function traceContract(name) {
|
|
5487
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
5488
|
+
throw new ValidationError("traceContract: name must be a non-empty string");
|
|
5489
|
+
}
|
|
5490
|
+
return new TraceContractBuilder(name);
|
|
5491
|
+
}
|
|
5492
|
+
function assertContract(contract) {
|
|
5493
|
+
if (typeof contract?.name !== "string" || contract.name.length === 0) {
|
|
5494
|
+
throw new ValidationError("evaluateTraceContract: contract.name must be a non-empty string");
|
|
5495
|
+
}
|
|
5496
|
+
if (!Array.isArray(contract.rules) || contract.rules.length === 0) {
|
|
5497
|
+
throw new ValidationError(
|
|
5498
|
+
`evaluateTraceContract: contract "${contract.name}" has no rules \u2014 an empty contract would vacuously pass`
|
|
5499
|
+
);
|
|
5500
|
+
}
|
|
5501
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5502
|
+
for (const rule of contract.rules) {
|
|
5503
|
+
const where = `contract "${contract.name}" rule "${rule?.label ?? "<unlabeled>"}"`;
|
|
5504
|
+
if (typeof rule?.label !== "string" || rule.label.length === 0) {
|
|
5505
|
+
throw new ValidationError(`${where}: label must be a non-empty string`);
|
|
5506
|
+
}
|
|
5507
|
+
if (seen.has(rule.label)) {
|
|
5508
|
+
throw new ValidationError(`${where}: duplicate label would collapse per-rule scores`);
|
|
5509
|
+
}
|
|
5510
|
+
seen.add(rule.label);
|
|
5511
|
+
switch (rule.kind) {
|
|
5512
|
+
case "always":
|
|
5513
|
+
case "never":
|
|
5514
|
+
case "eventually":
|
|
5515
|
+
assertPredicate(rule.p, `${where} (p)`);
|
|
5516
|
+
break;
|
|
5517
|
+
case "precedes":
|
|
5518
|
+
assertPredicate(rule.a, `${where} (a)`);
|
|
5519
|
+
assertPredicate(rule.b, `${where} (b)`);
|
|
5520
|
+
break;
|
|
5521
|
+
case "neverUnless":
|
|
5522
|
+
assertPredicate(rule.p, `${where} (p)`);
|
|
5523
|
+
assertPredicate(rule.prior, `${where} (prior)`);
|
|
5524
|
+
break;
|
|
5525
|
+
default:
|
|
5526
|
+
throw new ValidationError(
|
|
5527
|
+
`${where}: unknown rule kind "${String(rule.kind)}"`
|
|
5528
|
+
);
|
|
5529
|
+
}
|
|
5530
|
+
}
|
|
5531
|
+
}
|
|
5532
|
+
function orderSpans(spans) {
|
|
5533
|
+
const timed = spans.filter(
|
|
5534
|
+
(s) => typeof s.startedAt === "number" && Number.isFinite(s.startedAt)
|
|
5535
|
+
).length;
|
|
5536
|
+
if (timed === spans.length) {
|
|
5537
|
+
return spans.map((span, i) => ({ span, i })).sort((x, y) => x.span.startedAt - y.span.startedAt || x.i - y.i).map((x) => x.span);
|
|
5538
|
+
}
|
|
5539
|
+
if (timed === 0) return [...spans];
|
|
5540
|
+
throw new ValidationError(
|
|
5541
|
+
`evaluateTraceContract: ${timed}/${spans.length} spans carry a finite startedAt \u2014 mixed timestamps make ordering ambiguous; stamp every span or none`
|
|
5542
|
+
);
|
|
5543
|
+
}
|
|
5544
|
+
function spanRef(span, index) {
|
|
5545
|
+
return span.spanId ?? `#${index}`;
|
|
5546
|
+
}
|
|
5547
|
+
function checkGuarded(args) {
|
|
5548
|
+
const out = [];
|
|
5549
|
+
let guardSeen = false;
|
|
5550
|
+
for (let i = 0; i < args.ordered.length; i++) {
|
|
5551
|
+
const span = args.ordered[i];
|
|
5552
|
+
if (!guardSeen && matchSpan(span, args.guarded)) {
|
|
5553
|
+
out.push({ rule: args.label, spanId: span.spanId, detail: args.detail(spanRef(span, i)) });
|
|
5554
|
+
}
|
|
5555
|
+
if (matchSpan(span, args.guard)) guardSeen = true;
|
|
5556
|
+
}
|
|
5557
|
+
return out;
|
|
5558
|
+
}
|
|
5559
|
+
function checkRule(rule, ordered) {
|
|
5560
|
+
switch (rule.kind) {
|
|
5561
|
+
case "always": {
|
|
5562
|
+
const out = [];
|
|
5563
|
+
for (let i = 0; i < ordered.length; i++) {
|
|
5564
|
+
const span = ordered[i];
|
|
5565
|
+
if (!matchSpan(span, rule.p)) {
|
|
5566
|
+
out.push({
|
|
5567
|
+
rule: rule.label,
|
|
5568
|
+
spanId: span.spanId,
|
|
5569
|
+
detail: `span ${spanRef(span, i)} ("${span.name ?? ""}") fails always(${describePredicate(rule.p)})`
|
|
5570
|
+
});
|
|
5571
|
+
}
|
|
5572
|
+
}
|
|
5573
|
+
return out;
|
|
5574
|
+
}
|
|
5575
|
+
case "never": {
|
|
5576
|
+
const out = [];
|
|
5577
|
+
for (let i = 0; i < ordered.length; i++) {
|
|
5578
|
+
const span = ordered[i];
|
|
5579
|
+
if (matchSpan(span, rule.p)) {
|
|
5580
|
+
out.push({
|
|
5581
|
+
rule: rule.label,
|
|
5582
|
+
spanId: span.spanId,
|
|
5583
|
+
detail: `span ${spanRef(span, i)} ("${span.name ?? ""}") matches never(${describePredicate(rule.p)})`
|
|
5584
|
+
});
|
|
5585
|
+
}
|
|
5586
|
+
}
|
|
5587
|
+
return out;
|
|
5588
|
+
}
|
|
5589
|
+
case "eventually": {
|
|
5590
|
+
const hit = ordered.some((span) => matchSpan(span, rule.p));
|
|
5591
|
+
return hit ? [] : [
|
|
5592
|
+
{
|
|
5593
|
+
rule: rule.label,
|
|
5594
|
+
detail: `no span matches eventually(${describePredicate(rule.p)}) over ${ordered.length} span(s)`
|
|
5595
|
+
}
|
|
5596
|
+
];
|
|
5597
|
+
}
|
|
5598
|
+
case "precedes":
|
|
5599
|
+
return checkGuarded({
|
|
5600
|
+
label: rule.label,
|
|
5601
|
+
ordered,
|
|
5602
|
+
guard: rule.a,
|
|
5603
|
+
guarded: rule.b,
|
|
5604
|
+
detail: (ref) => `span ${ref} matches ${describePredicate(rule.b)} with no earlier ${describePredicate(rule.a)} match`
|
|
5605
|
+
});
|
|
5606
|
+
case "neverUnless":
|
|
5607
|
+
return checkGuarded({
|
|
5608
|
+
label: rule.label,
|
|
5609
|
+
ordered,
|
|
5610
|
+
guard: rule.prior,
|
|
5611
|
+
guarded: rule.p,
|
|
5612
|
+
detail: (ref) => `span ${ref} matches ${describePredicate(rule.p)} with no earlier ${describePredicate(rule.prior)} match`
|
|
5613
|
+
});
|
|
5614
|
+
}
|
|
4989
5615
|
}
|
|
4990
|
-
function
|
|
5616
|
+
function evaluateTraceContract(contract, spans) {
|
|
5617
|
+
assertContract(contract);
|
|
5618
|
+
const ordered = orderSpans(spans);
|
|
5619
|
+
const scores2 = {};
|
|
5620
|
+
const violations = [];
|
|
5621
|
+
for (const rule of contract.rules) {
|
|
5622
|
+
const ruleViolations = checkRule(rule, ordered);
|
|
5623
|
+
scores2[rule.label] = ruleViolations.length === 0 ? 1 : 0;
|
|
5624
|
+
violations.push(...ruleViolations);
|
|
5625
|
+
}
|
|
5626
|
+
const ruleCount = contract.rules.length;
|
|
5627
|
+
const passCount = Object.values(scores2).filter((s) => s === 1).length;
|
|
4991
5628
|
return {
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
score:
|
|
5629
|
+
contract: contract.name,
|
|
5630
|
+
valid: passCount === ruleCount,
|
|
5631
|
+
score: passCount / ruleCount,
|
|
5632
|
+
scores: scores2,
|
|
5633
|
+
violations,
|
|
5634
|
+
notes: `${passCount}/${ruleCount} rules passed`
|
|
4995
5635
|
};
|
|
4996
5636
|
}
|
|
4997
|
-
function
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
if (b.length === 0) return a.length === 0 ? 1 : 1;
|
|
5006
|
-
return Math.min(1, a.length / (b.length * minRatio));
|
|
5007
|
-
}
|
|
5008
|
-
};
|
|
5637
|
+
function checkTraceContracts(spans, contracts) {
|
|
5638
|
+
if (contracts.length === 0) {
|
|
5639
|
+
throw new ValidationError(
|
|
5640
|
+
"checkTraceContracts: empty contract list would vacuously pass \u2014 supply at least one contract"
|
|
5641
|
+
);
|
|
5642
|
+
}
|
|
5643
|
+
const verdicts = contracts.map((c) => evaluateTraceContract(c, spans));
|
|
5644
|
+
return { verdicts, allValid: verdicts.every((v) => v.valid) };
|
|
5009
5645
|
}
|
|
5010
|
-
function
|
|
5646
|
+
function contractJudge(contracts, opts) {
|
|
5647
|
+
if (contracts.length === 0) {
|
|
5648
|
+
throw new ValidationError("contractJudge: at least one contract required");
|
|
5649
|
+
}
|
|
5650
|
+
for (const c of contracts) assertContract(c);
|
|
5651
|
+
const names = /* @__PURE__ */ new Set();
|
|
5652
|
+
for (const c of contracts) {
|
|
5653
|
+
if (names.has(c.name)) {
|
|
5654
|
+
throw new ValidationError(
|
|
5655
|
+
`contractJudge: duplicate contract name "${c.name}" would collapse judge dimensions`
|
|
5656
|
+
);
|
|
5657
|
+
}
|
|
5658
|
+
names.add(c.name);
|
|
5659
|
+
}
|
|
5660
|
+
if (typeof opts?.spans !== "function") {
|
|
5661
|
+
throw new ValidationError("contractJudge: opts.spans extraction function is required");
|
|
5662
|
+
}
|
|
5663
|
+
const dimensions = contracts.map((c) => ({
|
|
5664
|
+
key: c.name,
|
|
5665
|
+
description: c.rules.map((r) => r.label).join("; ")
|
|
5666
|
+
}));
|
|
5011
5667
|
return {
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
score
|
|
5015
|
-
const
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5668
|
+
name: opts.name ?? "trace-contracts",
|
|
5669
|
+
dimensions,
|
|
5670
|
+
score({ artifact, scenario }) {
|
|
5671
|
+
const spans = opts.spans({ artifact, scenario });
|
|
5672
|
+
if (!Array.isArray(spans)) {
|
|
5673
|
+
throw new ValidationError(
|
|
5674
|
+
`contractJudge: spans() must return a span array, got ${typeof spans}`
|
|
5675
|
+
);
|
|
5676
|
+
}
|
|
5677
|
+
const { verdicts } = checkTraceContracts(spans, contracts);
|
|
5678
|
+
const dims = {};
|
|
5679
|
+
const violations = [];
|
|
5680
|
+
for (const v of verdicts) {
|
|
5681
|
+
dims[v.contract] = v.score;
|
|
5682
|
+
violations.push(...v.violations);
|
|
5683
|
+
}
|
|
5684
|
+
const composite = verdicts.reduce((acc, v) => acc + v.score, 0) / verdicts.length;
|
|
5685
|
+
return {
|
|
5686
|
+
dimensions: dims,
|
|
5687
|
+
composite,
|
|
5688
|
+
notes: violations.length === 0 ? "all trace contracts satisfied" : violations.slice(0, 8).map((x) => `${x.rule}: ${x.detail}`).join("\n")
|
|
5689
|
+
};
|
|
5019
5690
|
}
|
|
5020
5691
|
};
|
|
5021
5692
|
}
|
|
5022
5693
|
|
|
5023
|
-
// src/ui-finding.ts
|
|
5024
|
-
var UI_LENSES = [
|
|
5025
|
-
"consistency",
|
|
5026
|
-
"hierarchy",
|
|
5027
|
-
"layout",
|
|
5028
|
-
"ux-flow",
|
|
5029
|
-
"duplication",
|
|
5030
|
-
"accessibility",
|
|
5031
|
-
"responsive",
|
|
5032
|
-
"states",
|
|
5033
|
-
"content",
|
|
5034
|
-
"interaction",
|
|
5035
|
-
"performance-perceived",
|
|
5036
|
-
"other"
|
|
5037
|
-
];
|
|
5038
|
-
var UI_FINDING_SEVERITIES = [
|
|
5039
|
-
"critical",
|
|
5040
|
-
"high",
|
|
5041
|
-
"med",
|
|
5042
|
-
"low"
|
|
5043
|
-
];
|
|
5044
|
-
|
|
5045
5694
|
// src/behavior-dsl.ts
|
|
5046
5695
|
var BehaviorAssertion = class {
|
|
5047
5696
|
constructor(store, runId) {
|
|
@@ -5109,6 +5758,25 @@ var BehaviorAssertion = class {
|
|
|
5109
5758
|
}
|
|
5110
5759
|
};
|
|
5111
5760
|
}
|
|
5761
|
+
/** Evaluate a finite-trace temporal contract (`traceContract(...)`) over
|
|
5762
|
+
* this run's span sequence. See `trace-contracts.ts` for the operators. */
|
|
5763
|
+
toSatisfyContract(contract) {
|
|
5764
|
+
return {
|
|
5765
|
+
label: `agent(${this.runId}).toSatisfyContract(${contract.name})`,
|
|
5766
|
+
check: async () => {
|
|
5767
|
+
const spans = await this.store.spans({ runId: this.runId });
|
|
5768
|
+
const verdict = evaluateTraceContract(contract, spans);
|
|
5769
|
+
if (verdict.valid) {
|
|
5770
|
+
return { ok: true, detail: `contract "${contract.name}": ${verdict.notes}` };
|
|
5771
|
+
}
|
|
5772
|
+
return {
|
|
5773
|
+
ok: false,
|
|
5774
|
+
detail: verdict.violations.map((v) => `${v.rule}: ${v.detail}`).join("; "),
|
|
5775
|
+
evidence: verdict.violations.find((v) => v.spanId)?.spanId
|
|
5776
|
+
};
|
|
5777
|
+
}
|
|
5778
|
+
};
|
|
5779
|
+
}
|
|
5112
5780
|
toNeverCall(toolName) {
|
|
5113
5781
|
return {
|
|
5114
5782
|
label: `agent(${this.runId}).toNeverCall(${toolName})`,
|
|
@@ -5711,90 +6379,6 @@ async function promptBisect(options) {
|
|
|
5711
6379
|
};
|
|
5712
6380
|
}
|
|
5713
6381
|
|
|
5714
|
-
// src/counterfactual.ts
|
|
5715
|
-
async function runCounterfactual(store, originalRunId, mutation, runner) {
|
|
5716
|
-
const originalRun = await store.getRun(originalRunId);
|
|
5717
|
-
if (!originalRun) throw new NotFoundError(`counterfactual: run ${originalRunId} not found`);
|
|
5718
|
-
const trajectory = await buildTrajectory(store, originalRunId);
|
|
5719
|
-
if (mutation.at < 0 || mutation.at >= trajectory.steps.length) {
|
|
5720
|
-
throw new ValidationError(
|
|
5721
|
-
`counterfactual: mutation.at=${mutation.at} out of range [0, ${trajectory.steps.length})`
|
|
5722
|
-
);
|
|
5723
|
-
}
|
|
5724
|
-
const targetStep = trajectory.steps[mutation.at];
|
|
5725
|
-
const mutatedStep = applyMutation(targetStep, mutation);
|
|
5726
|
-
const cfEmitter = new TraceEmitter(store);
|
|
5727
|
-
await cfEmitter.startRun({
|
|
5728
|
-
scenarioId: originalRun.scenarioId,
|
|
5729
|
-
variantId: originalRun.variantId ? `${originalRun.variantId}+cf:${mutation.kind}@${mutation.at}` : `cf:${mutation.kind}@${mutation.at}`,
|
|
5730
|
-
projectId: originalRun.projectId,
|
|
5731
|
-
parentRunId: originalRunId,
|
|
5732
|
-
layer: "meta",
|
|
5733
|
-
tags: { counterfactual: "true", mutationKind: mutation.kind, mutationAt: String(mutation.at) }
|
|
5734
|
-
});
|
|
5735
|
-
await runner.executeFrom(
|
|
5736
|
-
{
|
|
5737
|
-
originalRunId,
|
|
5738
|
-
originalTrajectory: trajectory,
|
|
5739
|
-
prefix: trajectory.steps.slice(0, mutation.at),
|
|
5740
|
-
mutation,
|
|
5741
|
-
mutatedStep
|
|
5742
|
-
},
|
|
5743
|
-
cfEmitter
|
|
5744
|
-
);
|
|
5745
|
-
const counterfactual = await store.getRun(cfEmitter.runId);
|
|
5746
|
-
const delta = {
|
|
5747
|
-
originalOutcomeScore: originalRun.outcome?.score ?? null,
|
|
5748
|
-
counterfactualOutcomeScore: counterfactual?.outcome?.score ?? null,
|
|
5749
|
-
deltaScore: originalRun.outcome?.score !== void 0 && counterfactual?.outcome?.score !== void 0 ? counterfactual.outcome.score - originalRun.outcome.score : null
|
|
5750
|
-
};
|
|
5751
|
-
return { counterfactualRunId: cfEmitter.runId, originalRunId, mutation, delta };
|
|
5752
|
-
}
|
|
5753
|
-
function applyMutation(step, mutation) {
|
|
5754
|
-
if (mutation.kind === "swap-model" && step.span.kind === "llm") {
|
|
5755
|
-
const llm = step.span;
|
|
5756
|
-
return { ...step, span: { ...llm, model: mutation.newModel } };
|
|
5757
|
-
}
|
|
5758
|
-
if (mutation.kind === "swap-tool-result" && step.span.kind === "tool") {
|
|
5759
|
-
const tool = step.span;
|
|
5760
|
-
return { ...step, span: { ...tool, result: mutation.newResult } };
|
|
5761
|
-
}
|
|
5762
|
-
if (mutation.kind === "inject-system-message" && step.span.kind === "llm") {
|
|
5763
|
-
const llm = step.span;
|
|
5764
|
-
return {
|
|
5765
|
-
...step,
|
|
5766
|
-
span: {
|
|
5767
|
-
...llm,
|
|
5768
|
-
messages: [{ role: "system", content: mutation.content }, ...llm.messages]
|
|
5769
|
-
}
|
|
5770
|
-
};
|
|
5771
|
-
}
|
|
5772
|
-
if (mutation.kind === "custom") return mutation.apply(step);
|
|
5773
|
-
return step;
|
|
5774
|
-
}
|
|
5775
|
-
function attributeCounterfactuals(results) {
|
|
5776
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
5777
|
-
for (const r of results) {
|
|
5778
|
-
const arr = grouped.get(r.mutation.kind) ?? [];
|
|
5779
|
-
arr.push(r);
|
|
5780
|
-
grouped.set(r.mutation.kind, arr);
|
|
5781
|
-
}
|
|
5782
|
-
const out = [];
|
|
5783
|
-
for (const [kind, items] of grouped) {
|
|
5784
|
-
const deltas = items.map((i) => i.delta.deltaScore).filter((d) => typeof d === "number");
|
|
5785
|
-
if (deltas.length === 0) continue;
|
|
5786
|
-
const meanAbs = deltas.reduce((a, b) => a + Math.abs(b), 0) / deltas.length;
|
|
5787
|
-
const meanSigned = deltas.reduce((a, b) => a + b, 0) / deltas.length;
|
|
5788
|
-
out.push({
|
|
5789
|
-
mutationKind: kind,
|
|
5790
|
-
n: deltas.length,
|
|
5791
|
-
meanAbsDelta: meanAbs,
|
|
5792
|
-
meanSignedDelta: meanSigned
|
|
5793
|
-
});
|
|
5794
|
-
}
|
|
5795
|
-
return out.sort((a, b) => b.meanAbsDelta - a.meanAbsDelta);
|
|
5796
|
-
}
|
|
5797
|
-
|
|
5798
6382
|
// src/cross-trace-diff.ts
|
|
5799
6383
|
async function crossTraceDiff(store, runA, runB, options = {}) {
|
|
5800
6384
|
const [a, b] = await Promise.all([buildTrajectory(store, runA), buildTrajectory(store, runB)]);
|
|
@@ -6986,14 +7570,18 @@ function aggregate(layers, results, startedAt, startedAtMs) {
|
|
|
6986
7570
|
}
|
|
6987
7571
|
}
|
|
6988
7572
|
const finishedAtMs = Date.now();
|
|
7573
|
+
const allPass = ranAnyScoredLayer && !anyScoredLayerFailed && failCount === 0 && errorCount === 0;
|
|
7574
|
+
const blendedScore = scoredWeightSum > 0 ? scoredWeightedTotal / scoredWeightSum : 0;
|
|
6989
7575
|
return {
|
|
6990
7576
|
layers: results,
|
|
6991
7577
|
passCount,
|
|
6992
7578
|
failCount,
|
|
6993
7579
|
skippedCount,
|
|
6994
7580
|
errorCount,
|
|
6995
|
-
allPass
|
|
6996
|
-
blendedScore
|
|
7581
|
+
allPass,
|
|
7582
|
+
blendedScore,
|
|
7583
|
+
valid: allPass,
|
|
7584
|
+
score: blendedScore,
|
|
6997
7585
|
durationMs: finishedAtMs - startedAtMs,
|
|
6998
7586
|
startedAt,
|
|
6999
7587
|
finishedAt: new Date(finishedAtMs).toISOString()
|
|
@@ -8208,69 +8796,6 @@ function fmt(x) {
|
|
|
8208
8796
|
return x.toFixed(4);
|
|
8209
8797
|
}
|
|
8210
8798
|
|
|
8211
|
-
// src/judge-retry.ts
|
|
8212
|
-
var DEFAULT_MAX_ATTEMPTS = 3;
|
|
8213
|
-
var DEFAULT_TIMEOUT_MS = 3e5;
|
|
8214
|
-
function sleep(ms) {
|
|
8215
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
8216
|
-
}
|
|
8217
|
-
async function withJudgeRetry(judgeFn, policy = {}) {
|
|
8218
|
-
const maxAttempts = policy.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
8219
|
-
const timeoutMs = policy.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
8220
|
-
const backoff = policy.backoffMs ?? backoffMs;
|
|
8221
|
-
const isRetryable = policy.isRetryable ?? isTransientLlmError;
|
|
8222
|
-
const models = policy.models && policy.models.length > 0 ? policy.models : [void 0];
|
|
8223
|
-
let totalAttempts = 0;
|
|
8224
|
-
const attemptErrors = [];
|
|
8225
|
-
let lastError;
|
|
8226
|
-
for (const model of models) {
|
|
8227
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
8228
|
-
totalAttempts += 1;
|
|
8229
|
-
const controller = new AbortController();
|
|
8230
|
-
const timer = setTimeout(() => controller.abort(new Error("TimeoutError")), timeoutMs);
|
|
8231
|
-
try {
|
|
8232
|
-
const value = await judgeFn(model, controller.signal);
|
|
8233
|
-
clearTimeout(timer);
|
|
8234
|
-
return {
|
|
8235
|
-
value,
|
|
8236
|
-
succeeded: true,
|
|
8237
|
-
attempts: totalAttempts,
|
|
8238
|
-
modelUsed: model,
|
|
8239
|
-
attemptErrors
|
|
8240
|
-
};
|
|
8241
|
-
} catch (err) {
|
|
8242
|
-
clearTimeout(timer);
|
|
8243
|
-
const errObj = err instanceof Error ? err : new Error(String(err));
|
|
8244
|
-
lastError = errObj;
|
|
8245
|
-
attemptErrors.push({
|
|
8246
|
-
attempt: totalAttempts,
|
|
8247
|
-
model: model ?? "(default)",
|
|
8248
|
-
error: errObj.message
|
|
8249
|
-
});
|
|
8250
|
-
if (!isRetryable(errObj)) {
|
|
8251
|
-
return {
|
|
8252
|
-
value: null,
|
|
8253
|
-
succeeded: false,
|
|
8254
|
-
attempts: totalAttempts,
|
|
8255
|
-
error: errObj,
|
|
8256
|
-
attemptErrors
|
|
8257
|
-
};
|
|
8258
|
-
}
|
|
8259
|
-
if (attempt < maxAttempts - 1) {
|
|
8260
|
-
await sleep(backoff(attempt));
|
|
8261
|
-
}
|
|
8262
|
-
}
|
|
8263
|
-
}
|
|
8264
|
-
}
|
|
8265
|
-
return {
|
|
8266
|
-
value: null,
|
|
8267
|
-
succeeded: false,
|
|
8268
|
-
attempts: totalAttempts,
|
|
8269
|
-
error: lastError,
|
|
8270
|
-
attemptErrors
|
|
8271
|
-
};
|
|
8272
|
-
}
|
|
8273
|
-
|
|
8274
8799
|
// src/orthogonality.ts
|
|
8275
8800
|
function passOrthogonality(input) {
|
|
8276
8801
|
const passes = input.passes;
|
|
@@ -8649,6 +9174,7 @@ function traceJudgeEnsemble(judges, judgeNames, opts) {
|
|
|
8649
9174
|
});
|
|
8650
9175
|
try {
|
|
8651
9176
|
const allScores = [];
|
|
9177
|
+
let failedJudges = 0;
|
|
8652
9178
|
for (let i = 0; i < judges.length; i++) {
|
|
8653
9179
|
const judge = judges[i];
|
|
8654
9180
|
const name = judgeNames[i] ?? `judge_${i}`;
|
|
@@ -8656,8 +9182,13 @@ function traceJudgeEnsemble(judges, judgeNames, opts) {
|
|
|
8656
9182
|
emitter: opts.emitter,
|
|
8657
9183
|
parentSpanId: ensembleSpan.span.spanId
|
|
8658
9184
|
});
|
|
8659
|
-
|
|
8660
|
-
|
|
9185
|
+
try {
|
|
9186
|
+
const scores2 = await tracedFn(tc, input);
|
|
9187
|
+
allScores.push(...scores2);
|
|
9188
|
+
} catch (err) {
|
|
9189
|
+
if (!(err instanceof JudgeParseError)) throw err;
|
|
9190
|
+
failedJudges++;
|
|
9191
|
+
}
|
|
8661
9192
|
}
|
|
8662
9193
|
const composite = allScores.length > 0 ? allScores.reduce((sum3, s) => sum3 + s.score, 0) / allScores.length : 0;
|
|
8663
9194
|
await ensembleSpan.end({
|
|
@@ -8665,6 +9196,7 @@ function traceJudgeEnsemble(judges, judgeNames, opts) {
|
|
|
8665
9196
|
"judge.ensemble_size": judges.length,
|
|
8666
9197
|
"judge.composite_score": composite,
|
|
8667
9198
|
"judge.total_dimensions": allScores.length,
|
|
9199
|
+
"judge.failed_judges": failedJudges,
|
|
8668
9200
|
"eval.phase": "judge"
|
|
8669
9201
|
}
|
|
8670
9202
|
});
|
|
@@ -9094,8 +9626,256 @@ function applyDomainPatch(p, sectionId, newBody) {
|
|
|
9094
9626
|
function sectionHash(section) {
|
|
9095
9627
|
return surfaceContentHash(JSON.stringify({ title: section.title, body: section.body }));
|
|
9096
9628
|
}
|
|
9629
|
+
|
|
9630
|
+
// src/cost-report.ts
|
|
9631
|
+
function costReport(ledger) {
|
|
9632
|
+
const summary = ledger.summary();
|
|
9633
|
+
const perModel = /* @__PURE__ */ new Map();
|
|
9634
|
+
for (const entry of ledger.list()) {
|
|
9635
|
+
const roll = perModel.get(entry.model) ?? {
|
|
9636
|
+
model: entry.model,
|
|
9637
|
+
usd: 0,
|
|
9638
|
+
entries: 0,
|
|
9639
|
+
unpriced: false
|
|
9640
|
+
};
|
|
9641
|
+
roll.usd += entry.costUsd;
|
|
9642
|
+
roll.entries += 1;
|
|
9643
|
+
if (entry.costUnknown) roll.unpriced = true;
|
|
9644
|
+
perModel.set(entry.model, roll);
|
|
9645
|
+
}
|
|
9646
|
+
return {
|
|
9647
|
+
perChannel: summary.byChannel,
|
|
9648
|
+
total: {
|
|
9649
|
+
usd: summary.totalCostUsd,
|
|
9650
|
+
unknownEntries: summary.byChannel.reduce((sum3, c) => sum3 + c.unpricedCalls, 0)
|
|
9651
|
+
},
|
|
9652
|
+
perModel: [...perModel.values()].sort((a, b) => a.model.localeCompare(b.model))
|
|
9653
|
+
};
|
|
9654
|
+
}
|
|
9655
|
+
function attachCostToReport(report, ledger) {
|
|
9656
|
+
if ("cost" in report) {
|
|
9657
|
+
throw new ValidationError(
|
|
9658
|
+
"attachCostToReport: report already has a 'cost' key \u2014 refusing to overwrite an existing stamp"
|
|
9659
|
+
);
|
|
9660
|
+
}
|
|
9661
|
+
return { ...report, cost: costReport(ledger) };
|
|
9662
|
+
}
|
|
9663
|
+
|
|
9664
|
+
// src/model-seats.ts
|
|
9665
|
+
var seatPresets = {
|
|
9666
|
+
economy: {
|
|
9667
|
+
worker: "kimi-k2.6",
|
|
9668
|
+
judges: ["kimi-k2.6", "deepseek-v4-pro", "gpt-4.1-mini"],
|
|
9669
|
+
analyst: "gpt-4.1-mini",
|
|
9670
|
+
reflection: "gpt-4.1-mini",
|
|
9671
|
+
verifier: "deepseek-v4-pro"
|
|
9672
|
+
},
|
|
9673
|
+
frontier: {}
|
|
9674
|
+
};
|
|
9675
|
+
var SeatUnsetError = class extends ConfigError {
|
|
9676
|
+
constructor(seat) {
|
|
9677
|
+
super(
|
|
9678
|
+
`ModelSeats: seat '${seat}' is unset and no fallback was given \u2014 name a model explicitly (a model id is a budget decision, never a silent default)`
|
|
9679
|
+
);
|
|
9680
|
+
this.seat = seat;
|
|
9681
|
+
}
|
|
9682
|
+
seat;
|
|
9683
|
+
};
|
|
9684
|
+
function resolveSeat(seats, seat, fallback) {
|
|
9685
|
+
const value = seats[seat];
|
|
9686
|
+
if (seat === "judges") {
|
|
9687
|
+
if (value !== void 0 && !Array.isArray(value)) {
|
|
9688
|
+
throw new ValidationError(`ModelSeats: seat 'judges' must be a string[], got ${typeof value}`);
|
|
9689
|
+
}
|
|
9690
|
+
const models = Array.isArray(value) ? value : [];
|
|
9691
|
+
if (models.length > 0) {
|
|
9692
|
+
const blank = models.findIndex((m) => typeof m !== "string" || m.trim() === "");
|
|
9693
|
+
if (blank >= 0) {
|
|
9694
|
+
throw new ValidationError(
|
|
9695
|
+
`ModelSeats: judges[${blank}] is blank \u2014 every panel model must be a non-empty id`
|
|
9696
|
+
);
|
|
9697
|
+
}
|
|
9698
|
+
return [...models];
|
|
9699
|
+
}
|
|
9700
|
+
} else {
|
|
9701
|
+
if (value !== void 0 && typeof value !== "string") {
|
|
9702
|
+
throw new ValidationError(`ModelSeats: seat '${seat}' must be a string, got ${typeof value}`);
|
|
9703
|
+
}
|
|
9704
|
+
if (typeof value === "string" && value.trim() !== "") return value;
|
|
9705
|
+
}
|
|
9706
|
+
if (fallback !== void 0) {
|
|
9707
|
+
if (fallback.trim() === "") {
|
|
9708
|
+
throw new ValidationError(`ModelSeats: fallback for seat '${seat}' is blank`);
|
|
9709
|
+
}
|
|
9710
|
+
return seat === "judges" ? [fallback] : fallback;
|
|
9711
|
+
}
|
|
9712
|
+
throw new SeatUnsetError(seat);
|
|
9713
|
+
}
|
|
9714
|
+
|
|
9715
|
+
// src/verdict-cache.ts
|
|
9716
|
+
import { createHash } from "crypto";
|
|
9717
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
|
|
9718
|
+
function canonicalizeAt(value, path) {
|
|
9719
|
+
if (value === null) return "null";
|
|
9720
|
+
switch (typeof value) {
|
|
9721
|
+
case "boolean":
|
|
9722
|
+
return value ? "true" : "false";
|
|
9723
|
+
case "number":
|
|
9724
|
+
if (!Number.isFinite(value)) {
|
|
9725
|
+
throw new Error(
|
|
9726
|
+
`canonicalJson: non-finite number (${value}) at ${path} \u2014 ambiguity is an error, not a coercion`
|
|
9727
|
+
);
|
|
9728
|
+
}
|
|
9729
|
+
return JSON.stringify(value);
|
|
9730
|
+
case "string":
|
|
9731
|
+
return JSON.stringify(value);
|
|
9732
|
+
case "undefined":
|
|
9733
|
+
case "function":
|
|
9734
|
+
case "symbol":
|
|
9735
|
+
throw new Error(
|
|
9736
|
+
`canonicalJson: ${typeof value} at ${path} \u2014 ambiguity is an error, not a coercion`
|
|
9737
|
+
);
|
|
9738
|
+
case "bigint":
|
|
9739
|
+
throw new Error(`canonicalJson: bigint at ${path} \u2014 not representable in JSON`);
|
|
9740
|
+
case "object":
|
|
9741
|
+
break;
|
|
9742
|
+
}
|
|
9743
|
+
const obj = value;
|
|
9744
|
+
if (typeof obj["toJSON"] === "function") {
|
|
9745
|
+
return canonicalizeAt(obj.toJSON(), path);
|
|
9746
|
+
}
|
|
9747
|
+
if (Array.isArray(obj)) {
|
|
9748
|
+
return `[${obj.map((item, i) => canonicalizeAt(item, `${path}[${i}]`)).join(",")}]`;
|
|
9749
|
+
}
|
|
9750
|
+
if (obj instanceof Map || obj instanceof Set) {
|
|
9751
|
+
throw new Error(
|
|
9752
|
+
`canonicalJson: ${obj instanceof Map ? "Map" : "Set"} at ${path} \u2014 would serialize as '{}'; convert to a plain object/array first`
|
|
9753
|
+
);
|
|
9754
|
+
}
|
|
9755
|
+
const keys = Object.keys(obj).sort();
|
|
9756
|
+
const parts = keys.map((k) => `${JSON.stringify(k)}:${canonicalizeAt(obj[k], `${path}.${k}`)}`);
|
|
9757
|
+
return `{${parts.join(",")}}`;
|
|
9758
|
+
}
|
|
9759
|
+
function canonicalJson(value) {
|
|
9760
|
+
return canonicalizeAt(value, "$");
|
|
9761
|
+
}
|
|
9762
|
+
function contentHash(value) {
|
|
9763
|
+
return createHash("sha256").update(canonicalJson(value)).digest("hex");
|
|
9764
|
+
}
|
|
9765
|
+
function inMemoryVerdictCache() {
|
|
9766
|
+
const entries = /* @__PURE__ */ new Map();
|
|
9767
|
+
return {
|
|
9768
|
+
get: (key) => entries.get(key),
|
|
9769
|
+
set: (key, score) => {
|
|
9770
|
+
entries.set(key, score);
|
|
9771
|
+
}
|
|
9772
|
+
};
|
|
9773
|
+
}
|
|
9774
|
+
function parseCacheLine(line, path, lineNo) {
|
|
9775
|
+
let parsed;
|
|
9776
|
+
try {
|
|
9777
|
+
parsed = JSON.parse(line);
|
|
9778
|
+
} catch (err) {
|
|
9779
|
+
throw new Error(
|
|
9780
|
+
`fileVerdictCache: corrupt JSONL at ${path}:${lineNo} \u2014 ${err instanceof Error ? err.message : String(err)}`
|
|
9781
|
+
);
|
|
9782
|
+
}
|
|
9783
|
+
const rec = parsed;
|
|
9784
|
+
if (typeof rec !== "object" || rec === null || typeof rec.key !== "string" || typeof rec.score !== "object" || rec.score === null || typeof rec.score.composite !== "number" || typeof rec.score.dimensions !== "object") {
|
|
9785
|
+
throw new Error(
|
|
9786
|
+
`fileVerdictCache: invalid record shape at ${path}:${lineNo} \u2014 expected {key, score:{dimensions, composite, notes}}`
|
|
9787
|
+
);
|
|
9788
|
+
}
|
|
9789
|
+
return rec;
|
|
9790
|
+
}
|
|
9791
|
+
function fileVerdictCache(path) {
|
|
9792
|
+
const entries = /* @__PURE__ */ new Map();
|
|
9793
|
+
if (existsSync5(path)) {
|
|
9794
|
+
const lines = readFileSync6(path, "utf8").split("\n");
|
|
9795
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9796
|
+
const line = lines[i];
|
|
9797
|
+
if (line === void 0 || line.trim() === "") continue;
|
|
9798
|
+
const rec = parseCacheLine(line, path, i + 1);
|
|
9799
|
+
entries.set(rec.key, rec.score);
|
|
9800
|
+
}
|
|
9801
|
+
}
|
|
9802
|
+
return {
|
|
9803
|
+
get: (key) => entries.get(key),
|
|
9804
|
+
set: (key, score) => {
|
|
9805
|
+
appendFileSync3(path, `${JSON.stringify({ key, score })}
|
|
9806
|
+
`, "utf8");
|
|
9807
|
+
entries.set(key, score);
|
|
9808
|
+
}
|
|
9809
|
+
};
|
|
9810
|
+
}
|
|
9811
|
+
function cachedJudge(judge, store, options) {
|
|
9812
|
+
if (typeof options.judgeVersion !== "string" || options.judgeVersion.trim() === "") {
|
|
9813
|
+
throw new Error("cachedJudge: judgeVersion is required and must be a non-empty string");
|
|
9814
|
+
}
|
|
9815
|
+
const stats = { hits: 0, misses: 0 };
|
|
9816
|
+
const wrapped = {
|
|
9817
|
+
name: judge.name,
|
|
9818
|
+
dimensions: judge.dimensions,
|
|
9819
|
+
async score(input) {
|
|
9820
|
+
const key = contentHash({
|
|
9821
|
+
artifact: canonicalJson(input.artifact),
|
|
9822
|
+
scenarioId: input.scenario.id,
|
|
9823
|
+
judgeName: judge.name,
|
|
9824
|
+
dimensions: judge.dimensions,
|
|
9825
|
+
judgeVersion: options.judgeVersion
|
|
9826
|
+
});
|
|
9827
|
+
const cached = await store.get(key);
|
|
9828
|
+
if (cached !== void 0) {
|
|
9829
|
+
stats.hits += 1;
|
|
9830
|
+
return cached;
|
|
9831
|
+
}
|
|
9832
|
+
const score = await judge.score(input);
|
|
9833
|
+
await store.set(key, score);
|
|
9834
|
+
stats.misses += 1;
|
|
9835
|
+
return score;
|
|
9836
|
+
},
|
|
9837
|
+
stats: () => ({ ...stats })
|
|
9838
|
+
};
|
|
9839
|
+
if (judge.appliesTo) wrapped.appliesTo = judge.appliesTo;
|
|
9840
|
+
return wrapped;
|
|
9841
|
+
}
|
|
9842
|
+
|
|
9843
|
+
// src/attestation.ts
|
|
9844
|
+
var ATTESTATION_ALGORITHM = "sha256/canonical-json";
|
|
9845
|
+
function attest(report, provenance) {
|
|
9846
|
+
return {
|
|
9847
|
+
reportHash: contentHash(report),
|
|
9848
|
+
provenance,
|
|
9849
|
+
algorithm: ATTESTATION_ALGORITHM
|
|
9850
|
+
};
|
|
9851
|
+
}
|
|
9852
|
+
function verifyAttestation(report, attested) {
|
|
9853
|
+
if (attested.algorithm !== ATTESTATION_ALGORITHM) {
|
|
9854
|
+
return {
|
|
9855
|
+
valid: false,
|
|
9856
|
+
reason: `unknown algorithm '${attested.algorithm}' \u2014 this verifier only checks '${ATTESTATION_ALGORITHM}'`
|
|
9857
|
+
};
|
|
9858
|
+
}
|
|
9859
|
+
let recomputed;
|
|
9860
|
+
try {
|
|
9861
|
+
recomputed = contentHash(report);
|
|
9862
|
+
} catch (err) {
|
|
9863
|
+
return {
|
|
9864
|
+
valid: false,
|
|
9865
|
+
reason: `report is not canonicalizable: ${err instanceof Error ? err.message : String(err)}`
|
|
9866
|
+
};
|
|
9867
|
+
}
|
|
9868
|
+
if (recomputed !== attested.reportHash) {
|
|
9869
|
+
return {
|
|
9870
|
+
valid: false,
|
|
9871
|
+
reason: `report hash mismatch: attested ${attested.reportHash}, recomputed ${recomputed}`
|
|
9872
|
+
};
|
|
9873
|
+
}
|
|
9874
|
+
return { valid: true };
|
|
9875
|
+
}
|
|
9097
9876
|
export {
|
|
9098
9877
|
AGENT_PROFILE_KINDS,
|
|
9878
|
+
ATTESTATION_ALGORITHM,
|
|
9099
9879
|
AgentDriver,
|
|
9100
9880
|
AgentEvalError,
|
|
9101
9881
|
AgentProfileCellValidationError,
|
|
@@ -9150,6 +9930,7 @@ export {
|
|
|
9150
9930
|
InMemoryTraceStore,
|
|
9151
9931
|
InMemoryWorkspaceInspector,
|
|
9152
9932
|
JudgeError,
|
|
9933
|
+
JudgeParseError,
|
|
9153
9934
|
JudgeRunner,
|
|
9154
9935
|
KNOWLEDGE_GAP_KIND_SPEC,
|
|
9155
9936
|
KNOWLEDGE_POISONING_KIND_SPEC,
|
|
@@ -9182,6 +9963,7 @@ export {
|
|
|
9182
9963
|
SKILL_USAGE_ANALYST,
|
|
9183
9964
|
SandboxHarness,
|
|
9184
9965
|
ScenarioRegistry,
|
|
9966
|
+
SeatUnsetError,
|
|
9185
9967
|
SingleBackendError,
|
|
9186
9968
|
SkillUsageAnalyst,
|
|
9187
9969
|
SpanNotFoundError,
|
|
@@ -9192,6 +9974,7 @@ export {
|
|
|
9192
9974
|
TRACE_ANALYST_TRUNCATION_MARKER_PREFIX,
|
|
9193
9975
|
TRACE_SCHEMA_VERSION,
|
|
9194
9976
|
TokenCounter,
|
|
9977
|
+
TraceContractBuilder,
|
|
9195
9978
|
TraceEmitter,
|
|
9196
9979
|
TraceFileMissingError,
|
|
9197
9980
|
TraceNotFoundError,
|
|
@@ -9221,12 +10004,15 @@ export {
|
|
|
9221
10004
|
assertLlmRoute,
|
|
9222
10005
|
assertModelsServed,
|
|
9223
10006
|
assertRealBackend,
|
|
10007
|
+
assertRecordIntegrity,
|
|
9224
10008
|
assertReleaseConfidence,
|
|
9225
10009
|
assertRunAgentProfileCell,
|
|
9226
10010
|
assertRunCaptured,
|
|
9227
10011
|
assertSingleBackend,
|
|
9228
10012
|
assignFeedbackSplit,
|
|
9229
10013
|
assignHeldOutTag,
|
|
10014
|
+
attachCostToReport,
|
|
10015
|
+
attest,
|
|
9230
10016
|
attributeCounterfactuals,
|
|
9231
10017
|
backoffMs,
|
|
9232
10018
|
deterministicSplit as benchmarkDeterministicSplit,
|
|
@@ -9248,17 +10034,21 @@ export {
|
|
|
9248
10034
|
buildTraceInsightPrompt,
|
|
9249
10035
|
buildTrajectory,
|
|
9250
10036
|
byteLengthRange,
|
|
10037
|
+
cachedJudge,
|
|
9251
10038
|
calibrateJudge,
|
|
9252
10039
|
calibrateJudgeContinuous,
|
|
9253
10040
|
callLlm,
|
|
9254
10041
|
callLlmJson,
|
|
9255
10042
|
canaryLeakView,
|
|
10043
|
+
canonicalJson,
|
|
9256
10044
|
canonicalize,
|
|
9257
10045
|
captureFetchToRawSink,
|
|
9258
10046
|
causalAttribution,
|
|
9259
10047
|
checkBehavioralCanary,
|
|
9260
10048
|
checkCanaries,
|
|
10049
|
+
checkRecordIntegrity,
|
|
9261
10050
|
checkSlos,
|
|
10051
|
+
checkTraceContracts,
|
|
9262
10052
|
clamp01,
|
|
9263
10053
|
classifyEuAiRisk,
|
|
9264
10054
|
classifyFailure,
|
|
@@ -9272,6 +10062,7 @@ export {
|
|
|
9272
10062
|
compareReferenceReplay,
|
|
9273
10063
|
compareToBaseline,
|
|
9274
10064
|
compilerJudge,
|
|
10065
|
+
completionVerdict,
|
|
9275
10066
|
composeParsers,
|
|
9276
10067
|
composeValidators,
|
|
9277
10068
|
computeExperimentStats,
|
|
@@ -9280,7 +10071,9 @@ export {
|
|
|
9280
10071
|
computeTraceMetrics,
|
|
9281
10072
|
confidenceInterval,
|
|
9282
10073
|
containsAll,
|
|
10074
|
+
contentHash,
|
|
9283
10075
|
continuousAgreement,
|
|
10076
|
+
contractJudge,
|
|
9284
10077
|
controlFailureClassFromVerification,
|
|
9285
10078
|
controlRunToFeedbackTrajectory,
|
|
9286
10079
|
controlRunToRunRecord,
|
|
@@ -9288,6 +10081,7 @@ export {
|
|
|
9288
10081
|
corpusInterRaterAgreement,
|
|
9289
10082
|
corpusInterRaterAgreementFromJudgeScores,
|
|
9290
10083
|
costForUsage,
|
|
10084
|
+
costReport,
|
|
9291
10085
|
createAnalystAi,
|
|
9292
10086
|
createAntiSlopJudge,
|
|
9293
10087
|
createChatClient,
|
|
@@ -9326,6 +10120,8 @@ export {
|
|
|
9326
10120
|
distillPlaybook,
|
|
9327
10121
|
domainEvidencePattern,
|
|
9328
10122
|
dominates,
|
|
10123
|
+
eProcess,
|
|
10124
|
+
ensembleJudge,
|
|
9329
10125
|
estimateCost,
|
|
9330
10126
|
estimateTokens,
|
|
9331
10127
|
euAiActReport,
|
|
@@ -9335,7 +10131,9 @@ export {
|
|
|
9335
10131
|
evaluateInterimReleaseConfidence,
|
|
9336
10132
|
evaluateOracles,
|
|
9337
10133
|
evaluateReleaseConfidence,
|
|
10134
|
+
evaluateTraceContract,
|
|
9338
10135
|
executeScenario,
|
|
10136
|
+
expandMatrix,
|
|
9339
10137
|
expectAgent,
|
|
9340
10138
|
exportRewardModel,
|
|
9341
10139
|
exportRunAsOtlp,
|
|
@@ -9354,6 +10152,7 @@ export {
|
|
|
9354
10152
|
fileContains,
|
|
9355
10153
|
fileExists,
|
|
9356
10154
|
fileExperimentStore,
|
|
10155
|
+
fileVerdictCache,
|
|
9357
10156
|
findAutoMatchNoExpectation,
|
|
9358
10157
|
findConstructorCwdDropped,
|
|
9359
10158
|
findFallbackToPass,
|
|
@@ -9369,6 +10168,7 @@ export {
|
|
|
9369
10168
|
formatFindings,
|
|
9370
10169
|
formatScorecardDiff,
|
|
9371
10170
|
gainHistogram,
|
|
10171
|
+
gatePerf,
|
|
9372
10172
|
ghCliClient,
|
|
9373
10173
|
gitProvenanceReader,
|
|
9374
10174
|
precision as goldenPrecision,
|
|
@@ -9386,6 +10186,7 @@ export {
|
|
|
9386
10186
|
inMemoryReferenceReplayStore,
|
|
9387
10187
|
inMemoryReviewStore,
|
|
9388
10188
|
inMemoryRunRecordBackend,
|
|
10189
|
+
inMemoryVerdictCache,
|
|
9389
10190
|
inferDomainKeywords,
|
|
9390
10191
|
inferOtlpKind,
|
|
9391
10192
|
interRaterReliability,
|
|
@@ -9420,13 +10221,16 @@ export {
|
|
|
9420
10221
|
loadScorerFromGrader,
|
|
9421
10222
|
localCommandRunner,
|
|
9422
10223
|
lowercaseMutator,
|
|
10224
|
+
makeEvalTools,
|
|
9423
10225
|
makeFinding,
|
|
9424
10226
|
mannWhitneyU,
|
|
9425
10227
|
matchGoldens,
|
|
10228
|
+
matchSpan,
|
|
9426
10229
|
mergeLayerResults,
|
|
9427
10230
|
mergeSteeringBundle,
|
|
9428
10231
|
modelDescriptionBits,
|
|
9429
10232
|
modelPriceKey,
|
|
10233
|
+
mulberry32,
|
|
9430
10234
|
multiToolchainLayer,
|
|
9431
10235
|
nistAiRmfReport,
|
|
9432
10236
|
normalizeScores,
|
|
@@ -9495,6 +10299,7 @@ export {
|
|
|
9495
10299
|
researchReport,
|
|
9496
10300
|
resetLockedAppendersForTesting,
|
|
9497
10301
|
resolveModelPricing,
|
|
10302
|
+
resolveSeat,
|
|
9498
10303
|
roundTripRunRecord,
|
|
9499
10304
|
rowCount,
|
|
9500
10305
|
rowWhere,
|
|
@@ -9524,6 +10329,7 @@ export {
|
|
|
9524
10329
|
runsForScenario,
|
|
9525
10330
|
scalarScore,
|
|
9526
10331
|
scanForMuffledGates,
|
|
10332
|
+
scenarioKey,
|
|
9527
10333
|
scoreContinuity,
|
|
9528
10334
|
scoreFromEvals,
|
|
9529
10335
|
scoreKnowledgeReadiness,
|
|
@@ -9532,6 +10338,7 @@ export {
|
|
|
9532
10338
|
scoreRedTeamOutput,
|
|
9533
10339
|
scoreReferenceReplay,
|
|
9534
10340
|
scoreTraceInsightReadiness,
|
|
10341
|
+
seatPresets,
|
|
9535
10342
|
securityJudge,
|
|
9536
10343
|
selectHarnessVariant,
|
|
9537
10344
|
selfPreference,
|
|
@@ -9551,18 +10358,21 @@ export {
|
|
|
9551
10358
|
summarizeHarnessResults,
|
|
9552
10359
|
summarizePrReviewBenchmark,
|
|
9553
10360
|
summarizePreferenceMemory,
|
|
10361
|
+
summarizeRecords,
|
|
9554
10362
|
summaryTable,
|
|
9555
10363
|
testJudge,
|
|
9556
10364
|
textInSnapshot,
|
|
9557
10365
|
throwIfRunIncomplete,
|
|
9558
10366
|
toAgentProfileJson,
|
|
9559
10367
|
toLangfuseEnvelope,
|
|
10368
|
+
toOpenAiTool,
|
|
9560
10369
|
toPrometheusText,
|
|
9561
10370
|
tokenizeDomainWords,
|
|
9562
10371
|
toolNamesForRun,
|
|
9563
10372
|
toolSpans,
|
|
9564
10373
|
traceAnalystFunctionGroup,
|
|
9565
10374
|
traceAnalystOnRunComplete,
|
|
10375
|
+
traceContract,
|
|
9566
10376
|
traceJudge,
|
|
9567
10377
|
traceJudgeEnsemble,
|
|
9568
10378
|
tracedAnalyzeTraces,
|
|
@@ -9573,6 +10383,7 @@ export {
|
|
|
9573
10383
|
validateRunRecord,
|
|
9574
10384
|
verbosityBias,
|
|
9575
10385
|
verifyAgentProfileCell,
|
|
10386
|
+
verifyAttestation,
|
|
9576
10387
|
verifyCompletion,
|
|
9577
10388
|
verifyManifest,
|
|
9578
10389
|
visualDiff,
|