@iris-eval/mcp-server 0.8.2 → 0.10.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/README.md +9 -2
- package/dist/capabilities.d.ts +64 -0
- package/dist/capabilities.js +65 -0
- package/dist/config/defaults.js +17 -0
- package/dist/custom-rule-store.d.ts +4 -0
- package/dist/custom-rule-store.js +8 -3
- package/dist/dashboard/assets/{index-CyzO6OC7.js → index-CeJbaq6m.js} +1 -1
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/routes/capabilities.d.ts +3 -0
- package/dist/dashboard/routes/capabilities.js +11 -0
- package/dist/dashboard/routes/health.d.ts +5 -1
- package/dist/dashboard/routes/health.js +15 -3
- package/dist/dashboard/routes/rules.js +4 -1
- package/dist/dashboard/routes/traces.d.ts +3 -0
- package/dist/dashboard/routes/traces.js +11 -30
- package/dist/dashboard/seed-demo-data.js +1 -1
- package/dist/dashboard/server.d.ts +2 -0
- package/dist/dashboard/server.js +6 -2
- package/dist/eval/accuracy.d.ts +41 -0
- package/dist/eval/accuracy.js +97 -0
- package/dist/eval/citation-verify/verifier.d.ts +16 -1
- package/dist/eval/citation-verify/verifier.js +14 -4
- package/dist/eval/compose.d.ts +57 -0
- package/dist/eval/compose.js +179 -0
- package/dist/eval/criticality.d.ts +15 -1
- package/dist/eval/criticality.js +6 -0
- package/dist/eval/decision-moment.js +33 -4
- package/dist/eval/dormant.d.ts +4 -0
- package/dist/eval/dormant.js +22 -0
- package/dist/eval/engine.d.ts +6 -2
- package/dist/eval/engine.js +126 -12
- package/dist/eval/failure-classes.d.ts +8 -0
- package/dist/eval/failure-classes.js +18 -0
- package/dist/eval/llm-judge/evaluator.d.ts +30 -0
- package/dist/eval/llm-judge/evaluator.js +26 -2
- package/dist/eval/published-accuracy.d.ts +230 -0
- package/dist/eval/published-accuracy.js +86 -0
- package/dist/eval/questions.d.ts +12 -0
- package/dist/eval/questions.js +14 -0
- package/dist/eval/response-schema.d.ts +652 -0
- package/dist/eval/response-schema.js +130 -0
- package/dist/eval/response.d.ts +12 -0
- package/dist/eval/response.js +30 -0
- package/dist/eval/risk.d.ts +60 -0
- package/dist/eval/risk.js +187 -0
- package/dist/eval/rules/completeness.js +36 -1
- package/dist/eval/rules/cost.d.ts +2 -2
- package/dist/eval/rules/cost.js +50 -6
- package/dist/eval/rules/custom.d.ts +0 -12
- package/dist/eval/rules/custom.js +22 -0
- package/dist/eval/rules/relevance.js +23 -2
- package/dist/eval/rules/safety.d.ts +6 -2
- package/dist/eval/rules/safety.js +224 -51
- package/dist/eval/seeded-random.d.ts +4 -0
- package/dist/eval/seeded-random.js +36 -0
- package/dist/eval/stamp.d.ts +14 -0
- package/dist/eval/stamp.js +89 -0
- package/dist/eval/stats.d.ts +33 -0
- package/dist/eval/stats.js +109 -0
- package/dist/eval/text/checksums.d.ts +23 -0
- package/dist/eval/text/checksums.js +97 -0
- package/dist/eval/text/normalise.d.ts +30 -0
- package/dist/eval/text/normalise.js +265 -0
- package/dist/eval/text/sentences.d.ts +15 -0
- package/dist/eval/text/sentences.js +149 -0
- package/dist/eval/verdict.d.ts +34 -0
- package/dist/eval/verdict.js +131 -0
- package/dist/index.js +5 -28
- package/dist/instructions.d.ts +17 -0
- package/dist/instructions.js +53 -0
- package/dist/judge-enablement.d.ts +34 -0
- package/dist/judge-enablement.js +78 -0
- package/dist/judge-enablement.json +10 -0
- package/dist/preferences.d.ts +1 -1
- package/dist/prompts.d.ts +3 -0
- package/dist/prompts.js +29 -0
- package/dist/resources/index.d.ts +5 -2
- package/dist/resources/index.js +65 -5
- package/dist/resources/uris.d.ts +12 -0
- package/dist/resources/uris.js +24 -0
- package/dist/retention.d.ts +20 -0
- package/dist/retention.js +44 -0
- package/dist/self-test.d.ts +1 -0
- package/dist/self-test.js +17 -3
- package/dist/server.d.ts +10 -1
- package/dist/server.js +34 -7
- package/dist/storage/index.js +1 -1
- package/dist/storage/migrations/007-eval-provenance.d.ts +3 -0
- package/dist/storage/migrations/007-eval-provenance.js +30 -0
- package/dist/storage/migrations/index.js +24 -4
- package/dist/storage/sqlite-adapter.d.ts +26 -1
- package/dist/storage/sqlite-adapter.js +149 -15
- package/dist/tools/delete-rule.d.ts +8 -0
- package/dist/tools/delete-rule.js +30 -38
- package/dist/tools/delete-trace.d.ts +5 -0
- package/dist/tools/delete-trace.js +24 -27
- package/dist/tools/deploy-rule.d.ts +13 -1
- package/dist/tools/deploy-rule.js +37 -34
- package/dist/tools/describe.d.ts +20 -0
- package/dist/tools/describe.js +36 -0
- package/dist/tools/errors.d.ts +36 -0
- package/dist/tools/errors.js +134 -0
- package/dist/tools/evaluate-output.d.ts +8 -1
- package/dist/tools/evaluate-output.js +39 -60
- package/dist/tools/evaluate-with-llm-judge.d.ts +34 -0
- package/dist/tools/evaluate-with-llm-judge.js +124 -69
- package/dist/tools/get-traces.d.ts +9 -0
- package/dist/tools/get-traces.js +29 -28
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.js +22 -1
- package/dist/tools/list-rules.d.ts +13 -0
- package/dist/tools/list-rules.js +43 -46
- package/dist/tools/log-trace.d.ts +4 -0
- package/dist/tools/log-trace.js +31 -29
- package/dist/tools/respond.d.ts +42 -0
- package/dist/tools/respond.js +90 -0
- package/dist/tools/strict-input.js +1 -1
- package/dist/tools/trace-link.d.ts +2 -0
- package/dist/tools/trace-link.js +13 -2
- package/dist/tools/verify-citations.d.ts +18 -2
- package/dist/tools/verify-citations.js +122 -96
- package/dist/types/config.d.ts +44 -0
- package/dist/types/eval.d.ts +309 -0
- package/dist/types/eval.js +2 -1
- package/dist/types/query.d.ts +2 -0
- package/package.json +1 -1
- package/server.json +2 -2
- package/dist/resources/dashboard-summary.d.ts +0 -3
- package/dist/resources/dashboard-summary.js +0 -16
- package/dist/resources/trace-detail.d.ts +0 -3
- package/dist/resources/trace-detail.js +0 -30
package/dist/types/config.d.ts
CHANGED
|
@@ -2,6 +2,13 @@ export interface IrisConfig {
|
|
|
2
2
|
storage: {
|
|
3
3
|
type: 'sqlite';
|
|
4
4
|
path: string;
|
|
5
|
+
/**
|
|
6
|
+
* `critical_spans` stores each evaluation's output text with the spans a
|
|
7
|
+
* critical detector flagged replaced by `[REDACTED:<pattern>]`, so a tool
|
|
8
|
+
* that detects leaks need not keep the leak it found. The evidence
|
|
9
|
+
* offsets still index the original text the caller saw. Default `none`.
|
|
10
|
+
*/
|
|
11
|
+
redact?: 'none' | 'critical_spans';
|
|
5
12
|
};
|
|
6
13
|
server: {
|
|
7
14
|
name: string;
|
|
@@ -47,12 +54,49 @@ export interface IrisConfig {
|
|
|
47
54
|
* name in both lists is a config error: it does not say what you want.
|
|
48
55
|
*/
|
|
49
56
|
nonCriticalRules?: string[];
|
|
57
|
+
/** `risk` composes by kind (gates, vetoes, unknown, then the risk); `legacy` runs the pre-0.10.0 weighted mean. */
|
|
58
|
+
composer?: 'risk' | 'legacy';
|
|
59
|
+
/**
|
|
60
|
+
* How many wrongly blocked builds one shipped failure is worth. The risk
|
|
61
|
+
* threshold is 1 / (1 + this), so 1 means a false pass and a false block
|
|
62
|
+
* cost the same; a continuous-integration gate that hates flakiness sets
|
|
63
|
+
* it low, a compliance gate sets it high.
|
|
64
|
+
*/
|
|
65
|
+
falsePassCost?: number;
|
|
66
|
+
/**
|
|
67
|
+
* What a critical rule that was ASKED and could not answer does to the
|
|
68
|
+
* verdict — defeated by the output, or configured invalidly. Not the
|
|
69
|
+
* same as never asked, which is coverage. Today's behaviour is `pass`,
|
|
70
|
+
* which is the fail-open seam; the default is `unknown`.
|
|
71
|
+
*/
|
|
72
|
+
onCriticalSkipped?: 'unknown' | 'fail' | 'pass';
|
|
73
|
+
/** Inputs every evaluation must carry; an absent one makes the verdict unknown rather than clean. */
|
|
74
|
+
requiredEvidence?: string[];
|
|
75
|
+
/**
|
|
76
|
+
* Whether a threshold IRIS ships decides the verdict, or only advises.
|
|
77
|
+
* A default is our guess about a deployment we have never seen; a
|
|
78
|
+
* threshold you set is your decision. A policy with no number in it —
|
|
79
|
+
* "the output is empty" — gates either way.
|
|
80
|
+
*/
|
|
81
|
+
defaultsGate?: boolean;
|
|
82
|
+
/** The prior that an output is bad before any rule speaks. 0.5 matches the proof corpus, not your traffic. */
|
|
83
|
+
prior?: number;
|
|
84
|
+
/**
|
|
85
|
+
* How that prior is spread over the failure classes the detectors
|
|
86
|
+
* examine. `per-output` keeps it at the stated value for the output as a
|
|
87
|
+
* whole; `per-class` applies it to each class independently, which makes
|
|
88
|
+
* installing another detector raise the prior before that detector has
|
|
89
|
+
* looked at anything.
|
|
90
|
+
*/
|
|
91
|
+
priorMode?: 'per-output' | 'per-class';
|
|
50
92
|
};
|
|
51
93
|
logging: {
|
|
52
94
|
level: 'debug' | 'info' | 'warn' | 'error';
|
|
53
95
|
};
|
|
54
96
|
retention: {
|
|
55
97
|
days: number;
|
|
98
|
+
/** How often the sweep re-runs after boot, in hours; 0 disables the timer (the boot sweep still runs). Default 24. */
|
|
99
|
+
sweepIntervalHours: number;
|
|
56
100
|
};
|
|
57
101
|
security: {
|
|
58
102
|
apiKey?: string;
|
package/dist/types/eval.d.ts
CHANGED
|
@@ -8,6 +8,23 @@ export type EvalType = 'completeness' | 'relevance' | 'safety' | 'cost' | 'custo
|
|
|
8
8
|
* under a real bundle, never under 'all'.
|
|
9
9
|
*/
|
|
10
10
|
export type EvalResultType = EvalType | 'all';
|
|
11
|
+
/**
|
|
12
|
+
* What KIND of claim a rule makes — the mandate's distinction between a
|
|
13
|
+
* measurement (a statistic against a threshold), a detection (a pattern is
|
|
14
|
+
* present, with a measured error rate), an inference (a signal standing in
|
|
15
|
+
* for an unobservable property), a judgment (a model's reasoning), a policy
|
|
16
|
+
* (the deployment's own constraint) and an external verification. Kind is
|
|
17
|
+
* the claim; `mechanism` is how the claim is measured. The composer decides
|
|
18
|
+
* by kind and never averages kinds together.
|
|
19
|
+
*/
|
|
20
|
+
export type ClaimKind = 'measurement' | 'detection' | 'inference' | 'judgment' | 'policy' | 'verification';
|
|
21
|
+
export type Mechanism = 'formula' | 'pattern' | 'heuristic' | 'model' | 'external';
|
|
22
|
+
/** An input a rule reads. A rule skips — never passes — when a declared need is absent. */
|
|
23
|
+
export type Need = 'output' | 'input' | 'expected' | 'tool_calls' | 'tool_outputs' | 'tools_catalogue' | 'cost' | 'tokens' | 'citations';
|
|
24
|
+
/** The evaluation question a rule answers; the registry is src/eval/questions.ts. */
|
|
25
|
+
export type QuestionId = 'safe_output' | 'grounded' | 'complete' | 'relevant' | 'task_completed' | 'tool_use_correct' | 'within_budget';
|
|
26
|
+
/** What went wrong, in the reader's words, independent of which rule caught it; the registry is src/eval/failure-classes.ts. */
|
|
27
|
+
export type FailureClass = 'pii_leak' | 'credential_leak' | 'injection' | 'injection_compliance' | 'silent_tool_failure' | 'tool_loop' | 'stub' | 'fabrication' | 'ungrounded' | 'incomplete_ask' | 'off_task' | 'over_budget' | 'format' | 'invalid_tool_call';
|
|
11
28
|
export interface EvalRule {
|
|
12
29
|
name: string;
|
|
13
30
|
description: string;
|
|
@@ -25,6 +42,31 @@ export interface EvalRule {
|
|
|
25
42
|
* is the verdict, and a critical violation must never be averaged away.
|
|
26
43
|
*/
|
|
27
44
|
critical?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* The rule's metadata — what kind of claim it makes, how it measures it,
|
|
47
|
+
* what it reads, which question it answers, which failure classes a
|
|
48
|
+
* failing result belongs to, and the version of its definition. Every
|
|
49
|
+
* built-in declares all six (tests/unit/eval/rule-metadata.test.ts);
|
|
50
|
+
* custom types declare kind, mechanism, needs and version and leave the
|
|
51
|
+
* question to their author. Optional on the interface so a rule built
|
|
52
|
+
* elsewhere still compiles; a result from a rule without them carries no
|
|
53
|
+
* `kind`, which reads as unknown — never as a measurement.
|
|
54
|
+
*/
|
|
55
|
+
kind?: ClaimKind;
|
|
56
|
+
mechanism?: Mechanism;
|
|
57
|
+
needs?: readonly Need[];
|
|
58
|
+
question?: QuestionId;
|
|
59
|
+
classes?: readonly FailureClass[];
|
|
60
|
+
/** Bumped when the rule's meaning changes, so a stored result names the definition that produced it. */
|
|
61
|
+
version?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Who wrote this rule. `custom` marks anything `createCustomRule`
|
|
64
|
+
* produced — a deployed rule or one passed inline in the call. The
|
|
65
|
+
* composer needs it: for OUR rule a shipped threshold is a guess and only
|
|
66
|
+
* advises, while for THEIRS the severity they deployed it at is their own
|
|
67
|
+
* statement of how much it matters. Absent means built-in.
|
|
68
|
+
*/
|
|
69
|
+
origin?: 'built-in' | 'custom';
|
|
28
70
|
evaluate(context: EvalContext): EvalRuleResult;
|
|
29
71
|
}
|
|
30
72
|
export interface EvalContext {
|
|
@@ -63,9 +105,263 @@ export interface EvalContext {
|
|
|
63
105
|
regexBudget?: {
|
|
64
106
|
breaches: number;
|
|
65
107
|
};
|
|
108
|
+
/**
|
|
109
|
+
|
|
110
|
+
* Whether this evaluation may call a paid provider. Set ONLY by the tools
|
|
111
|
+
|
|
112
|
+
* whose whole purpose is to do so — the LLM judge and the citation
|
|
113
|
+
|
|
114
|
+
* verifier. The engine refuses to run a judgment rule without it, which
|
|
115
|
+
|
|
116
|
+
* is what makes "evaluate_output never spends" a property of the engine
|
|
117
|
+
|
|
118
|
+
* rather than a promise in a tool description.
|
|
119
|
+
|
|
120
|
+
*/
|
|
121
|
+
allowPaid?: boolean;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* What the composer DID with a result under this deployment's configuration
|
|
125
|
+
* — distinct from `kind`, which is what the rule claims. Today's composer
|
|
126
|
+
* (a weighted mean plus the critical veto) knows two roles: `veto` for an
|
|
127
|
+
* effectively critical rule and `term` for one that feeds the score. The
|
|
128
|
+
* compose-by-kind release adds `gate` (a configured policy that decides),
|
|
129
|
+
* `risk` (a detection or inference feeding the risk estimate) and
|
|
130
|
+
* `advisory` (reported, deciding nothing).
|
|
131
|
+
*/
|
|
132
|
+
export type Role = 'gate' | 'veto' | 'risk' | 'advisory' | 'term';
|
|
133
|
+
/**
|
|
134
|
+
* Why a rule skipped. `not_applicable`: the evidence it needs was not
|
|
135
|
+
* supplied (never asked — coverage). `defeated`: asked and could not answer,
|
|
136
|
+
* because this output stalled its pattern past the sandbox budget.
|
|
137
|
+
* `config_invalid`: asked and could not answer, because its definition is
|
|
138
|
+
* broken. A gate that fails closed treats the last two as unknown; the first
|
|
139
|
+
* is a coverage fact, not a verdict.
|
|
140
|
+
*/
|
|
141
|
+
export type SkipClass = 'not_applicable' | 'defeated' | 'config_invalid';
|
|
142
|
+
export interface Interval {
|
|
143
|
+
point: number;
|
|
144
|
+
lo: number;
|
|
145
|
+
hi: number;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* What a rule saw — typed, locatable, never an excerpt. A detection reports
|
|
149
|
+
* the OFFSETS of what it matched (into the raw text, so a leak detector can
|
|
150
|
+
* redact the span it found without ever repeating it); a trajectory rule the
|
|
151
|
+
* index of the call it judged; a measurement its statistic with a unit and
|
|
152
|
+
* the threshold it was held to; a signal that yields no offset yet reports
|
|
153
|
+
* its name and count. The reader can locate every claim; the stored row
|
|
154
|
+
* can be redacted; nothing here restates the offending text.
|
|
155
|
+
*/
|
|
156
|
+
export type Evidence = {
|
|
157
|
+
type: 'span';
|
|
158
|
+
source: 'output' | 'input' | `tool_outputs[${number}]`;
|
|
159
|
+
start: number;
|
|
160
|
+
end: number;
|
|
161
|
+
label: string;
|
|
162
|
+
} | {
|
|
163
|
+
type: 'pattern';
|
|
164
|
+
name: string;
|
|
165
|
+
count: number;
|
|
166
|
+
} | {
|
|
167
|
+
type: 'toolCall';
|
|
168
|
+
index: number;
|
|
169
|
+
toolName: string;
|
|
170
|
+
label: string;
|
|
171
|
+
} | {
|
|
172
|
+
type: 'citation';
|
|
173
|
+
url: string;
|
|
174
|
+
status: 'resolved' | 'dead' | 'unverifiable' | 'supported' | 'unsupported';
|
|
175
|
+
} | {
|
|
176
|
+
type: 'count';
|
|
177
|
+
stat: string;
|
|
178
|
+
unit: string;
|
|
179
|
+
value: number;
|
|
180
|
+
threshold?: number;
|
|
181
|
+
thresholdSource?: 'default' | 'config' | 'call' | 'rule';
|
|
182
|
+
} | {
|
|
183
|
+
type: 'sample';
|
|
184
|
+
score: number;
|
|
185
|
+
selfReportedPass?: boolean;
|
|
186
|
+
rationaleHash: string;
|
|
187
|
+
};
|
|
188
|
+
/** A measurement's statistic — the number the rule computed, with its unit, before any score transform. */
|
|
189
|
+
export interface MeasuredValue {
|
|
190
|
+
stat: string;
|
|
191
|
+
unit: string;
|
|
192
|
+
value: number;
|
|
193
|
+
}
|
|
194
|
+
/** Evidence lists are capped so a pathological output cannot balloon a stored row. */
|
|
195
|
+
export declare const MAX_EVIDENCE_ITEMS = 25;
|
|
196
|
+
/**
|
|
197
|
+
* Which evaluation questions this evaluation judged, which it did not and
|
|
198
|
+
* why — coverage by question, not by rule count. `inputs` says what the
|
|
199
|
+
* call carried; a question is `judged` when at least one rule that answers
|
|
200
|
+
* it ran, `unjudged` when every such rule skipped (the reason names the
|
|
201
|
+
* missing input, or that the rule was defeated or broken), and
|
|
202
|
+
* `not_applicable` when no rule for it was in the selected bundles.
|
|
203
|
+
*/
|
|
204
|
+
export interface Coverage {
|
|
205
|
+
inputs: Record<Need, boolean>;
|
|
206
|
+
questions: Array<{
|
|
207
|
+
id: QuestionId;
|
|
208
|
+
status: 'judged' | 'unjudged' | 'not_applicable';
|
|
209
|
+
why?: string;
|
|
210
|
+
}>;
|
|
211
|
+
/** Quarantined critical rules that did not run (surfaced by the rule-store release). */
|
|
212
|
+
dormant?: Array<{
|
|
213
|
+
ruleId: string;
|
|
214
|
+
name: string;
|
|
215
|
+
reason: string;
|
|
216
|
+
}>;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The verdict with its basis. `passed` is `state === 'pass'` and equals the
|
|
220
|
+
* top-level `passed`; `basis` says which layer decided — a configured policy,
|
|
221
|
+
* a detector's veto, nothing judged, or the score against the threshold.
|
|
222
|
+
* `risk` is null until the compose-by-kind release computes it.
|
|
223
|
+
*/
|
|
224
|
+
/**
|
|
225
|
+
* A sentence a reader needs that the verdict alone does not carry, with who
|
|
226
|
+
* it is for and what to change. The one that must exist: when a rule
|
|
227
|
+
* visibly FIRED and the verdict still passed, say why and name the setting
|
|
228
|
+
* that would change it — "cost_under_threshold failed" beside
|
|
229
|
+
* "passed: true" reads as a bug to anyone who has not read the composer.
|
|
230
|
+
*
|
|
231
|
+
* `suggestions` remains for now and is rendered from these; it is deprecated
|
|
232
|
+
* from 0.13.0 and removed at 1.0, per VERSIONING.md's two-minor rule.
|
|
233
|
+
*/
|
|
234
|
+
export interface Interpretation {
|
|
235
|
+
severity: 'block' | 'warn' | 'note';
|
|
236
|
+
addressee: 'agent' | 'operator' | 'author';
|
|
237
|
+
/** The rule this is about, when it is about one. */
|
|
238
|
+
rule?: string;
|
|
239
|
+
text: string;
|
|
240
|
+
/** The configuration key that changes this behaviour, when there is one. */
|
|
241
|
+
configKey?: string;
|
|
242
|
+
}
|
|
243
|
+
/** Placed on EvalResult by the engine; see Interpretation above. */
|
|
244
|
+
export interface Verdict {
|
|
245
|
+
state: 'pass' | 'fail' | 'unknown';
|
|
246
|
+
passed: boolean;
|
|
247
|
+
basis: 'policy_gate' | 'detector_veto' | 'critical_unknown' | 'required_evidence_missing' | 'risk_over_loss' | 'score_below_threshold' | 'clean' | 'no_rules';
|
|
248
|
+
by: string[];
|
|
249
|
+
risk: {
|
|
250
|
+
pBad: number;
|
|
251
|
+
lo: number;
|
|
252
|
+
hi: number;
|
|
253
|
+
perClass: Partial<Record<FailureClass, number | null>>;
|
|
254
|
+
assumptions: string[];
|
|
255
|
+
} | null;
|
|
256
|
+
confidence?: 'decisive' | 'marginal';
|
|
257
|
+
}
|
|
258
|
+
/** What produced this verdict, so it can be replayed or compared: the release, the ruleset, the configuration, the thresholds, the proof corpus, the time. */
|
|
259
|
+
export interface Provenance {
|
|
260
|
+
irisVersion: string;
|
|
261
|
+
rulesetHash: string;
|
|
262
|
+
configHash: string;
|
|
263
|
+
thresholds: {
|
|
264
|
+
default: number;
|
|
265
|
+
perRule?: Record<string, unknown>;
|
|
266
|
+
};
|
|
267
|
+
corpusVersion: string;
|
|
268
|
+
judgedAt: string;
|
|
66
269
|
}
|
|
270
|
+
/**
|
|
271
|
+
* How wrong this result tends to be, and on what basis. `published_accuracy`
|
|
272
|
+
* carries the rule's measured numbers from the shipped proof (src/eval/
|
|
273
|
+
* published-accuracy.ts): for a fired detection or inference the positive
|
|
274
|
+
* predictive value at the stated prior, for one that did not fire the
|
|
275
|
+
* residual miss rate, each with a 95% credible interval. `definition` is a
|
|
276
|
+
* measurement's conformance to its formula (n cases, matched). `policy` is
|
|
277
|
+
* the deployment's own constraint — no error rate applies. `self_consistency`
|
|
278
|
+
* and `local_labels` arrive with the judge-through-the-engine and the
|
|
279
|
+
* own-traffic labels releases. `unmeasured` says why nothing can be stated.
|
|
280
|
+
*/
|
|
281
|
+
export type Uncertainty = {
|
|
282
|
+
basis: 'published_accuracy';
|
|
283
|
+
fired: true;
|
|
284
|
+
ppv: Interval;
|
|
285
|
+
prior: {
|
|
286
|
+
pi: number;
|
|
287
|
+
source: 'default' | 'config' | 'estimated';
|
|
288
|
+
};
|
|
289
|
+
corpus: {
|
|
290
|
+
n: number;
|
|
291
|
+
tp: number;
|
|
292
|
+
fp: number;
|
|
293
|
+
fn: number;
|
|
294
|
+
tn: number;
|
|
295
|
+
version: string;
|
|
296
|
+
release: string;
|
|
297
|
+
labelling: 'same-model' | 'human-verified';
|
|
298
|
+
};
|
|
299
|
+
} | {
|
|
300
|
+
basis: 'published_accuracy';
|
|
301
|
+
fired: false;
|
|
302
|
+
missRate: Interval;
|
|
303
|
+
prior: {
|
|
304
|
+
pi: number;
|
|
305
|
+
source: 'default' | 'config' | 'estimated';
|
|
306
|
+
};
|
|
307
|
+
corpus: {
|
|
308
|
+
n: number;
|
|
309
|
+
tp: number;
|
|
310
|
+
fp: number;
|
|
311
|
+
fn: number;
|
|
312
|
+
tn: number;
|
|
313
|
+
version: string;
|
|
314
|
+
release: string;
|
|
315
|
+
labelling: 'same-model' | 'human-verified';
|
|
316
|
+
};
|
|
317
|
+
} | {
|
|
318
|
+
basis: 'definition';
|
|
319
|
+
conformance: {
|
|
320
|
+
n: number;
|
|
321
|
+
matched: number;
|
|
322
|
+
};
|
|
323
|
+
} | {
|
|
324
|
+
basis: 'self_consistency';
|
|
325
|
+
samples: number;
|
|
326
|
+
voteFraction: number;
|
|
327
|
+
scoreSd: number;
|
|
328
|
+
} | {
|
|
329
|
+
basis: 'local_labels';
|
|
330
|
+
precision: Interval;
|
|
331
|
+
n: number;
|
|
332
|
+
} | {
|
|
333
|
+
basis: 'policy';
|
|
334
|
+
} | {
|
|
335
|
+
basis: 'unmeasured';
|
|
336
|
+
why: string;
|
|
337
|
+
};
|
|
67
338
|
export interface EvalRuleResult {
|
|
68
339
|
ruleName: string;
|
|
340
|
+
/**
|
|
341
|
+
* What kind of claim this result makes, what the composer did with it,
|
|
342
|
+
* which question it answers and which failure classes a failure belongs
|
|
343
|
+
* to — stamped by the engine from the rule's declaration (0.9.0). Absent
|
|
344
|
+
* on results written before that release and on rules that declare no
|
|
345
|
+
* metadata; never fabricated on read.
|
|
346
|
+
*/
|
|
347
|
+
kind?: ClaimKind;
|
|
348
|
+
role?: Role;
|
|
349
|
+
question?: QuestionId;
|
|
350
|
+
classes?: FailureClass[];
|
|
351
|
+
/** The version of the rule definition that produced this result. */
|
|
352
|
+
ruleVersion?: number;
|
|
353
|
+
/** Who wrote the rule: `custom` for anything createCustomRule produced. See EvalRule.origin. */
|
|
354
|
+
origin?: 'built-in' | 'custom';
|
|
355
|
+
/** Which of the rule's declared needs the call actually carried — what the rule SAW. */
|
|
356
|
+
saw?: Need[];
|
|
357
|
+
/** Present only when `skipped`; says whether the rule was never asked or was asked and could not answer. */
|
|
358
|
+
skipClass?: SkipClass;
|
|
359
|
+
/** How wrong this result tends to be, and on what basis. Present on every result that made a claim (not on skips). */
|
|
360
|
+
uncertainty?: Uncertainty;
|
|
361
|
+
/** What the rule saw: spans (offsets, never text), tool-call indices, pattern names, counts. Present on every fired detection or inference, and on measurements. */
|
|
362
|
+
evidence?: Evidence[];
|
|
363
|
+
/** A measurement's statistic and unit — the number before the score transform. */
|
|
364
|
+
value?: MeasuredValue;
|
|
69
365
|
/**
|
|
70
366
|
* Deployed rule id (rule-<hex>) when the rule came from the custom-rule
|
|
71
367
|
* store. Absent for built-in rules and for inline custom_rules. Names are
|
|
@@ -169,6 +465,19 @@ export interface EvalResult {
|
|
|
169
465
|
* rule_results carry a `category` per rule so a reader can regroup.
|
|
170
466
|
*/
|
|
171
467
|
categories?: Partial<Record<EvalType, EvalCategoryResult>>;
|
|
468
|
+
/** The verdict with its basis (0.9.0) — computed by the engine, derived on read for stored rows that carry provenance. */
|
|
469
|
+
verdict?: Verdict;
|
|
470
|
+
/** Sentences a reader needs that the verdict alone does not carry (0.10.0). */
|
|
471
|
+
interpretations?: Interpretation[];
|
|
472
|
+
/** Coverage by evaluation question (0.9.0) — computed by the engine, derived on read from the stamped rule results. */
|
|
473
|
+
coverage?: Coverage;
|
|
474
|
+
/** What produced this verdict (0.9.0) — persisted; absent on rows written before it, never fabricated. */
|
|
475
|
+
provenance?: Provenance;
|
|
476
|
+
/** What the evaluation itself cost (the judge's spend); undefined for the free rules. */
|
|
477
|
+
eval_cost_usd?: number;
|
|
478
|
+
eval_tokens?: number;
|
|
479
|
+
/** Set when the linked trace was deleted (delete_trace or the retention sweep) and this row's text was erased. */
|
|
480
|
+
erased_at?: string;
|
|
172
481
|
}
|
|
173
482
|
export type CustomRuleType = 'regex_match' | 'regex_no_match' | 'min_length' | 'max_length' | 'contains_keywords' | 'excludes_keywords' | 'json_schema' | 'cost_threshold';
|
|
174
483
|
export interface CustomRuleDefinition {
|
package/dist/types/eval.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
/** Evidence lists are capped so a pathological output cannot balloon a stored row. */
|
|
2
|
+
export const MAX_EVIDENCE_ITEMS = 25;
|
package/dist/types/query.d.ts
CHANGED
|
@@ -82,6 +82,8 @@ export interface IStorageAdapter {
|
|
|
82
82
|
getSpansByTraceId(tenantId: TenantId, traceId: string): Promise<Span[]>;
|
|
83
83
|
insertEvalResult(tenantId: TenantId, result: EvalResult): Promise<void>;
|
|
84
84
|
getEvalsByTraceId(tenantId: TenantId, traceId: string): Promise<EvalResult[]>;
|
|
85
|
+
/** One stored evaluation by id, in the same derived-on-read shape as every other reader; null when absent. */
|
|
86
|
+
getEvalById(tenantId: TenantId, id: string): Promise<EvalResult | null>;
|
|
85
87
|
queryEvalResults(tenantId: TenantId, options: {
|
|
86
88
|
eval_type?: string;
|
|
87
89
|
passed?: boolean;
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/iris-eval/mcp-server",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.10.0",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@iris-eval/mcp-server",
|
|
14
|
-
"version": "0.
|
|
14
|
+
"version": "0.10.0",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { LOCAL_TENANT } from '../types/tenant.js';
|
|
2
|
-
export function registerDashboardSummaryResource(server, storage) {
|
|
3
|
-
server.resource('dashboard-summary', 'iris://dashboard/summary', { description: 'Dashboard summary with key metrics and trends' }, async () => {
|
|
4
|
-
// OSS single-tenant: summary scopes to the local user.
|
|
5
|
-
const summary = await storage.getDashboardSummary(LOCAL_TENANT);
|
|
6
|
-
return {
|
|
7
|
-
contents: [
|
|
8
|
-
{
|
|
9
|
-
uri: 'iris://dashboard/summary',
|
|
10
|
-
mimeType: 'application/json',
|
|
11
|
-
text: JSON.stringify(summary, null, 2),
|
|
12
|
-
},
|
|
13
|
-
],
|
|
14
|
-
};
|
|
15
|
-
});
|
|
16
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { LOCAL_TENANT } from '../types/tenant.js';
|
|
2
|
-
export function registerTraceDetailResource(server, storage) {
|
|
3
|
-
server.resource('trace-detail', 'iris://traces/{trace_id}', { description: 'Full trace detail with spans and evaluation results' }, async (uri) => {
|
|
4
|
-
const traceId = uri.pathname.split('/').pop();
|
|
5
|
-
// OSS single-tenant: MCP caller is the local user.
|
|
6
|
-
const trace = await storage.getTrace(LOCAL_TENANT, traceId);
|
|
7
|
-
if (!trace) {
|
|
8
|
-
return {
|
|
9
|
-
contents: [
|
|
10
|
-
{
|
|
11
|
-
uri: uri.href,
|
|
12
|
-
mimeType: 'application/json',
|
|
13
|
-
text: JSON.stringify({ error: 'Trace not found' }),
|
|
14
|
-
},
|
|
15
|
-
],
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
const spans = await storage.getSpansByTraceId(LOCAL_TENANT, traceId);
|
|
19
|
-
const evals = await storage.getEvalsByTraceId(LOCAL_TENANT, traceId);
|
|
20
|
-
return {
|
|
21
|
-
contents: [
|
|
22
|
-
{
|
|
23
|
-
uri: uri.href,
|
|
24
|
-
mimeType: 'application/json',
|
|
25
|
-
text: JSON.stringify({ trace, spans, evals }, null, 2),
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
};
|
|
29
|
-
});
|
|
30
|
-
}
|