@wix/pathgrade 0.37.0 → 1.0.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 +116 -32
- package/dist/adapter-kit/index.d.ts +17 -0
- package/dist/adapter-kit/index.js +14 -0
- package/dist/adapters/jest/index.d.ts +3 -0
- package/dist/adapters/jest/index.js +3 -0
- package/dist/adapters/jest/invocation-adapter.d.ts +15 -0
- package/dist/adapters/jest/invocation-adapter.js +115 -0
- package/dist/adapters/jest/lifecycle.d.ts +21 -0
- package/dist/adapters/jest/lifecycle.js +62 -0
- package/dist/adapters/jest/metadata.d.ts +6 -0
- package/dist/adapters/jest/metadata.js +35 -0
- package/dist/adapters/jest/reporter.cjs +10 -0
- package/dist/adapters/jest/reporter.d.ts +5 -0
- package/dist/adapters/jest/reporter.js +67 -0
- package/dist/adapters/jest/results.d.ts +31 -0
- package/dist/adapters/jest/results.js +144 -0
- package/dist/adapters/jest/runner-adapter.d.ts +9 -0
- package/dist/adapters/jest/runner-adapter.js +49 -0
- package/dist/adapters/jest/setup.d.ts +1 -0
- package/dist/adapters/jest/setup.js +10 -0
- package/dist/adapters/node-test/index.d.ts +8 -0
- package/dist/adapters/node-test/index.js +106 -0
- package/dist/adapters/node-test/invocation-adapter.d.ts +5 -0
- package/dist/adapters/node-test/invocation-adapter.js +47 -0
- package/dist/adapters/node-test/runner-adapter.d.ts +2 -0
- package/dist/adapters/node-test/runner-adapter.js +116 -0
- package/dist/adapters/vitest/index.d.ts +9 -0
- package/dist/adapters/vitest/index.js +82 -0
- package/dist/adapters/vitest/lifecycle.d.ts +24 -0
- package/dist/adapters/vitest/lifecycle.js +41 -0
- package/dist/adapters/vitest/reporter.d.ts +12 -0
- package/dist/adapters/vitest/reporter.js +75 -0
- package/dist/adapters/vitest/setup.d.ts +1 -0
- package/dist/adapters/vitest/setup.js +3 -0
- package/dist/agents/codex-app-server/agent.js +5 -1
- package/dist/agents/codex-app-server/protocol/index.js +3 -3
- package/dist/agents/codex-app-server/turn-completion.d.ts +1 -0
- package/dist/agents/codex-app-server/turn-completion.js +9 -0
- package/dist/commands/affected.js +22 -15
- package/dist/commands/run-args.d.ts +4 -3
- package/dist/commands/run-args.js +12 -6
- package/dist/commands/run-changed.d.ts +4 -4
- package/dist/commands/run-changed.js +45 -58
- package/dist/config/pathgrade.d.ts +48 -0
- package/dist/config/pathgrade.js +204 -0
- package/dist/pathgrade.d.ts +2 -2
- package/dist/pathgrade.js +35 -34
- package/dist/plugin/index.d.ts +2 -9
- package/dist/plugin/index.js +1 -81
- package/dist/plugin/lifecycle.d.ts +1 -45
- package/dist/plugin/lifecycle.js +1 -146
- package/dist/plugin/reporter.d.ts +1 -37
- package/dist/plugin/reporter.js +1 -280
- package/dist/plugin/setup.d.ts +1 -1
- package/dist/plugin/setup.js +1 -3
- package/dist/providers/copy-filter.d.ts +5 -0
- package/dist/providers/copy-filter.js +9 -0
- package/dist/providers/sandbox.js +2 -2
- package/dist/providers/workspace.d.ts +6 -0
- package/dist/providers/workspace.js +34 -14
- package/dist/reporters/diagnostics.d.ts +2 -40
- package/dist/reporters/diagnostics.js +1 -80
- package/dist/reporters/report-summary.d.ts +6 -0
- package/dist/reporters/report-summary.js +29 -0
- package/dist/reporting/artifacts.d.ts +2 -0
- package/dist/reporting/artifacts.js +20 -0
- package/dist/reporting/core.d.ts +2 -0
- package/dist/reporting/core.js +173 -0
- package/dist/reporting/types.d.ts +57 -0
- package/dist/reporting/types.js +1 -0
- package/dist/reporting/vitest-edge.d.ts +1 -0
- package/dist/reporting/vitest-edge.js +1 -0
- package/dist/runners/adapter-loader.d.ts +17 -0
- package/dist/runners/adapter-loader.js +67 -0
- package/dist/runners/adapter.d.ts +65 -0
- package/dist/runners/adapter.js +1 -0
- package/dist/runners/invocation.d.ts +10 -0
- package/dist/runners/invocation.js +1 -0
- package/dist/runners/lifecycle-hooks.d.ts +2 -0
- package/dist/runners/lifecycle-hooks.js +18 -0
- package/dist/runners/model-builders.d.ts +4 -0
- package/dist/runners/model-builders.js +67 -0
- package/dist/runners/model-validation.d.ts +12 -0
- package/dist/runners/model-validation.js +213 -0
- package/dist/runners/model.d.ts +117 -0
- package/dist/runners/model.js +1 -0
- package/dist/runners/orchestrator.d.ts +30 -0
- package/dist/runners/orchestrator.js +74 -0
- package/dist/runners/report-projection.d.ts +6 -0
- package/dist/runners/report-projection.js +62 -0
- package/dist/runners/selection.d.ts +6 -0
- package/dist/runners/selection.js +10 -0
- package/dist/runners/vitest-adapter.d.ts +8 -0
- package/dist/runners/vitest-adapter.js +108 -0
- package/dist/runners/vitest-invocation.d.ts +10 -0
- package/dist/runners/vitest-invocation.js +44 -0
- package/dist/runners/vitest-lifecycle.d.ts +43 -0
- package/dist/runners/vitest-lifecycle.js +150 -0
- package/dist/sdk/agent.js +15 -16
- package/dist/sdk/case-context.d.ts +20 -0
- package/dist/sdk/case-context.js +31 -0
- package/dist/sdk/diagnostics.d.ts +40 -0
- package/dist/sdk/diagnostics.js +80 -0
- package/dist/sdk/eval-runtime.d.ts +4 -0
- package/dist/sdk/eval-runtime.js +11 -1
- package/dist/sdk/evaluate.js +2 -1
- package/dist/sdk/index.d.ts +2 -0
- package/dist/sdk/index.js +1 -0
- package/dist/sdk/lifecycle.d.ts +40 -0
- package/dist/sdk/lifecycle.js +170 -0
- package/dist/sdk/result-capture.d.ts +18 -0
- package/dist/sdk/result-capture.js +49 -0
- package/dist/sdk/types.d.ts +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/utils/llm-providers/anthropic.js +9 -2
- package/package.json +56 -4
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { extractSkillsFromLog } from '../tool-events.js';
|
|
2
|
+
export function buildPathgradeReport(input) {
|
|
3
|
+
const warnings = [];
|
|
4
|
+
const consolidatedGroups = [];
|
|
5
|
+
const traces = [];
|
|
6
|
+
const summaries = [];
|
|
7
|
+
const builtGroups = input.groups.map(group => ({
|
|
8
|
+
groupName: group.groupName,
|
|
9
|
+
cases: group.cases.map(toBuiltCase),
|
|
10
|
+
}));
|
|
11
|
+
for (const group of builtGroups) {
|
|
12
|
+
for (const testCase of group.cases) {
|
|
13
|
+
warnings.push(...testCase.warnings);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const reportableGroups = builtGroups
|
|
17
|
+
.map(group => ({
|
|
18
|
+
...group,
|
|
19
|
+
cases: group.cases.filter(reportableBuiltCase),
|
|
20
|
+
}))
|
|
21
|
+
.filter(group => group.cases.length > 0);
|
|
22
|
+
for (const group of reportableGroups) {
|
|
23
|
+
const report = buildEvalReport(group.groupName, group.cases);
|
|
24
|
+
summaries.push(buildSummary(group.groupName, group.cases, report));
|
|
25
|
+
const traceFile = `traces/${slug(group.groupName)}.json`;
|
|
26
|
+
traces.push({ traceFile, trials: report.trials });
|
|
27
|
+
const strippedTrials = report.trials.map(trial => {
|
|
28
|
+
const { session_log, conversation, ...rest } = trial;
|
|
29
|
+
return rest;
|
|
30
|
+
});
|
|
31
|
+
const { trials: _trials, ...rest } = report;
|
|
32
|
+
consolidatedGroups.push({
|
|
33
|
+
...rest,
|
|
34
|
+
trials: strippedTrials,
|
|
35
|
+
trace_file: traceFile,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const scores = reportableGroups.flatMap(group => group.cases.map(testCase => testCase.score));
|
|
39
|
+
const overallPassRate = average(scores);
|
|
40
|
+
const status = input.threshold != null
|
|
41
|
+
? (overallPassRate >= input.threshold ? 'pass' : 'fail')
|
|
42
|
+
: (reportableGroups.every(group => group.cases.every(testCase => testCase.state === 'passed')) ? 'pass' : 'fail');
|
|
43
|
+
return {
|
|
44
|
+
report: {
|
|
45
|
+
version: 1,
|
|
46
|
+
timestamp: new Date().toISOString(),
|
|
47
|
+
...(input.threshold != null ? { threshold: input.threshold } : {}),
|
|
48
|
+
overall_pass_rate: overallPassRate,
|
|
49
|
+
status,
|
|
50
|
+
groups: consolidatedGroups,
|
|
51
|
+
...(input.selection ? { selection: input.selection } : {}),
|
|
52
|
+
},
|
|
53
|
+
traces,
|
|
54
|
+
summaries,
|
|
55
|
+
warnings,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function reportableBuiltCase(testCase) {
|
|
59
|
+
if (testCase.reportable !== undefined)
|
|
60
|
+
return testCase.reportable;
|
|
61
|
+
return testCase.state !== 'skipped' && testCase.state !== 'pending';
|
|
62
|
+
}
|
|
63
|
+
function toBuiltCase(testCase) {
|
|
64
|
+
if (testCase.evaluations === undefined) {
|
|
65
|
+
return fallbackCase(testCase, []);
|
|
66
|
+
}
|
|
67
|
+
if (testCase.evaluations.length === 0) {
|
|
68
|
+
return fallbackCase(testCase, [
|
|
69
|
+
`empty results for "${testCase.name}" — evaluate() may not have been called`,
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
72
|
+
const evaluation = testCase.evaluations[testCase.evaluations.length - 1];
|
|
73
|
+
const diagnostics = evaluation.diagnostics ?? testCase.diagnostics;
|
|
74
|
+
return {
|
|
75
|
+
name: testCase.name,
|
|
76
|
+
state: testCase.state,
|
|
77
|
+
score: evaluation.score,
|
|
78
|
+
runnerDurationMs: testCase.runnerDurationMs,
|
|
79
|
+
diagnostics,
|
|
80
|
+
reportable: testCase.reportable,
|
|
81
|
+
trial: normalizeTrial({
|
|
82
|
+
name: testCase.name,
|
|
83
|
+
score: evaluation.score,
|
|
84
|
+
runnerDurationMs: testCase.runnerDurationMs,
|
|
85
|
+
trial: evaluation.trial,
|
|
86
|
+
diagnostics,
|
|
87
|
+
}),
|
|
88
|
+
warnings: [],
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function fallbackCase(testCase, warnings) {
|
|
92
|
+
const score = testCase.state === 'passed' ? 1 : 0;
|
|
93
|
+
return {
|
|
94
|
+
name: testCase.name,
|
|
95
|
+
state: testCase.state,
|
|
96
|
+
score,
|
|
97
|
+
runnerDurationMs: testCase.runnerDurationMs,
|
|
98
|
+
diagnostics: testCase.diagnostics,
|
|
99
|
+
reportable: testCase.reportable,
|
|
100
|
+
trial: normalizeTrial({
|
|
101
|
+
name: testCase.name,
|
|
102
|
+
score,
|
|
103
|
+
runnerDurationMs: testCase.runnerDurationMs,
|
|
104
|
+
diagnostics: testCase.diagnostics,
|
|
105
|
+
}),
|
|
106
|
+
warnings,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function normalizeTrial(input) {
|
|
110
|
+
const base = input.trial ?? {
|
|
111
|
+
trial_id: 1,
|
|
112
|
+
reward: input.score,
|
|
113
|
+
scorer_results: [],
|
|
114
|
+
duration_ms: input.runnerDurationMs,
|
|
115
|
+
n_commands: 0,
|
|
116
|
+
input_tokens: 0,
|
|
117
|
+
output_tokens: 0,
|
|
118
|
+
session_log: [],
|
|
119
|
+
};
|
|
120
|
+
const skills = base.skills_used ?? extractSkillsFromLog(base.session_log);
|
|
121
|
+
return {
|
|
122
|
+
...base,
|
|
123
|
+
name: input.name,
|
|
124
|
+
duration_ms: base.duration_ms || input.runnerDurationMs,
|
|
125
|
+
diagnostics: input.diagnostics ?? base.diagnostics,
|
|
126
|
+
...(skills.length > 0 ? { skills_used: skills } : {}),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function buildEvalReport(groupName, cases) {
|
|
130
|
+
const trials = cases.map((testCase, index) => ({
|
|
131
|
+
...testCase.trial,
|
|
132
|
+
trial_id: index + 1,
|
|
133
|
+
}));
|
|
134
|
+
const passRate = average(cases.map(testCase => testCase.state === 'passed' ? 1 : 0));
|
|
135
|
+
const skills = new Set();
|
|
136
|
+
for (const trial of trials) {
|
|
137
|
+
for (const skill of trial.skills_used ?? []) {
|
|
138
|
+
skills.add(skill);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
task: groupName,
|
|
143
|
+
pass_rate: passRate,
|
|
144
|
+
pass_at_k: 1 - Math.pow(1 - passRate, trials.length),
|
|
145
|
+
pass_pow_k: Math.pow(passRate, trials.length),
|
|
146
|
+
trials,
|
|
147
|
+
skills_used: [...skills],
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function buildSummary(groupName, cases, report) {
|
|
151
|
+
return {
|
|
152
|
+
task: groupName,
|
|
153
|
+
pass_rate: report.pass_rate,
|
|
154
|
+
pass_at_k: report.pass_at_k,
|
|
155
|
+
pass_pow_k: report.pass_pow_k,
|
|
156
|
+
average_duration_ms: average(cases.map(testCase => testCase.runnerDurationMs)),
|
|
157
|
+
trial_count: cases.length,
|
|
158
|
+
diagnostics: cases.flatMap(testCase => testCase.diagnostics
|
|
159
|
+
? [{ caseName: testCase.name, state: testCase.state, report: testCase.diagnostics }]
|
|
160
|
+
: []),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function average(values) {
|
|
164
|
+
if (values.length === 0)
|
|
165
|
+
return 0;
|
|
166
|
+
return values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
167
|
+
}
|
|
168
|
+
function slug(value) {
|
|
169
|
+
return value
|
|
170
|
+
.toLowerCase()
|
|
171
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
172
|
+
.replace(/^-+|-+$/g, '') || 'report';
|
|
173
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { DiagnosticsReport } from '../sdk/diagnostics.js';
|
|
2
|
+
import type { PathgradeReport, PathgradeSelectionReport, TrialResult } from '../types.js';
|
|
3
|
+
export type ReportCaseState = 'passed' | 'failed' | 'skipped' | 'pending';
|
|
4
|
+
export interface ReportRunInput {
|
|
5
|
+
threshold?: number;
|
|
6
|
+
selection?: PathgradeSelectionReport;
|
|
7
|
+
groups: ReportGroupInput[];
|
|
8
|
+
}
|
|
9
|
+
export interface ReportGroupInput {
|
|
10
|
+
groupName: string;
|
|
11
|
+
cases: ReportCaseInput[];
|
|
12
|
+
}
|
|
13
|
+
export interface ReportCaseInput {
|
|
14
|
+
caseId?: string;
|
|
15
|
+
name: string;
|
|
16
|
+
state: ReportCaseState;
|
|
17
|
+
runnerDurationMs: number;
|
|
18
|
+
sourceRef?: string;
|
|
19
|
+
filePath?: string;
|
|
20
|
+
groupName?: string;
|
|
21
|
+
runnerCaseId?: string;
|
|
22
|
+
reportable?: boolean;
|
|
23
|
+
evaluations?: readonly ReportEvaluationInput[];
|
|
24
|
+
diagnostics?: DiagnosticsReport;
|
|
25
|
+
}
|
|
26
|
+
export interface ReportEvaluationInput {
|
|
27
|
+
score: number;
|
|
28
|
+
trial?: TrialResult;
|
|
29
|
+
diagnostics?: DiagnosticsReport;
|
|
30
|
+
}
|
|
31
|
+
export interface PathgradeReportBuildResult {
|
|
32
|
+
report: PathgradeReport;
|
|
33
|
+
traces: Array<{
|
|
34
|
+
traceFile: string;
|
|
35
|
+
trials: TrialResult[];
|
|
36
|
+
}>;
|
|
37
|
+
summaries: ReportSummaryGroup[];
|
|
38
|
+
warnings: string[];
|
|
39
|
+
}
|
|
40
|
+
export interface ArtifactWriteResult {
|
|
41
|
+
resultsPath: string;
|
|
42
|
+
traceFiles: string[];
|
|
43
|
+
}
|
|
44
|
+
export interface ReportSummaryGroup {
|
|
45
|
+
task: string;
|
|
46
|
+
pass_rate: number;
|
|
47
|
+
pass_at_k: number;
|
|
48
|
+
pass_pow_k: number;
|
|
49
|
+
average_duration_ms: number;
|
|
50
|
+
trial_count: number;
|
|
51
|
+
diagnostics: ReportSummaryDiagnostics[];
|
|
52
|
+
}
|
|
53
|
+
export interface ReportSummaryDiagnostics {
|
|
54
|
+
caseName: string;
|
|
55
|
+
state: ReportCaseState;
|
|
56
|
+
report: DiagnosticsReport;
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { collectVitestReportGroups } from '../runners/vitest-adapter.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { collectVitestReportGroups } from '../runners/vitest-adapter.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ResolvedPathgradeConfig } from '../config/pathgrade.js';
|
|
2
|
+
import { type SpawnVitest } from './vitest-invocation.js';
|
|
3
|
+
import type { RunnerAdapter } from './adapter.js';
|
|
4
|
+
import type { RunnerInvocationAdapter } from './invocation.js';
|
|
5
|
+
export interface ExternalAdapterFactoryInput {
|
|
6
|
+
config: ResolvedPathgradeConfig;
|
|
7
|
+
}
|
|
8
|
+
export declare function loadRunnerAdapter(input: {
|
|
9
|
+
adapterName?: string;
|
|
10
|
+
cwd?: string;
|
|
11
|
+
}): Promise<RunnerAdapter>;
|
|
12
|
+
export declare function loadRunnerInvocationAdapter(input: {
|
|
13
|
+
adapterName?: string;
|
|
14
|
+
cwd?: string;
|
|
15
|
+
config: ResolvedPathgradeConfig;
|
|
16
|
+
spawnVitest?: SpawnVitest;
|
|
17
|
+
}): Promise<RunnerInvocationAdapter>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { createJestInvocationAdapter } from '../adapters/jest/invocation-adapter.js';
|
|
5
|
+
import { createJestAdapter } from '../adapters/jest/runner-adapter.js';
|
|
6
|
+
import { createNodeTestInvocationAdapter } from '../adapters/node-test/invocation-adapter.js';
|
|
7
|
+
import { createVitestInvocationAdapter } from './vitest-invocation.js';
|
|
8
|
+
import { resolveRunnerAdapter } from './selection.js';
|
|
9
|
+
export async function loadRunnerAdapter(input) {
|
|
10
|
+
const name = input.adapterName ?? 'vitest';
|
|
11
|
+
if (name === 'vitest' || name === 'node-test') {
|
|
12
|
+
return resolveRunnerAdapter({ adapterName: name });
|
|
13
|
+
}
|
|
14
|
+
if (name === 'jest')
|
|
15
|
+
return createJestAdapter();
|
|
16
|
+
const mod = await importExternalAdapter({
|
|
17
|
+
adapterName: name,
|
|
18
|
+
cwd: input.cwd ?? process.cwd(),
|
|
19
|
+
});
|
|
20
|
+
if (typeof mod.createPathgradeAdapter !== 'function') {
|
|
21
|
+
throw new Error(`Pathgrade adapter package "${adapterPackageSpecifier(name)}" must export createPathgradeAdapter().`);
|
|
22
|
+
}
|
|
23
|
+
return mod.createPathgradeAdapter();
|
|
24
|
+
}
|
|
25
|
+
export async function loadRunnerInvocationAdapter(input) {
|
|
26
|
+
const name = input.adapterName ?? 'vitest';
|
|
27
|
+
if (name === 'vitest')
|
|
28
|
+
return createVitestInvocationAdapter({ spawnVitest: input.spawnVitest });
|
|
29
|
+
if (name === 'node-test')
|
|
30
|
+
return createNodeTestInvocationAdapter({ config: input.config });
|
|
31
|
+
if (name === 'jest')
|
|
32
|
+
return createJestInvocationAdapter({ config: input.config });
|
|
33
|
+
const mod = await importExternalAdapter({
|
|
34
|
+
adapterName: name,
|
|
35
|
+
cwd: input.cwd ?? process.cwd(),
|
|
36
|
+
});
|
|
37
|
+
if (typeof mod.createPathgradeInvocationAdapter !== 'function') {
|
|
38
|
+
throw new Error(`Pathgrade adapter package "${adapterPackageSpecifier(name)}" must export createPathgradeInvocationAdapter().`);
|
|
39
|
+
}
|
|
40
|
+
return mod.createPathgradeInvocationAdapter({ config: input.config });
|
|
41
|
+
}
|
|
42
|
+
function adapterPackageSpecifier(adapterName) {
|
|
43
|
+
if (adapterName.startsWith('.') || path.isAbsolute(adapterName) || adapterName.includes('/')) {
|
|
44
|
+
return adapterName;
|
|
45
|
+
}
|
|
46
|
+
return `@wix/pathgrade-adapter-${adapterName}`;
|
|
47
|
+
}
|
|
48
|
+
function resolveAdapterSpecifier(input) {
|
|
49
|
+
if (input.adapterName.startsWith('.') || path.isAbsolute(input.adapterName)) {
|
|
50
|
+
return path.resolve(input.cwd, input.adapterName);
|
|
51
|
+
}
|
|
52
|
+
const specifier = adapterPackageSpecifier(input.adapterName);
|
|
53
|
+
return createRequire(path.join(input.cwd, 'package.json')).resolve(specifier);
|
|
54
|
+
}
|
|
55
|
+
async function importExternalAdapter(input) {
|
|
56
|
+
try {
|
|
57
|
+
const resolved = resolveAdapterSpecifier(input);
|
|
58
|
+
return await import(pathToFileURL(resolved).href);
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
throw new Error(`Unsupported Pathgrade runner adapter: ${input.adapterName}. ` +
|
|
62
|
+
`Tried to load ${adapterPackageSpecifier(input.adapterName)} from ${input.cwd}: ${errMsg(err)}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function errMsg(err) {
|
|
66
|
+
return err instanceof Error ? err.message : String(err);
|
|
67
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { PathgradeSelectionReport } from '../types.js';
|
|
2
|
+
import type { Agent, PathgradeTestMeta, RecordedEvalResult } from '../sdk/types.js';
|
|
3
|
+
import type { CaseContextScope } from '../sdk/case-context.js';
|
|
4
|
+
import type { NormalizedRunSnapshot } from './model.js';
|
|
5
|
+
export declare const runnerAdapterContractVersion = 1;
|
|
6
|
+
export interface RunnerAdapter {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
discover(input: AdapterDiscoveryInput): Promise<AdapterDiscoveryResult>;
|
|
9
|
+
invoke(input: AdapterInvocationInput): Promise<AdapterRunHandle>;
|
|
10
|
+
collectNormalizedRunSnapshot(run: AdapterRunHandle): Promise<NormalizedRunSnapshot>;
|
|
11
|
+
}
|
|
12
|
+
export interface AdapterDiscoveryInput {
|
|
13
|
+
cwd: string;
|
|
14
|
+
include?: string[];
|
|
15
|
+
exclude?: string[];
|
|
16
|
+
selection?: PathgradeSelectionReport;
|
|
17
|
+
}
|
|
18
|
+
export interface AdapterDiscoveryResult {
|
|
19
|
+
units: AdapterEvalUnit[];
|
|
20
|
+
diagnostics?: AdapterDiagnostic[];
|
|
21
|
+
}
|
|
22
|
+
export interface AdapterEvalUnit {
|
|
23
|
+
id: string;
|
|
24
|
+
displayName: string;
|
|
25
|
+
sourceRef?: string;
|
|
26
|
+
native?: unknown;
|
|
27
|
+
}
|
|
28
|
+
export interface AdapterDiagnostic {
|
|
29
|
+
severity: 'info' | 'warning' | 'error';
|
|
30
|
+
message: string;
|
|
31
|
+
sourceRef?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface AdapterInvocationInput {
|
|
34
|
+
discovered: AdapterDiscoveryResult;
|
|
35
|
+
argv: string[];
|
|
36
|
+
env: NodeJS.ProcessEnv;
|
|
37
|
+
lifecycle: AdapterLifecycleHooks;
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
}
|
|
40
|
+
export interface AdapterRunHandle {
|
|
41
|
+
adapterName: string;
|
|
42
|
+
status: AdapterRunStatus;
|
|
43
|
+
exitCode: number;
|
|
44
|
+
native?: unknown;
|
|
45
|
+
diagnostics?: AdapterDiagnostic[];
|
|
46
|
+
}
|
|
47
|
+
export type AdapterRunStatus = 'completed' | 'failed' | 'cancelled' | 'timed_out' | 'parked';
|
|
48
|
+
export interface AdapterLifecycleHooks {
|
|
49
|
+
onResult(event: EvalResultEvent): void;
|
|
50
|
+
withCaseContext<T>(context: AdapterCaseContext, run: () => T): T;
|
|
51
|
+
flushCase(caseId: string): Promise<PathgradeTestMeta[]>;
|
|
52
|
+
cleanupRun(): Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
export interface AdapterCaseContext {
|
|
55
|
+
caseId: string;
|
|
56
|
+
caseName: string;
|
|
57
|
+
filePath?: string;
|
|
58
|
+
sourceRef?: string;
|
|
59
|
+
runnerNativeId?: string;
|
|
60
|
+
scope: CaseContextScope;
|
|
61
|
+
}
|
|
62
|
+
export interface EvalResultEvent {
|
|
63
|
+
result: RecordedEvalResult;
|
|
64
|
+
agent: Agent;
|
|
65
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const runnerAdapterContractVersion = 1;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface RunnerInvocationAdapter {
|
|
2
|
+
readonly name: string;
|
|
3
|
+
run(input: RunnerInvocationInput): Promise<number>;
|
|
4
|
+
}
|
|
5
|
+
export interface RunnerInvocationInput {
|
|
6
|
+
cwd: string;
|
|
7
|
+
runnerArgs: string[];
|
|
8
|
+
selectedFiles?: string[];
|
|
9
|
+
env: NodeJS.ProcessEnv;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { lifecycleCore } from '../sdk/lifecycle.js';
|
|
2
|
+
import { runWithCaseContext } from '../sdk/case-context.js';
|
|
3
|
+
export function createRunnerLifecycleHooks() {
|
|
4
|
+
return {
|
|
5
|
+
onResult: event => lifecycleCore.recordResult(event.result, event.agent),
|
|
6
|
+
withCaseContext: (context, run) => runWithCaseContext(toCaseContext(context), run),
|
|
7
|
+
flushCase: caseId => lifecycleCore.flushCase({ caseId }),
|
|
8
|
+
cleanupRun: () => lifecycleCore.cleanupAll(),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function toCaseContext(context) {
|
|
12
|
+
return {
|
|
13
|
+
caseId: context.caseId,
|
|
14
|
+
caseName: context.caseName,
|
|
15
|
+
filePath: context.filePath ?? context.sourceRef ?? '',
|
|
16
|
+
scope: context.scope,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ReportGroupInput } from '../reporting/types.js';
|
|
2
|
+
import type { AdapterRunHandle } from './adapter.js';
|
|
3
|
+
import type { NormalizedRunSnapshot } from './model.js';
|
|
4
|
+
export declare function buildNormalizedRunSnapshotFromReportGroups(run: AdapterRunHandle, groups: ReportGroupInput[]): NormalizedRunSnapshot;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export function buildNormalizedRunSnapshotFromReportGroups(run, groups) {
|
|
2
|
+
return {
|
|
3
|
+
version: 1,
|
|
4
|
+
completeness: 'final',
|
|
5
|
+
model: {
|
|
6
|
+
run: {
|
|
7
|
+
id: `${run.adapterName}:run`,
|
|
8
|
+
adapterName: run.adapterName,
|
|
9
|
+
status: run.status,
|
|
10
|
+
...(run.diagnostics ? { diagnostics: run.diagnostics.map(diagnostic => ({
|
|
11
|
+
severity: diagnostic.severity,
|
|
12
|
+
message: diagnostic.message,
|
|
13
|
+
})) } : {}),
|
|
14
|
+
},
|
|
15
|
+
units: groups.map((group, index) => ({
|
|
16
|
+
id: unitId(group.groupName, index),
|
|
17
|
+
runId: `${run.adapterName}:run`,
|
|
18
|
+
displayName: group.groupName,
|
|
19
|
+
groupingHints: [{ kind: 'suite', key: group.groupName, label: group.groupName, order: index }],
|
|
20
|
+
})),
|
|
21
|
+
cases: groups.flatMap((group, groupIndex) => group.cases.map((testCase, caseIndex) => {
|
|
22
|
+
const caseId = testCase.caseId ?? `${unitId(group.groupName, groupIndex)}:case-${caseIndex + 1}`;
|
|
23
|
+
const attemptId = `${caseId}:attempt-1`;
|
|
24
|
+
const evaluations = testCase.evaluations?.map((evaluation, evaluationIndex) => ({
|
|
25
|
+
id: `${attemptId}:evaluation-${evaluationIndex + 1}`,
|
|
26
|
+
attemptId,
|
|
27
|
+
score: evaluation.score,
|
|
28
|
+
...(evaluation.trial ? { trial: evaluation.trial } : {}),
|
|
29
|
+
...(evaluation.diagnostics ? { diagnostics: evaluation.diagnostics } : {}),
|
|
30
|
+
}));
|
|
31
|
+
return {
|
|
32
|
+
id: caseId,
|
|
33
|
+
runId: `${run.adapterName}:run`,
|
|
34
|
+
unitId: unitId(group.groupName, groupIndex),
|
|
35
|
+
name: testCase.name,
|
|
36
|
+
state: testCase.state,
|
|
37
|
+
scoringPolicy: scoringPolicyForCase(testCase.state, evaluations),
|
|
38
|
+
groupingHints: [{ kind: 'suite', key: group.groupName, label: group.groupName, order: groupIndex }],
|
|
39
|
+
attempts: [{
|
|
40
|
+
id: attemptId,
|
|
41
|
+
caseId,
|
|
42
|
+
outcome: outcomeForCase(testCase.state),
|
|
43
|
+
durationMs: testCase.runnerDurationMs,
|
|
44
|
+
...(evaluations ? { evaluations } : {}),
|
|
45
|
+
}],
|
|
46
|
+
};
|
|
47
|
+
})),
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function unitId(groupName, index) {
|
|
52
|
+
return `unit-${index + 1}:${groupName}`;
|
|
53
|
+
}
|
|
54
|
+
function scoringPolicyForCase(state, evaluations) {
|
|
55
|
+
if (state === 'skipped' || state === 'pending')
|
|
56
|
+
return { kind: 'non-scoring', reason: state };
|
|
57
|
+
if (evaluations && evaluations.length > 0)
|
|
58
|
+
return { kind: 'from-evaluations' };
|
|
59
|
+
return { kind: 'score', score: state === 'passed' ? 1 : 0 };
|
|
60
|
+
}
|
|
61
|
+
function outcomeForCase(state) {
|
|
62
|
+
if (state === 'passed')
|
|
63
|
+
return { kind: 'passed' };
|
|
64
|
+
if (state === 'failed')
|
|
65
|
+
return { kind: 'failed' };
|
|
66
|
+
return { kind: 'not-run', reason: state };
|
|
67
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NormalizedRunSnapshot, SnapshotCompleteness } from './model.js';
|
|
2
|
+
export interface NormalizedRunValidationResult {
|
|
3
|
+
ok: boolean;
|
|
4
|
+
errors: NormalizedRunValidationError[];
|
|
5
|
+
}
|
|
6
|
+
export interface NormalizedRunValidationError {
|
|
7
|
+
path: string;
|
|
8
|
+
message: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function validateNormalizedRunSnapshot(snapshot: NormalizedRunSnapshot, options: {
|
|
11
|
+
completeness: SnapshotCompleteness;
|
|
12
|
+
}): NormalizedRunValidationResult;
|