@jmoak/vibrissa 0.1.0 → 2.0.0-alpha.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/LICENSE +21 -21
- package/README.md +268 -254
- package/bin/vib-test.js +1 -1
- package/dist/chunk-7GFB72TI.js +10706 -0
- package/dist/chunk-7GFB72TI.js.map +1 -0
- package/dist/chunk-TFXZJODE.js +2329 -0
- package/dist/chunk-TFXZJODE.js.map +1 -0
- package/dist/cli.d.ts +57 -0
- package/dist/cli.js +5896 -613
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +837 -58
- package/dist/index.js +211 -10
- package/dist/index.js.map +1 -1
- package/dist/runner-J_V7pgax.d.ts +1194 -0
- package/dist/vitest.d.ts +44 -0
- package/dist/vitest.js +240 -0
- package/dist/vitest.js.map +1 -0
- package/package.json +27 -9
- package/schema/case.schema.json +405 -14
- package/schema/vibrissa.schema.json +528 -12
- package/dist/chunk-FTVETYUK.js +0 -656
- package/dist/chunk-FTVETYUK.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,73 +1,852 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { J as JsonValue, M as Mismatch, R as Reporter, a as ReporterContext, b as RunEvent, C as CaseStatus, c as CheckOutcome, d as ResourceSummary, e as ResourceTemplateSummary, P as PromptSummary, T as ToolSummary, f as ManifestLike, S as ServerConfig, N as NormalizeConfig$1, g as ShardSpec, h as SuiteHooks, i as RunOptions, j as ProtocolMode } from './runner-J_V7pgax.js';
|
|
2
|
+
export { A as AwaitOptions, k as CASE_KEYS, l as CallOptions, m as CaptureManifestOptions, n as CapturedManifest, o as CaseExecution, p as CaseFilters, q as CaseOutcome, r as CaseRef, D as DEFAULT_BASELINE_LATENCY_MIN_MS, s as DEFAULT_BASELINE_LATENCY_PCT, t as DEFAULT_FLAKY_FLOOR, u as DEFAULT_MANIFEST_IGNORE, v as DiffManifestOptions, w as DiscoverInfo, E as ExecuteContext, x as ExpectError, y as ExpectationUpdate, F as FieldChange, H as HttpServerConfig, I as InputResponder, z as InterpolationError, B as InterpolationScope, G as ItemChange, K as JsonObject, L as ListResult, O as LoadCasesOptions, Q as LoadCasesResult, U as LoadedCase, V as MANIFEST_SECTIONS, W as MANIFEST_VERSION, X as MODERN_PROTOCOL_VERSION, Y as Manifest, Z as ManifestDiff, _ as ManifestFileError, $ as ManifestResult, a0 as ManifestSection, a1 as McpSession, a2 as OP_KINDS, a3 as OpKind, a4 as Operation, a5 as Outcome, a6 as PlannedStep, a7 as PromptSummary, a8 as ResourceSummary, a9 as ResourceTemplateSummary, aa as RunCasesInput, ab as RunResult, ac as RunSummary, ad as Runner, ae as RunnerOptions, af as STEP_KEYS, ag as SectionDiff, ah as SessionOptions, ai as SessionRequestError, aj as SessionStartError, ak as SessionStartErrorCode, al as StdioServerConfig, am as Step, an as StepRef, ao as TASKS_EXTENSION, ap as TestCase, aq as ToolError, ar as ToolSummary, as as TranscriptEntry, at as applyUpdates, au as buildManifest, av as canonicalCodeName, aw as captureManifest, ax as caseIdFor, ay as detectIndent, az as diffManifest, aA as errorCodeName, aB as errorMatchesCode, aC as executeCase, aD as expandUriTemplate, aE as filterCases, aF as formatDiff, aG as getPath, aH as inputRequestView, aI as interpolate, aJ as interpolateStep, aK as isHttpServerConfig, aL as isStdioServerConfig, aM as jsonEqual, aN as knownCodeNames, aO as loadCaseFile, aP as loadCases, aQ as loadCasesSync, aR as looksLikeManifest, aS as manifestFrom, aT as manifestPathFor, aU as matcherPathSegments, aV as parseCaseText, aW as parsePath, aX as placeholdersIn, aY as planSteps, aZ as readManifest, a_ as resolveCaseFiles, a$ as resolveRunnerOptions, b0 as runCases, b1 as shardCases, b2 as summarizeDiff, b3 as toPointer, b4 as traceName, b5 as validateCaseShape, b6 as wordDiff, b7 as writeManifest } from './runner-J_V7pgax.js';
|
|
3
|
+
import '@modelcontextprotocol/client';
|
|
4
|
+
|
|
5
|
+
type EnvLike = Record<string, string | undefined>;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Paste-ready `respond` suggestions for unanswered input requests (MRTR rounds or 2025-era
|
|
9
|
+
* server→client requests). Used by `vib call`/`vib record` hints and by the runner's
|
|
10
|
+
* failure messages, so every surface suggests the same answer.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A `respond` map (keyed by request method) that answers every request in an
|
|
15
|
+
* InputRequests map (`{ key: { method, params } }`):
|
|
16
|
+
* - `elicitation/create` → `{ action: "accept", content: { <each requested field>: <sample> } }`
|
|
17
|
+
* - `sampling/createMessage` → a scripted assistant text message
|
|
18
|
+
* - `roots/list` → `{ roots: [] }`
|
|
19
|
+
* - anything else → `{}`
|
|
20
|
+
* The first request of each method wins (a respond map holds one answer per method).
|
|
21
|
+
*/
|
|
22
|
+
declare function suggestRespond(requests: JsonValue): Record<string, JsonValue>;
|
|
23
|
+
|
|
24
|
+
interface MatchOptions {
|
|
25
|
+
/** Start in partial mode, as if the root expectation carried `"$partial": true`. */
|
|
26
|
+
partial?: boolean;
|
|
15
27
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
declare const MAX_MISMATCHES = 25;
|
|
29
|
+
declare function matchValue(expected: JsonValue, actual: unknown, options?: MatchOptions): Mismatch[];
|
|
30
|
+
declare function matches(expected: JsonValue, actual: unknown, options?: MatchOptions): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* True when `value` would be interpreted as a matcher (`{"$regex": ...}`, `{"$type": ...}`,
|
|
33
|
+
* `{"$ref": ...}`, `{"$literal": ...}`), not compared as plain data. Plain objects carrying
|
|
34
|
+
* only `$partial` next to data keys are not matchers.
|
|
35
|
+
*/
|
|
36
|
+
declare function isMatcherObject(value: unknown): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Statically check an expectation for malformed matchers (bad shapes, invalid regexes,
|
|
39
|
+
* uncompilable `$schema`s), including inside `$oneOf` alternatives that matching may never
|
|
40
|
+
* reach. Paths point into the expectation tree (e.g. `content[0].text.$oneOf[1]`).
|
|
41
|
+
* Returns an empty array when the expectation is well formed.
|
|
42
|
+
*/
|
|
43
|
+
declare function lintExpectation(expected: JsonValue): Mismatch[];
|
|
44
|
+
/**
|
|
45
|
+
* Make recorded data safe to use as an expectation: any object that would be read as a
|
|
46
|
+
* matcher (it has a key such as `$ref`, `$type`, `$regex`, `$partial`) is wrapped in
|
|
47
|
+
* `{"$literal": ...}` so it is compared as plain data. Use when writing goldens from
|
|
48
|
+
* live results.
|
|
49
|
+
*/
|
|
50
|
+
declare function escapeDirectives(value: JsonValue): JsonValue;
|
|
51
|
+
/**
|
|
52
|
+
* Loads a referenced fragment. `relPath` is a normalized POSIX path relative to the
|
|
53
|
+
* directory of the case file (nested refs are already re-based onto that directory), or
|
|
54
|
+
* an absolute path / URL when the ref was written that way.
|
|
55
|
+
*/
|
|
56
|
+
type RefLoader = (relPath: string) => JsonValue;
|
|
57
|
+
declare class RefResolutionError extends Error {
|
|
58
|
+
/** The `$ref` string as written. */
|
|
59
|
+
readonly ref: string;
|
|
60
|
+
/** Where in the expectation tree the failing `$ref` sits (matcher path format). */
|
|
61
|
+
readonly path: string;
|
|
62
|
+
/** Fragments being expanded when the error occurred, outermost first. */
|
|
63
|
+
readonly chain: string[];
|
|
64
|
+
constructor(message: string, ref: string, path: string, chain: string[]);
|
|
31
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Expand every `{"$ref": "./shared/x.json"}` (optionally with a `#/json/pointer`) in an
|
|
68
|
+
* expectation tree. Refs may appear anywhere and fragments may contain further refs,
|
|
69
|
+
* which resolve relative to the fragment that contains them. `$schema` bodies and
|
|
70
|
+
* `$literal` values are left untouched (their `$ref`s are JSON Schema refs / data).
|
|
71
|
+
* Throws `RefResolutionError` on load failures, bad pointers and cycles.
|
|
72
|
+
*/
|
|
73
|
+
declare function resolveRefs(expected: JsonValue, loader: RefLoader): JsonValue;
|
|
32
74
|
|
|
33
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Result-envelope normalization, applied by the runner before matching and before
|
|
77
|
+
* recording so goldens stay free of per-protocol-version noise. Pure and idempotent:
|
|
78
|
+
* the input is never mutated and `normalizeResult(normalizeResult(x)) === normalizeResult(x)`
|
|
79
|
+
* structurally.
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
interface NormalizeConfig {
|
|
83
|
+
/** Glob patterns (`*` = any run of characters) for keys removed from every `_meta` object. */
|
|
84
|
+
stripMeta?: string[];
|
|
85
|
+
/** Top-level result keys to remove. */
|
|
86
|
+
dropKeys?: string[];
|
|
87
|
+
/** `'ignore'` removes top-level `ttlMs` and `cacheScope` (list-result cache hints). */
|
|
88
|
+
ttlMs?: 'keep' | 'ignore';
|
|
89
|
+
}
|
|
90
|
+
declare const defaultNormalizeConfig: Required<NormalizeConfig>;
|
|
91
|
+
/** True when `key` matches the simple glob `pattern` (`*` matches any run of characters). */
|
|
92
|
+
declare function matchesGlob(pattern: string, key: string): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Normalize an MCP result for matching/recording. Missing config fields fall back to
|
|
95
|
+
* `defaultNormalizeConfig`; pass empty arrays / `ttlMs: 'keep'` to disable a rule.
|
|
96
|
+
*/
|
|
97
|
+
declare function normalizeResult(result: JsonValue, cfg?: NormalizeConfig): JsonValue;
|
|
34
98
|
|
|
35
|
-
interface
|
|
99
|
+
interface SchemaIssue {
|
|
100
|
+
/** Location in the validated value (matcher path format; '' = the value itself). */
|
|
36
101
|
path: string;
|
|
37
102
|
message: string;
|
|
38
|
-
|
|
39
|
-
|
|
103
|
+
/** The JSON Schema keyword that failed (e.g. `type`, `required`). */
|
|
104
|
+
keyword?: string;
|
|
105
|
+
/** Pointer into the schema, e.g. `#/properties/id/type`. */
|
|
106
|
+
schemaPath?: string;
|
|
40
107
|
}
|
|
41
|
-
|
|
42
|
-
|
|
108
|
+
interface ValidationResult {
|
|
109
|
+
ok: boolean;
|
|
110
|
+
errors: SchemaIssue[];
|
|
111
|
+
/** Set when the schema itself could not be compiled; `errors` then holds one entry. */
|
|
112
|
+
schemaError?: string;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Hook for `$ref`s that point outside the schema document. Receives the ref URI resolved
|
|
116
|
+
* against the schema's `$id` (as written when there is no `$id`), without its `#fragment`.
|
|
117
|
+
* Return the referenced schema document, or `undefined` if it cannot be found.
|
|
118
|
+
*/
|
|
119
|
+
type SchemaRefResolver = (ref: string) => JsonValue | undefined;
|
|
120
|
+
interface CompileOptions {
|
|
121
|
+
resolve?: SchemaRefResolver;
|
|
122
|
+
}
|
|
123
|
+
type Validator = (value: unknown) => ValidationResult;
|
|
124
|
+
declare class SchemaCompileError extends Error {
|
|
125
|
+
constructor(message: string);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Compile a JSON Schema into a reusable validator. Results are cached by schema object
|
|
129
|
+
* identity (and resolver identity, when one is given), so compiling the same schema
|
|
130
|
+
* object repeatedly is cheap. Throws `SchemaCompileError` if the schema is unusable.
|
|
131
|
+
*/
|
|
132
|
+
declare function compileSchema(schema: JsonValue, opts?: CompileOptions): Validator;
|
|
133
|
+
/** Validate `value` against `schema`. Never throws; compile failures set `schemaError`. */
|
|
134
|
+
declare function validate(schema: JsonValue, value: unknown, opts?: CompileOptions): ValidationResult;
|
|
135
|
+
/**
|
|
136
|
+
* Meta-validate a schema (is it a well-formed schema for its dialect?) and check that it
|
|
137
|
+
* compiles, i.e. every `$ref` resolves. Error paths point into the schema document.
|
|
138
|
+
*/
|
|
139
|
+
declare function isValidSchema(schema: JsonValue, opts?: CompileOptions): {
|
|
140
|
+
ok: boolean;
|
|
141
|
+
errors: SchemaIssue[];
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Console reporter (default). Replaces v1's display.ts.
|
|
146
|
+
*
|
|
147
|
+
* - Quiet on success: one line per case, a header, and the summary banner.
|
|
148
|
+
* - Loud on failure: a Failures section with the reason, flow steps, the mismatch list
|
|
149
|
+
* (capped), budget breaches, a Jest-style expected/actual diff, and the trace path.
|
|
150
|
+
* - Pretty mode: ANSI colors and unicode glyphs, v1-style summary box.
|
|
151
|
+
* - Plain mode (non-TTY, NO_COLOR, TERM=dumb, or ctx.pretty=false): same information,
|
|
152
|
+
* ASCII only, line-oriented, stable for CI logs.
|
|
153
|
+
*
|
|
154
|
+
* Output order is stable under concurrency: a case (and its steps) prints only at
|
|
155
|
+
* case:end, so interleaved events from parallel workers never interleave lines.
|
|
156
|
+
*/
|
|
43
157
|
|
|
44
|
-
interface
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
158
|
+
interface ConsoleReporterOptions {
|
|
159
|
+
/** Force a style. Default: derived from ctx.pretty, NO_COLOR, FORCE_COLOR and TTY. */
|
|
160
|
+
style?: 'pretty' | 'plain';
|
|
161
|
+
/** Environment used for style detection. Default: process.env. */
|
|
162
|
+
env?: EnvLike;
|
|
163
|
+
/** Whether stdout is a terminal. Default: process.stdout.isTTY. */
|
|
164
|
+
isTTY?: boolean;
|
|
165
|
+
/** Max mismatches listed per failed case. Default 10. */
|
|
166
|
+
maxMismatches?: number;
|
|
167
|
+
/** Max diff lines per failed case. Default 80. */
|
|
168
|
+
maxDiffLines?: number;
|
|
169
|
+
/** Width of the pretty summary box. Default 64 (v1). */
|
|
170
|
+
width?: number;
|
|
171
|
+
}
|
|
172
|
+
declare class ConsoleReporter implements Reporter {
|
|
173
|
+
private readonly ctx;
|
|
174
|
+
readonly name = "console";
|
|
175
|
+
readonly style: 'pretty' | 'plain';
|
|
176
|
+
private readonly c;
|
|
177
|
+
private readonly g;
|
|
178
|
+
private readonly run;
|
|
179
|
+
private readonly maxMismatches;
|
|
180
|
+
private readonly maxDiffLines;
|
|
181
|
+
private readonly width;
|
|
182
|
+
private finished;
|
|
183
|
+
/** Passing checks, folded into one "Checks: N passed" line per server. */
|
|
184
|
+
private readonly passedChecks;
|
|
185
|
+
/** The runner's "no cases matched" warning, printed as an error line at the end. */
|
|
186
|
+
private noCasesMessage?;
|
|
187
|
+
constructor(ctx: ReporterContext, options?: ConsoleReporterOptions);
|
|
188
|
+
onEvent(event: RunEvent): void;
|
|
189
|
+
close(): void;
|
|
190
|
+
private out;
|
|
191
|
+
private path;
|
|
192
|
+
private badge;
|
|
193
|
+
/** `[server] ` prefix for manifest/check lines, only when the run spans several servers. */
|
|
194
|
+
private serverTag;
|
|
195
|
+
private printHeader;
|
|
196
|
+
private caseTitle;
|
|
197
|
+
private caseMeta;
|
|
198
|
+
private printCase;
|
|
199
|
+
private stepLines;
|
|
200
|
+
/**
|
|
201
|
+
* Like `vib check`: passing checks fold into one line; failed/warned checks print their
|
|
202
|
+
* id, title and detail, plus the allow-list entry that would accept them after review.
|
|
203
|
+
*/
|
|
204
|
+
private printCheck;
|
|
205
|
+
private flushPassedChecks;
|
|
206
|
+
private printWarning;
|
|
207
|
+
/** "No cases matched" is an error unless the runner accepted it (allowEmpty). */
|
|
208
|
+
private printNoCases;
|
|
209
|
+
private finish;
|
|
210
|
+
private rule;
|
|
211
|
+
private printFailures;
|
|
212
|
+
private printFailure;
|
|
213
|
+
private printManifestDrift;
|
|
214
|
+
private summaryParts;
|
|
215
|
+
/** `6 cases` or, when repeated, `2 cases, 6 runs`. */
|
|
216
|
+
private sizeLabel;
|
|
217
|
+
private printPlainSummary;
|
|
218
|
+
/** v1-style ASCII banner box. */
|
|
219
|
+
private printBox;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
declare const RESULTS_FILE = "results.json";
|
|
223
|
+
/**
|
|
224
|
+
* Shape of `results.json` (version 1). Additive changes keep `version: 1`; any removal or
|
|
225
|
+
* change of meaning bumps it.
|
|
226
|
+
*
|
|
227
|
+
* ```jsonc
|
|
228
|
+
* {
|
|
229
|
+
* "version": 1,
|
|
230
|
+
* "generator": "vibrissa",
|
|
231
|
+
* "ok": false, // run:end.ok, else: no failed cases/checks, no drift
|
|
232
|
+
* "complete": true, // false when the run stopped before run:end
|
|
233
|
+
* "startedAt": "2026-10-28T12:00:00.000Z",
|
|
234
|
+
* "finishedAt": "2026-10-28T12:00:01.234Z", // startedAt + summary.durationMs
|
|
235
|
+
* "repeat": 3, // run:start.repeat, when > 1
|
|
236
|
+
* "shard": { "index": 1, "total": 4 }, // run:start.shard, when sharded
|
|
237
|
+
* "servers": [{ "name": "local", "era": "modern", "protocolVersion": "2026-07-28",
|
|
238
|
+
* "serverInfo": { "name": "echo-server", "version": "1.2.0" } }],
|
|
239
|
+
* "summary": {
|
|
240
|
+
* "total": 3, // distinct cases (not runs)
|
|
241
|
+
* "runs": 9, // executions; only when repeated
|
|
242
|
+
* "passed": 1, "failed": 1, "skipped": 1, "updated": 0, // per run when repeated
|
|
243
|
+
* "durationMs": 1234,
|
|
244
|
+
* "passRates": { "cases/echo.json": 0.6 }, // only with --repeat > 1
|
|
245
|
+
* "flaky": ["cases/echo.json"], // 0 < pass rate < 1; omitted when none
|
|
246
|
+
* "checks": { "passed": 2, "failed": 0, "warned": 1 },
|
|
247
|
+
* "manifestDrift": 0,
|
|
248
|
+
* "warnings": 1
|
|
249
|
+
* },
|
|
250
|
+
* "cases": [{
|
|
251
|
+
* "id": "cases/echo.json#2", // CaseRef.id as emitted (unique per run)
|
|
252
|
+
* "caseId": "cases/echo.json", // id without the repeat suffix (stable across runs)
|
|
253
|
+
* "run": 2, // repeat index, only when repeated
|
|
254
|
+
* "name": "echo returns input", "file": "cases/echo.json", "server": "local",
|
|
255
|
+
* "tags": ["smoke"], "line": 1, "flaky": true, // line/flaky only when known/declared
|
|
256
|
+
* "status": "failed", "durationMs": 12,
|
|
257
|
+
* "errorKind": "assertion", // assertion | error | timeout | budget | load
|
|
258
|
+
* "reason": "Expectation failed at content[0].text: ...",
|
|
259
|
+
* "mismatches": [{ "path": "content[0].text", "message": "...", "expected": "A", "actual": "a",
|
|
260
|
+
* "field": "expect", "pointer": "/steps/1/expect/content/0/text", "stepIndex": 1 }],
|
|
261
|
+
* "updateSkipped": { "reason": "expect contains matchers" }, // --update left it alone
|
|
262
|
+
* "expected": { ... }, "actual": { ... },
|
|
263
|
+
* "steps": [{ "index": 0, "name": "create", "kind": "tool", "target": "session.create",
|
|
264
|
+
* "status": "passed", "durationMs": 5 }],
|
|
265
|
+
* "budget": { "budgetMs": 800, "actualMs": 812 },
|
|
266
|
+
* "tracePath": "reports/traces/echo.ndjson",
|
|
267
|
+
* "stderrPath": "reports/traces/echo.stderr.log"
|
|
268
|
+
* }],
|
|
269
|
+
* "checks": [{ "id": "spec/tools-list-stable", "server": "local", "title": "...", "status": "warned",
|
|
270
|
+
* "detail": "..." }],
|
|
271
|
+
* "manifests": [{ "path": "tests/integration/__manifest__.json", "server": "local", "status": "diff",
|
|
272
|
+
* "summary": "1 tool changed", "diff": { ... } }],
|
|
273
|
+
* "warnings": ["..."]
|
|
274
|
+
* }
|
|
275
|
+
* ```
|
|
276
|
+
*
|
|
277
|
+
* Empty arrays are omitted (`tags`, `summary.flaky`, `checks`, `manifests`, `warnings`);
|
|
278
|
+
* `servers` and `cases` are always present.
|
|
279
|
+
*/
|
|
280
|
+
interface JsonReport {
|
|
281
|
+
version: 1;
|
|
282
|
+
generator: 'vibrissa';
|
|
283
|
+
/**
|
|
284
|
+
* The run's verdict: run:end.ok when the runner sent it; otherwise true when no case
|
|
285
|
+
* failed, no check failed and no manifest drifted.
|
|
286
|
+
*/
|
|
287
|
+
ok: boolean;
|
|
288
|
+
/** False when the reporter was closed without seeing run:end (crash / abort). */
|
|
289
|
+
complete: boolean;
|
|
290
|
+
/** ISO 8601, from run:start.startedAt; null if run:start never arrived. */
|
|
291
|
+
startedAt: string | null;
|
|
292
|
+
/** ISO 8601, startedAt + summary.durationMs; null if unknown. */
|
|
293
|
+
finishedAt: string | null;
|
|
294
|
+
/** run:start.repeat, when cases were repeated (> 1). */
|
|
295
|
+
repeat?: number;
|
|
296
|
+
/** run:start.shard, when the run was one shard of several. */
|
|
297
|
+
shard?: {
|
|
298
|
+
index: number;
|
|
299
|
+
total: number;
|
|
300
|
+
};
|
|
301
|
+
servers: JsonServer[];
|
|
302
|
+
summary: JsonSummary;
|
|
303
|
+
/** Sorted by caseId, then run. */
|
|
304
|
+
cases: JsonCase[];
|
|
305
|
+
/** In emission order; omitted when no checks ran. */
|
|
306
|
+
checks?: CheckOutcome[];
|
|
307
|
+
/** One entry per manifest:ok / manifest:diff event; omitted when no manifest was checked. */
|
|
308
|
+
manifests?: JsonManifest[];
|
|
309
|
+
/** Omitted when there were none. */
|
|
310
|
+
warnings?: string[];
|
|
311
|
+
}
|
|
312
|
+
interface JsonServer {
|
|
313
|
+
name: string;
|
|
314
|
+
era?: 'legacy' | 'modern';
|
|
315
|
+
protocolVersion?: string;
|
|
316
|
+
/** What the server reported about itself (session:start.serverInfo). */
|
|
317
|
+
serverInfo?: {
|
|
318
|
+
name?: string;
|
|
319
|
+
version?: string;
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
interface JsonSummary {
|
|
323
|
+
/** Distinct cases. With --repeat this is not the number of executions (see `runs`). */
|
|
324
|
+
total: number;
|
|
325
|
+
/** Executions (cases x repeat); present only when cases were repeated. */
|
|
326
|
+
runs?: number;
|
|
327
|
+
/** Status counts; counted per run when repeated, so they sum to `runs`. */
|
|
328
|
+
passed: number;
|
|
329
|
+
failed: number;
|
|
330
|
+
skipped: number;
|
|
331
|
+
updated: number;
|
|
332
|
+
durationMs: number;
|
|
333
|
+
/** Pass rate per case id; present only when cases were repeated. */
|
|
334
|
+
passRates?: Record<string, number>;
|
|
335
|
+
/** Case ids (without repeat suffix) whose pass rate is strictly between 0 and 1; omitted when none. */
|
|
336
|
+
flaky?: string[];
|
|
337
|
+
checks: {
|
|
338
|
+
passed: number;
|
|
339
|
+
failed: number;
|
|
340
|
+
warned: number;
|
|
341
|
+
};
|
|
342
|
+
/** Number of manifests that differ from the lock file. */
|
|
343
|
+
manifestDrift: number;
|
|
344
|
+
warnings: number;
|
|
345
|
+
}
|
|
346
|
+
interface JsonCase {
|
|
347
|
+
/** CaseRef.id as emitted: relative case file path, plus `#<n>` for repeated runs. */
|
|
348
|
+
id: string;
|
|
349
|
+
/** id without the `#<n>` repeat suffix; the key to aggregate or compare on. */
|
|
350
|
+
caseId: string;
|
|
351
|
+
/** Repeat index, present only for repeated runs. */
|
|
352
|
+
run?: number;
|
|
353
|
+
name: string;
|
|
354
|
+
/** Case file, forward slashes. */
|
|
355
|
+
file: string;
|
|
356
|
+
server: string;
|
|
357
|
+
/** Omitted when the case has no tags. */
|
|
358
|
+
tags?: string[];
|
|
359
|
+
/** 1-based line of the case in its file, when known. */
|
|
360
|
+
line?: number;
|
|
361
|
+
/** The case declared `flaky: true`. */
|
|
362
|
+
flaky?: true;
|
|
363
|
+
status: CaseStatus;
|
|
364
|
+
durationMs: number;
|
|
365
|
+
/** Kind of failure: assertion | error | timeout | budget | load. */
|
|
366
|
+
errorKind?: 'assertion' | 'error' | 'timeout' | 'budget' | 'load';
|
|
367
|
+
reason?: string;
|
|
368
|
+
mismatches?: JsonMismatch[];
|
|
369
|
+
/** Present when --update left this failing golden alone, and why. */
|
|
370
|
+
updateSkipped?: {
|
|
371
|
+
reason: string;
|
|
372
|
+
};
|
|
373
|
+
expected?: JsonValue;
|
|
374
|
+
actual?: JsonValue;
|
|
375
|
+
steps?: JsonStep[];
|
|
376
|
+
budget?: {
|
|
377
|
+
budgetMs: number;
|
|
378
|
+
actualMs: number;
|
|
379
|
+
};
|
|
380
|
+
/** Transcript path, relative to the project root when inside it. */
|
|
381
|
+
tracePath?: string;
|
|
382
|
+
/** Captured server stderr for this case, relative to the project root when inside it. */
|
|
383
|
+
stderrPath?: string;
|
|
384
|
+
}
|
|
385
|
+
interface JsonMismatch {
|
|
386
|
+
path: string;
|
|
48
387
|
message: string;
|
|
388
|
+
expected?: JsonValue;
|
|
389
|
+
actual?: JsonValue;
|
|
390
|
+
/** Case field the mismatch belongs to (`expect`, `expectError`, ...). */
|
|
391
|
+
field?: string;
|
|
392
|
+
/** RFC 6901 pointer into the case file, e.g. `/steps/1/expect/content/0/text`. */
|
|
393
|
+
pointer?: string;
|
|
394
|
+
/** 0-based step index for flows. */
|
|
395
|
+
stepIndex?: number;
|
|
49
396
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
} | {
|
|
54
|
-
kind: 'error';
|
|
55
|
-
error: ToolError;
|
|
56
|
-
};
|
|
57
|
-
interface ToolSummary {
|
|
397
|
+
interface JsonStep {
|
|
398
|
+
/** 0-based step index. */
|
|
399
|
+
index: number;
|
|
58
400
|
name: string;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
401
|
+
kind: string;
|
|
402
|
+
target: string;
|
|
403
|
+
status: CaseStatus;
|
|
404
|
+
durationMs: number;
|
|
405
|
+
reason?: string;
|
|
406
|
+
}
|
|
407
|
+
interface JsonManifest {
|
|
408
|
+
path: string;
|
|
409
|
+
/** Server whose manifest this is, when the runner attributed it. */
|
|
410
|
+
server?: string;
|
|
411
|
+
status: 'ok' | 'diff';
|
|
412
|
+
summary?: string;
|
|
413
|
+
diff?: JsonValue;
|
|
414
|
+
}
|
|
415
|
+
declare class JsonReporter implements Reporter {
|
|
416
|
+
private readonly ctx;
|
|
417
|
+
readonly name = "json";
|
|
418
|
+
private readonly run;
|
|
419
|
+
/** Absolute path of the file written (or to be written). */
|
|
420
|
+
readonly filePath: string;
|
|
421
|
+
constructor(ctx: ReporterContext);
|
|
422
|
+
onEvent(event: RunEvent): void;
|
|
423
|
+
/** The document as it would be written right now. */
|
|
424
|
+
report(): JsonReport;
|
|
425
|
+
close(): Promise<void>;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
declare class JunitReporter implements Reporter {
|
|
429
|
+
private readonly ctx;
|
|
430
|
+
readonly name = "junit";
|
|
431
|
+
private readonly run;
|
|
432
|
+
readonly filePath: string;
|
|
433
|
+
constructor(ctx: ReporterContext);
|
|
434
|
+
onEvent(event: RunEvent): void;
|
|
435
|
+
xml(): string;
|
|
436
|
+
close(): Promise<void>;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
interface GithubReporterOptions {
|
|
440
|
+
/** Environment (GITHUB_ACTIONS, GITHUB_STEP_SUMMARY, GITHUB_WORKSPACE). Default: process.env. */
|
|
441
|
+
env?: EnvLike;
|
|
442
|
+
/** 'auto' (default): workflow commands only when GITHUB_ACTIONS=true. */
|
|
443
|
+
annotations?: 'auto' | 'always' | 'never';
|
|
444
|
+
/** Mismatches included per annotation. Default 10. */
|
|
445
|
+
maxMismatches?: number;
|
|
446
|
+
/** Failure detail blocks included in the summary. Default 25. */
|
|
447
|
+
maxDetails?: number;
|
|
448
|
+
/** Rows in the collapsed "all cases" table. Default 500. */
|
|
449
|
+
maxRows?: number;
|
|
450
|
+
}
|
|
451
|
+
declare class GithubReporter implements Reporter {
|
|
452
|
+
private readonly ctx;
|
|
453
|
+
readonly name = "github";
|
|
454
|
+
private readonly run;
|
|
455
|
+
private readonly env;
|
|
456
|
+
private readonly annotate;
|
|
457
|
+
private readonly maxMismatches;
|
|
458
|
+
private readonly maxDetails;
|
|
459
|
+
private readonly maxRows;
|
|
460
|
+
constructor(ctx: ReporterContext, options?: GithubReporterOptions);
|
|
461
|
+
/** True when workflow commands are written to stdout. */
|
|
462
|
+
get annotating(): boolean;
|
|
463
|
+
onEvent(event: RunEvent): void;
|
|
464
|
+
private emit;
|
|
465
|
+
/** Annotation paths must be relative to the repository root (GITHUB_WORKSPACE). */
|
|
466
|
+
private annotationPath;
|
|
467
|
+
private caseTitle;
|
|
468
|
+
private annotateCase;
|
|
469
|
+
private annotateCheck;
|
|
470
|
+
/** Markdown job summary for the run so far. */
|
|
471
|
+
markdown(): string;
|
|
68
472
|
close(): Promise<void>;
|
|
69
473
|
}
|
|
70
474
|
|
|
71
|
-
|
|
475
|
+
/**
|
|
476
|
+
* TAP 14 reporter (https://testanything.org/tap-version-14-specification.html).
|
|
477
|
+
*
|
|
478
|
+
* Streams one test point per case as it completes (plus manifest and check results),
|
|
479
|
+
* with YAML diagnostics (`duration_ms`, failure message, mismatches, diff). Flows are
|
|
480
|
+
* emitted as TAP 14 subtests, one point per step. The plan is trailing (`1..N`) because
|
|
481
|
+
* points stream before the total is known; an aborted run ends with `Bail out!`.
|
|
482
|
+
*/
|
|
483
|
+
|
|
484
|
+
declare class TapReporter implements Reporter {
|
|
485
|
+
private readonly ctx;
|
|
486
|
+
readonly name = "tap";
|
|
487
|
+
private readonly run;
|
|
488
|
+
private started;
|
|
489
|
+
private finished;
|
|
490
|
+
private count;
|
|
491
|
+
constructor(ctx: ReporterContext);
|
|
492
|
+
private out;
|
|
493
|
+
private ensureHeader;
|
|
494
|
+
onEvent(event: RunEvent): void;
|
|
495
|
+
close(): void;
|
|
496
|
+
private point;
|
|
497
|
+
private checkPoint;
|
|
498
|
+
private casePoint;
|
|
499
|
+
private finish;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Reporter registry. Built-ins: console (default), json, junit, github, tap.
|
|
504
|
+
* Plugins add their own with `registerReporter(name, factory)`; the runner builds the
|
|
505
|
+
* active set with `createReporters(names, ctx)` and drives them through `fanout(...)`.
|
|
506
|
+
*/
|
|
507
|
+
|
|
508
|
+
type ReporterFactory = (ctx: ReporterContext) => Reporter;
|
|
509
|
+
declare const BUILTIN_REPORTERS: readonly ["console", "json", "junit", "github", "tap"];
|
|
510
|
+
/**
|
|
511
|
+
* Register a reporter factory under `name` (case-insensitive). Throws if the name is
|
|
512
|
+
* taken, unless `{ override: true }` — which also allows replacing a built-in.
|
|
513
|
+
*/
|
|
514
|
+
declare function registerReporter(name: string, factory: ReporterFactory, options?: {
|
|
515
|
+
override?: boolean;
|
|
516
|
+
}): void;
|
|
517
|
+
/** Remove a reporter. Built-ins removed this way can be restored with `resetReporters()`. */
|
|
518
|
+
declare function unregisterReporter(name: string): boolean;
|
|
519
|
+
/** Restore the registry to just the built-ins (mainly for tests). */
|
|
520
|
+
declare function resetReporters(): void;
|
|
521
|
+
declare function hasReporter(name: string): boolean;
|
|
522
|
+
/** Registered reporter names, sorted. */
|
|
523
|
+
declare function listReporters(): string[];
|
|
524
|
+
declare class UnknownReporterError extends Error {
|
|
525
|
+
readonly reporter: string;
|
|
526
|
+
readonly known: string[];
|
|
527
|
+
readonly suggestion?: string;
|
|
528
|
+
constructor(reporter: string, known: string[]);
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Instantiate reporters by name. Names are case-insensitive, may be comma-separated
|
|
532
|
+
* (`"console,junit"`), and duplicates collapse to one instance. All names are validated
|
|
533
|
+
* before any reporter is created, so a typo never leaves half the set constructed.
|
|
534
|
+
*/
|
|
535
|
+
declare function createReporters(names: string[], ctx: ReporterContext): Reporter[];
|
|
536
|
+
/**
|
|
537
|
+
* Combine reporters into one. Every reporter sees every event even if another throws;
|
|
538
|
+
* failures are rethrown afterwards (one Error, or an AggregateError for several). The
|
|
539
|
+
* return value is synchronous when all reporters handled the event synchronously, and a
|
|
540
|
+
* promise otherwise. `close()` waits for queued events, then awaits every close().
|
|
541
|
+
*/
|
|
542
|
+
declare function fanout(reporters: Reporter[]): Reporter;
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Shared plumbing for lint rules and spec checks: allow-listing, severity
|
|
546
|
+
* overrides, grouping findings into CheckOutcomes, sorting and summarizing.
|
|
547
|
+
*/
|
|
548
|
+
|
|
549
|
+
type Severity = 'failed' | 'warned';
|
|
550
|
+
interface LintOptions {
|
|
551
|
+
/**
|
|
552
|
+
* Check ids to skip. Each entry is one of:
|
|
553
|
+
* - an exact id: `desc/long-description`
|
|
554
|
+
* - a prefix wildcard: `desc/*`
|
|
555
|
+
* - an id scoped to one subject: `desc/sensitive-paths:ssh.list_keys`
|
|
556
|
+
* (subject = tool / prompt name, resource URI, or `instructions`)
|
|
557
|
+
*/
|
|
558
|
+
allow?: string[];
|
|
559
|
+
/** Per-check severity override for findings, e.g. `{ "desc/embedded-url": "failed" }`. */
|
|
560
|
+
severity?: Record<string, Severity>;
|
|
561
|
+
}
|
|
562
|
+
declare function summarizeChecks(outcomes: readonly CheckOutcome[]): {
|
|
563
|
+
failed: number;
|
|
564
|
+
warned: number;
|
|
565
|
+
passed: number;
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Tool-description lint: a deterministic, offline smoke detector for tool
|
|
570
|
+
* poisoning, shadowing and exfiltration hints in model-visible text, plus a few
|
|
571
|
+
* quality checks. Pattern-based by design — it catches the well-known shapes of
|
|
572
|
+
* these attacks, not every paraphrase. See docs/CHECKS.md for every rule.
|
|
573
|
+
*
|
|
574
|
+
* Adding a rule = one entry in DESCRIPTION_RULES. Adding a phrase to an existing
|
|
575
|
+
* rule = one entry in its pattern table.
|
|
576
|
+
*/
|
|
577
|
+
|
|
578
|
+
interface DescriptionLintOptions extends LintOptions {
|
|
579
|
+
/** Also lint these descriptions (pattern rules only; quality rules are tool-only). */
|
|
580
|
+
resources?: ResourceSummary[];
|
|
581
|
+
resourceTemplates?: ResourceTemplateSummary[];
|
|
582
|
+
prompts?: PromptSummary[];
|
|
583
|
+
/** Server instructions from discover/initialize. */
|
|
584
|
+
instructions?: string;
|
|
585
|
+
/** Tool names from other servers the same client loads, for cross-server shadowing. */
|
|
586
|
+
knownToolNames?: string[];
|
|
587
|
+
/** Threshold for `desc/long-description`. Default 2000. */
|
|
588
|
+
maxDescriptionLength?: number;
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Lint tool descriptions (and, via opts, resource/prompt descriptions and server
|
|
592
|
+
* instructions). Pure. Returns one `passed` outcome per clean rule and one
|
|
593
|
+
* outcome per offending subject otherwise, sorted failed -> warned -> passed.
|
|
594
|
+
*/
|
|
595
|
+
declare function lintDescriptions(tools?: ToolSummary[], opts?: DescriptionLintOptions): CheckOutcome[];
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Deterministic spec-sanity checks over a recorded manifest (the Layer 0 subset
|
|
599
|
+
* Vibrissa can run on stdio): protocol version, tools/list determinism, list
|
|
600
|
+
* cache metadata, JSON Schema validity and local `$ref` resolution, server
|
|
601
|
+
* identity and capabilities. Pure: takes data, never opens a session.
|
|
602
|
+
*
|
|
603
|
+
* Also exports `errorCodeExpectations`, the per-era error-code table the live
|
|
604
|
+
* runner uses to assert error behavior.
|
|
605
|
+
*/
|
|
606
|
+
|
|
607
|
+
type SchemaValidator = (schema: JsonValue) => {
|
|
608
|
+
ok: boolean;
|
|
609
|
+
/** `keyword` is the failing JSON Schema keyword when known (`$ref` for unresolved refs). */
|
|
610
|
+
errors: Array<{
|
|
611
|
+
path: string;
|
|
612
|
+
message: string;
|
|
613
|
+
keyword?: string;
|
|
614
|
+
}>;
|
|
615
|
+
};
|
|
616
|
+
interface SpecInput extends ManifestLike {
|
|
617
|
+
/** Two or more consecutive tools/list results, in call order. */
|
|
618
|
+
toolsListSamples?: ToolSummary[][];
|
|
619
|
+
}
|
|
620
|
+
interface SpecOptions extends LintOptions {
|
|
621
|
+
/** Defaults to `isValidSchema` from src/schema.ts (Ajv 2020-12). */
|
|
622
|
+
validateSchema?: SchemaValidator;
|
|
623
|
+
}
|
|
624
|
+
/** Run every spec-sanity check over `input`. Pure; sorted failed -> warned -> passed. */
|
|
625
|
+
declare function checkSpec(input: SpecInput, opts?: SpecOptions): CheckOutcome[];
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* `vib check` engine: spec-sanity checks + description lint over a manifest.
|
|
629
|
+
* Pure: give it data recorded from a session, get CheckOutcome[] back.
|
|
630
|
+
*/
|
|
631
|
+
|
|
632
|
+
interface RunChecksOptions {
|
|
633
|
+
/** Two or more consecutive tools/list results, for `spec/tools-list-stable`. */
|
|
634
|
+
toolsListSamples?: ToolSummary[][];
|
|
635
|
+
/** Check ids to skip (`id`, `prefix/*`, or `id:subject`). Config: `checks.allow`. */
|
|
636
|
+
allow?: string[];
|
|
637
|
+
/** Severity overrides by check id. Config: `checks.severity`. */
|
|
638
|
+
severity?: Record<string, Severity>;
|
|
639
|
+
/** Tool names from other configured servers, for cross-server shadowing detection. */
|
|
640
|
+
knownToolNames?: string[];
|
|
641
|
+
maxDescriptionLength?: DescriptionLintOptions['maxDescriptionLength'];
|
|
642
|
+
/** Override the JSON Schema validator (defaults to src/schema.ts `isValidSchema`). */
|
|
643
|
+
validateSchema?: SchemaValidator;
|
|
644
|
+
}
|
|
645
|
+
/** Every check id with its title and default severity, in table order. */
|
|
646
|
+
declare function listChecks(): Array<{
|
|
647
|
+
id: string;
|
|
648
|
+
title: string;
|
|
649
|
+
severity: Severity;
|
|
650
|
+
}>;
|
|
651
|
+
/**
|
|
652
|
+
* Run spec-sanity checks and description lint over a manifest.
|
|
653
|
+
* Returns outcomes sorted failed -> warned -> passed (then by id).
|
|
654
|
+
*/
|
|
655
|
+
declare function runChecks(manifest: ManifestLike, opts?: RunChecksOptions): CheckOutcome[];
|
|
656
|
+
|
|
657
|
+
type Strategy = 'valid' | 'boundary' | 'invalid' | 'inject';
|
|
658
|
+
type Expectation = 'success' | 'invalid-params' | 'any';
|
|
659
|
+
interface GeneratedCase {
|
|
660
|
+
/** `<strategy>-<hash>`; stable for a given schema + seed (content-addressed). */
|
|
661
|
+
id: string;
|
|
662
|
+
strategy: Strategy;
|
|
663
|
+
args: Record<string, JsonValue>;
|
|
664
|
+
/**
|
|
665
|
+
* success: schema-valid, the server must not answer with a protocol error.
|
|
666
|
+
* invalid-params: schema-invalid, the server must reject it (InvalidParams or a tool error).
|
|
667
|
+
* any: no contract either way; the server must merely survive it.
|
|
668
|
+
*/
|
|
669
|
+
expectation: Expectation;
|
|
670
|
+
/** Human-readable why, e.g. "required property 'text' omitted". */
|
|
671
|
+
reason: string;
|
|
672
|
+
/** The argument this case targets, in matcher path format ('(root)' for the args object). */
|
|
673
|
+
target?: string;
|
|
674
|
+
/** Injection payload ids carried by this case (inject strategy only). */
|
|
675
|
+
payloadIds?: string[];
|
|
676
|
+
}
|
|
677
|
+
interface GenerateOptions {
|
|
678
|
+
seed: number | string;
|
|
679
|
+
/** Total number of cases across all strategies (an upper bound; small schemas yield fewer). */
|
|
680
|
+
count: number;
|
|
681
|
+
/** Default: valid, boundary, invalid. `inject` is opt-in (`vib fuzz --inject`). */
|
|
682
|
+
strategies?: Strategy[];
|
|
683
|
+
/** Maximum object/array nesting the generator builds (default 6). */
|
|
684
|
+
maxDepth?: number;
|
|
685
|
+
/**
|
|
686
|
+
* Validate each case with `validateAgainstSchema` (or the given predicate) and drop any
|
|
687
|
+
* whose expectation would be wrong. Also restores strict expectations that were relaxed
|
|
688
|
+
* to 'any' inside non-discriminated oneOf/anyOf. Recommended for the runner.
|
|
689
|
+
*/
|
|
690
|
+
selfCheck?: boolean | ((schema: JsonValue, args: Record<string, JsonValue>) => boolean);
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Generate up to `count` argument sets for a tool's `inputSchema`.
|
|
694
|
+
*
|
|
695
|
+
* Cases are taken round-robin from each requested strategy (systematic cases first, then
|
|
696
|
+
* seeded random variations) and returned grouped by strategy. A strategy that runs out
|
|
697
|
+
* (e.g. `inject` on a schema without free-form strings) leaves its share to the others.
|
|
698
|
+
* Raising `count` only appends: every case of a smaller run is in the larger one.
|
|
699
|
+
*/
|
|
700
|
+
declare function generateArgs(schema: JsonValue, opts: GenerateOptions): GeneratedCase[];
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Fuzz invariants as data: properties every tool call must satisfy without any golden.
|
|
704
|
+
* Each invariant says which generated cases it applies to and judges one call outcome.
|
|
705
|
+
* The runner (phase 2) supplies the outcome; this module never touches a session.
|
|
706
|
+
*/
|
|
707
|
+
|
|
708
|
+
interface FuzzOutcome {
|
|
709
|
+
/**
|
|
710
|
+
* result: the call returned a CallToolResult (possibly `isError: true`).
|
|
711
|
+
* error: a JSON-RPC / tool / transport error. input_required: an MRTR pause.
|
|
712
|
+
* crash: the server process died or the connection dropped. timeout: no answer in time.
|
|
713
|
+
*/
|
|
714
|
+
kind: 'result' | 'error' | 'input_required' | 'crash' | 'timeout';
|
|
715
|
+
result?: JsonValue;
|
|
716
|
+
/** `kind` mirrors the session's ToolError: 'protocol' | 'tool' | 'transport' | 'timeout'. */
|
|
717
|
+
error?: {
|
|
718
|
+
kind: string;
|
|
719
|
+
code?: number;
|
|
720
|
+
codeName?: string;
|
|
721
|
+
message: string;
|
|
722
|
+
};
|
|
723
|
+
durationMs: number;
|
|
724
|
+
/** structuredContent validated against the tool's outputSchema; undefined = no outputSchema. */
|
|
725
|
+
structuredValid?: boolean;
|
|
726
|
+
}
|
|
727
|
+
interface Verdict {
|
|
728
|
+
ok: boolean;
|
|
729
|
+
detail?: string;
|
|
730
|
+
/** The invariant did not judge this outcome (e.g. no outputSchema declared). Always ok. */
|
|
731
|
+
skipped?: boolean;
|
|
732
|
+
}
|
|
733
|
+
type InvariantLevel = 'error' | 'warn';
|
|
734
|
+
interface Invariant {
|
|
735
|
+
id: string;
|
|
736
|
+
title: string;
|
|
737
|
+
/** 'warn' verdicts are reported but never fail a run. */
|
|
738
|
+
level: InvariantLevel;
|
|
739
|
+
applies: (c: GeneratedCase) => boolean;
|
|
740
|
+
check: (c: GeneratedCase, outcome: FuzzOutcome) => Verdict;
|
|
741
|
+
}
|
|
742
|
+
interface EvaluatedVerdict extends Verdict {
|
|
743
|
+
invariant: string;
|
|
744
|
+
title: string;
|
|
745
|
+
level: InvariantLevel;
|
|
746
|
+
}
|
|
747
|
+
interface InvalidParamsOptions {
|
|
748
|
+
/**
|
|
749
|
+
* Accept a tool error (`isError: true`) as a correct rejection. MCP 2025-11-25 asks
|
|
750
|
+
* servers to report input validation failures as tool errors so the model can
|
|
751
|
+
* self-correct, so this defaults to true. Set false to require JSON-RPC InvalidParams.
|
|
752
|
+
*/
|
|
753
|
+
acceptToolError?: boolean;
|
|
754
|
+
}
|
|
755
|
+
interface InvariantOptions extends InvalidParamsOptions {
|
|
756
|
+
/** Adds latency-under-budget when set. */
|
|
757
|
+
budgetMs?: number;
|
|
758
|
+
}
|
|
759
|
+
/** The standard invariant set; `latency-under-budget` only when `budgetMs` is given. */
|
|
760
|
+
declare function createDefaultInvariants(opts?: InvariantOptions): Invariant[];
|
|
761
|
+
declare const defaultInvariants: readonly Invariant[];
|
|
762
|
+
/** Judge one outcome against every applicable invariant. */
|
|
763
|
+
declare function evaluate(c: GeneratedCase, outcome: FuzzOutcome, invariants?: readonly Invariant[]): EvaluatedVerdict[];
|
|
764
|
+
|
|
765
|
+
/** The on-disk config shape. Every key is optional; v1 files are valid. */
|
|
766
|
+
interface ConfigFile {
|
|
767
|
+
$schema?: string;
|
|
768
|
+
/** v1 sugar for `servers.default`. */
|
|
769
|
+
server?: ServerConfig;
|
|
770
|
+
servers?: Record<string, ServerConfig>;
|
|
771
|
+
defaultServer?: string;
|
|
772
|
+
globs?: string[] | string;
|
|
773
|
+
manifest?: string;
|
|
774
|
+
normalize?: NormalizeConfig$1;
|
|
775
|
+
timeoutMs?: number;
|
|
776
|
+
budgetMs?: number;
|
|
777
|
+
failFast?: boolean;
|
|
778
|
+
allowEmpty?: boolean;
|
|
779
|
+
reporters?: string[] | string;
|
|
780
|
+
tags?: string[];
|
|
781
|
+
excludeTags?: string[];
|
|
782
|
+
grep?: string;
|
|
783
|
+
repeat?: number;
|
|
784
|
+
shard?: ShardSpec | string;
|
|
785
|
+
concurrency?: number;
|
|
786
|
+
update?: boolean;
|
|
787
|
+
trace?: boolean;
|
|
788
|
+
check?: boolean;
|
|
789
|
+
baseline?: string;
|
|
790
|
+
hooks?: SuiteHooks;
|
|
791
|
+
checks?: ChecksOptions;
|
|
792
|
+
manifestIgnore?: string[] | string;
|
|
793
|
+
outDir?: string;
|
|
794
|
+
profiles?: Record<string, ConfigFile>;
|
|
795
|
+
}
|
|
796
|
+
/** `checks` config: skipped check ids and per-check severity overrides. */
|
|
797
|
+
type ChecksOptions = NonNullable<RunOptions['checks']>;
|
|
798
|
+
/** Programmatic / CLI overrides: the top layer. */
|
|
799
|
+
interface ConfigOverrides extends Partial<Omit<RunOptions, 'servers' | 'rootDir' | 'normalize' | 'defaultServer'>> {
|
|
800
|
+
servers?: Record<string, ServerConfig>;
|
|
801
|
+
defaultServer?: string;
|
|
802
|
+
normalize?: NormalizeConfig$1;
|
|
803
|
+
/**
|
|
804
|
+
* CLI `--server`: a name from `servers` (selects it), an http(s) URL, or a command line
|
|
805
|
+
* (`"node dist/index.js"`) replacing the selected server's command.
|
|
806
|
+
*/
|
|
807
|
+
server?: string;
|
|
808
|
+
/** CLI `--server-cwd` (absolute) for the selected stdio server. */
|
|
809
|
+
serverCwd?: string;
|
|
810
|
+
/** CLI `--env KEY=VALUE`: merged into every stdio server's env. */
|
|
811
|
+
env?: Record<string, string>;
|
|
812
|
+
/** CLI `--protocol`: applied to every server. */
|
|
813
|
+
protocol?: ProtocolMode;
|
|
814
|
+
}
|
|
815
|
+
interface ResolveOptionsInput {
|
|
816
|
+
/** Explicit config file (absolute or relative to cwd). */
|
|
817
|
+
configPath?: string;
|
|
818
|
+
/** Name under `profiles` to merge over the base config. */
|
|
819
|
+
profile?: string;
|
|
820
|
+
overrides?: ConfigOverrides;
|
|
821
|
+
/** Environment for `${VAR}` interpolation. Default `process.env`. */
|
|
822
|
+
env?: Record<string, string | undefined>;
|
|
823
|
+
}
|
|
824
|
+
interface ResolvedConfig {
|
|
825
|
+
options: RunOptions;
|
|
826
|
+
/** Non-fatal problems (unknown keys, unset env vars). Print them; do not ignore them. */
|
|
827
|
+
warnings: string[];
|
|
828
|
+
configPath?: string;
|
|
829
|
+
profile?: string;
|
|
830
|
+
}
|
|
831
|
+
declare class ConfigError extends Error {
|
|
832
|
+
constructor(message: string);
|
|
833
|
+
}
|
|
834
|
+
/** Parse "auto" | "legacy" | "modern" | "<YYYY-MM-DD>" | { pin }. */
|
|
835
|
+
declare function parseProtocolMode(value: unknown, onError?: (got: unknown) => never): ProtocolMode;
|
|
836
|
+
/** Parse "1/4" or { index, total } (1-based). Undefined when invalid. */
|
|
837
|
+
declare function parseShard(value: unknown): ShardSpec | undefined;
|
|
838
|
+
/** Load + merge config. Prefer this in CLIs so `warnings` can be printed. */
|
|
839
|
+
declare function loadConfig(cwd: string, input?: ResolveOptionsInput): ResolvedConfig;
|
|
840
|
+
/**
|
|
841
|
+
* Resolve RunOptions: defaults → file → profile → overrides.
|
|
842
|
+
* The second argument may be a config path (v1 signature).
|
|
843
|
+
*/
|
|
844
|
+
declare function resolveOptions(cwd: string, input?: ResolveOptionsInput | string): RunOptions;
|
|
845
|
+
|
|
846
|
+
declare const DEFAULT_NORMALIZE: Required<NormalizeConfig$1>;
|
|
847
|
+
/** Built-in defaults: the bottom layer of defaults → file → profile → CLI. */
|
|
848
|
+
declare const defaultRunOptions: RunOptions;
|
|
849
|
+
/** @deprecated v1 name; use defaultRunOptions. */
|
|
850
|
+
declare const defaultRunCasesOptions: RunOptions;
|
|
72
851
|
|
|
73
|
-
export { type JsonValue,
|
|
852
|
+
export { BUILTIN_REPORTERS, CaseStatus, CheckOutcome, type CompileOptions, ConfigError, type ConfigFile, type ConfigOverrides, ConsoleReporter, DEFAULT_NORMALIZE, type FuzzOutcome, type GenerateOptions, type GeneratedCase, GithubReporter, type Invariant, type JsonCase, type JsonReport, JsonReporter, JsonValue, JunitReporter, MAX_MISMATCHES, ManifestLike, type MatchOptions, Mismatch, NormalizeConfig$1 as NormalizeConfig, ProtocolMode, RESULTS_FILE, type RefLoader, RefResolutionError, Reporter, ReporterContext, type ReporterFactory, type ResolvedConfig, RunOptions as RunCasesOptions, type RunChecksOptions, RunEvent, RunOptions, SchemaCompileError, type SchemaIssue, type SchemaRefResolver, ServerConfig, ShardSpec, SuiteHooks, TapReporter, UnknownReporterError, type ValidationResult, type Validator, checkSpec, compileSchema, createDefaultInvariants, createReporters, defaultInvariants, defaultNormalizeConfig, defaultRunCasesOptions, defaultRunOptions, escapeDirectives, evaluate, fanout, generateArgs, hasReporter, isMatcherObject, isValidSchema, lintDescriptions, lintExpectation, listChecks, listReporters, loadConfig, matchValue, matches, matchesGlob, normalizeResult, parseProtocolMode, parseShard, registerReporter, resetReporters, resolveOptions, resolveRefs, runChecks, suggestRespond, summarizeChecks, unregisterReporter, validate };
|