@wix/pathgrade 1.0.27 → 1.0.29
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 +12 -19
- package/dist/adapters/jest/invocation-adapter.js +5 -1
- package/dist/adapters/jest/reporter.js +4 -1
- package/dist/adapters/jest/results.js +8 -0
- package/dist/adapters/node-test/index.d.ts +5 -0
- package/dist/adapters/node-test/index.js +35 -7
- package/dist/adapters/node-test/invocation-adapter.js +3 -1
- package/dist/adapters/node-test/runner-adapter.js +22 -15
- package/dist/adapters/vitest/reporter.js +4 -1
- package/dist/agents/claude.js +5 -4
- package/dist/agents/codex-app-server/agent.js +1 -51
- package/dist/agents/codex-app-server/turn-notifications.d.ts +6 -0
- package/dist/agents/codex-app-server/turn-notifications.js +51 -0
- package/dist/agents/codex-app-server/turn-state.d.ts +19 -0
- package/dist/agents/codex-app-server/turn-state.js +1 -0
- package/dist/agents/opencode/protocol.d.ts +7 -0
- package/dist/agents/opencode/protocol.js +47 -0
- package/dist/agents/opencode.js +1 -47
- package/dist/analytics/engine.js +5 -2
- package/dist/commands/report.js +16 -2
- package/dist/commands/run-args.d.ts +1 -0
- package/dist/commands/run-args.js +13 -0
- package/dist/commands/run-changed.js +5 -15
- package/dist/config/pathgrade.d.ts +3 -0
- package/dist/config/pathgrade.js +33 -1
- package/dist/internal/direct-mcp-v2/claude-direct-mcp.d.ts +2 -1
- package/dist/internal/direct-mcp-v2/claude-direct-mcp.js +35 -17
- package/dist/internal/direct-mcp-v2/claude-profile.d.ts +33 -7
- package/dist/internal/direct-mcp-v2/claude-profile.js +104 -72
- package/dist/internal/direct-mcp-v2/public-scenario-runtime.js +13 -16
- package/dist/pathgrade.js +10 -2
- package/dist/reporters/cli.js +13 -6
- package/dist/reporters/github-comment.js +12 -9
- package/dist/reporters/loader.d.ts +1 -0
- package/dist/reporters/loader.js +27 -2
- package/dist/reporters/report-summary.js +13 -5
- package/dist/reporting/core.d.ts +1 -0
- package/dist/reporting/core.js +183 -105
- package/dist/reporting/types.d.ts +19 -3
- package/dist/runners/adapter-loader.js +17 -12
- package/dist/runners/direct-reporter-attempts.d.ts +1 -0
- package/dist/runners/direct-reporter-attempts.js +7 -0
- package/dist/runners/invocation.d.ts +2 -0
- package/dist/runners/model-builders.js +1 -0
- package/dist/runners/model-validation.js +31 -0
- package/dist/runners/model.d.ts +4 -0
- package/dist/runners/orchestrator.d.ts +2 -0
- package/dist/runners/orchestrator.js +11 -1
- package/dist/runners/repeated-attempts.d.ts +7 -0
- package/dist/runners/repeated-attempts.js +149 -0
- package/dist/runners/repeated-invocation.d.ts +7 -0
- package/dist/runners/repeated-invocation.js +129 -0
- package/dist/runners/report-projection.js +16 -6
- package/dist/runners/vitest-adapter.js +10 -0
- package/dist/runners/vitest-invocation.js +2 -0
- package/dist/sdk/agent-runtime-options.d.ts +12 -0
- package/dist/sdk/agent-runtime-options.js +67 -0
- package/dist/sdk/agent.js +4 -57
- package/dist/sdk/case-context.js +7 -2
- package/dist/sdk/lifecycle.js +8 -3
- package/dist/sdk/result-capture.js +4 -1
- package/dist/types.d.ts +34 -5
- package/dist/viewer.html +19 -19
- package/package.json +2 -2
|
@@ -8,7 +8,7 @@ import { projectEndpoints } from './projection.js';
|
|
|
8
8
|
import { admitRuntimeLock } from './runtime-lock.js';
|
|
9
9
|
import { startScenarioHttpHost } from './scenario-http-host.js';
|
|
10
10
|
import { createClaudeParentBridges } from './claude-direct-mcp.js';
|
|
11
|
-
import {
|
|
11
|
+
import { observeClaudeRuntimeControlPlane, resolveClaudeScenarioExecutable, selectClaudeRuntimeTarget } from './claude-profile.js';
|
|
12
12
|
const defaultCoordinatorDependencies = {
|
|
13
13
|
startHost: startScenarioHttpHost,
|
|
14
14
|
scanBearerSurfaces,
|
|
@@ -286,8 +286,10 @@ function scenarioScanRoots(workspacePath, environment) {
|
|
|
286
286
|
].filter((path) => typeof path === 'string' && path.length > 0))];
|
|
287
287
|
}
|
|
288
288
|
export async function startPublicClaudeScenarioRuntime(options, dependencies = {}) {
|
|
289
|
-
const
|
|
290
|
-
const
|
|
289
|
+
const target = dependencies.lock ? undefined : selectClaudeRuntimeTarget();
|
|
290
|
+
const lock = dependencies.lock ?? target.lock;
|
|
291
|
+
const resolveExecutable = dependencies.resolveExecutable
|
|
292
|
+
?? (async () => resolveClaudeScenarioExecutable(target));
|
|
291
293
|
const coordinator = await startScenarioCoordinator({
|
|
292
294
|
...options,
|
|
293
295
|
provider: 'claude',
|
|
@@ -301,16 +303,10 @@ export async function startPublicClaudeScenarioRuntime(options, dependencies = {
|
|
|
301
303
|
environment: options.environment,
|
|
302
304
|
forbiddenSecrets: [bearer.slice('Bearer '.length), bearer],
|
|
303
305
|
...(dependencies.commandVersion ? { commandVersion: dependencies.commandVersion } : {}),
|
|
304
|
-
initializeControlPlane: async () => {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
return {
|
|
309
|
-
normalizedHandshake: CLAUDE_DIRECT_MCP_NORMALIZED_HANDSHAKE,
|
|
310
|
-
capabilities: lock.capabilities,
|
|
311
|
-
close: async () => undefined,
|
|
312
|
-
};
|
|
313
|
-
},
|
|
306
|
+
initializeControlPlane: async () => observeClaudeRuntimeControlPlane({
|
|
307
|
+
createSdkMcpServer,
|
|
308
|
+
query: sdkQuery,
|
|
309
|
+
}),
|
|
314
310
|
});
|
|
315
311
|
const projected = projectEndpoints(context.endpoints, admission.profile);
|
|
316
312
|
if (!Array.isArray(projected))
|
|
@@ -320,7 +316,7 @@ export async function startPublicClaudeScenarioRuntime(options, dependencies = {
|
|
|
320
316
|
}));
|
|
321
317
|
const bridges = createClaudeParentBridges({ artifact: options.artifact, projections: materialized });
|
|
322
318
|
return {
|
|
323
|
-
value: bridges,
|
|
319
|
+
value: { bridges, commandPath: admission.commandPath },
|
|
324
320
|
async interrupt() { },
|
|
325
321
|
async waitForTerminal() { },
|
|
326
322
|
async close() {
|
|
@@ -340,9 +336,10 @@ export async function startPublicClaudeScenarioRuntime(options, dependencies = {
|
|
|
340
336
|
},
|
|
341
337
|
});
|
|
342
338
|
return {
|
|
343
|
-
claudeServers: coordinator.attachment,
|
|
339
|
+
claudeServers: coordinator.attachment.bridges,
|
|
344
340
|
configureSession(sessionOptions) {
|
|
345
|
-
sessionOptions.scenarioMcpServers = coordinator.attachment;
|
|
341
|
+
sessionOptions.scenarioMcpServers = coordinator.attachment.bridges;
|
|
342
|
+
sessionOptions.admittedClaudeCodeExecutable = coordinator.attachment.commandPath;
|
|
346
343
|
},
|
|
347
344
|
beginTurn: coordinator.beginTurn,
|
|
348
345
|
settleTurn: coordinator.settleTurn,
|
package/dist/pathgrade.js
CHANGED
|
@@ -201,7 +201,14 @@ export async function runPathgradeCli(options = {}) {
|
|
|
201
201
|
await clearSidecar(process.cwd());
|
|
202
202
|
const env = buildRunnerEnv(parsed);
|
|
203
203
|
try {
|
|
204
|
-
const config = await resolvePathgradeConfig({
|
|
204
|
+
const config = await resolvePathgradeConfig({
|
|
205
|
+
cwd: process.cwd(),
|
|
206
|
+
cli: parsed.attempts === undefined ? undefined : { attempts: parsed.attempts },
|
|
207
|
+
runnerArgs: parsed.runnerArgs,
|
|
208
|
+
});
|
|
209
|
+
if (!parsed.quiet && config.attempts > 1) {
|
|
210
|
+
console.error(`pathgrade: ${config.attempts} sequential attempts per selected case`);
|
|
211
|
+
}
|
|
205
212
|
const runner = await loadRunnerInvocationAdapter({
|
|
206
213
|
adapterName: parsed.adapterName ?? config.runner.adapter,
|
|
207
214
|
cwd: process.cwd(),
|
|
@@ -210,7 +217,7 @@ export async function runPathgradeCli(options = {}) {
|
|
|
210
217
|
process.exitCode = await runner.run({
|
|
211
218
|
cwd: process.cwd(),
|
|
212
219
|
runnerArgs: [...config.runner.args, ...parsed.runnerArgs],
|
|
213
|
-
env,
|
|
220
|
+
env: { ...env, PATHGRADE_ATTEMPT_COUNT: String(config.attempts) },
|
|
214
221
|
});
|
|
215
222
|
}
|
|
216
223
|
catch (err) {
|
|
@@ -233,6 +240,7 @@ function printHelp(cliName) {
|
|
|
233
240
|
[--since=<ref>] Override base ref (implies git mode)
|
|
234
241
|
[--changed-files=<path>] Use an explicit newline-delimited file list
|
|
235
242
|
[--adapter=<name|path>] Select built-in or third-party runner adapter
|
|
243
|
+
[--attempts=N] Run every selected case N times sequentially
|
|
236
244
|
[--diagnostics] Print full diagnostics for passing evals too
|
|
237
245
|
[--quiet] Suppress the run-start summary
|
|
238
246
|
[--verbose|-v] Stream live per-turn events to stderr during the run
|
package/dist/reporters/cli.js
CHANGED
|
@@ -18,8 +18,8 @@ export async function runCliPreview(resultsDir, opts) {
|
|
|
18
18
|
}
|
|
19
19
|
console.log(`\n${fmt.bold('pathgrade preview')} ${fmt.dim(`${entries.length} reports from ${resolved}`)}\n`);
|
|
20
20
|
for (const { file, ...report } of entries) {
|
|
21
|
-
const
|
|
22
|
-
const isPass =
|
|
21
|
+
const meanReward = report.mean_reward ?? report.pass_rate ?? 0;
|
|
22
|
+
const isPass = report.status === undefined ? meanReward >= 0.5 : report.status === 'pass';
|
|
23
23
|
const trials = report.trials || [];
|
|
24
24
|
const avgDur = trials.reduce((s, t) => s + (t.duration_ms || 0), 0) / (trials.length || 1);
|
|
25
25
|
const totalTokens = trials.reduce((s, t) => s + (t.input_tokens || 0) + (t.output_tokens || 0) + (t.conversation_input_tokens || 0) + (t.conversation_output_tokens || 0), 0);
|
|
@@ -33,9 +33,9 @@ export async function runCliPreview(resultsDir, opts) {
|
|
|
33
33
|
console.log();
|
|
34
34
|
// ── Summary metrics
|
|
35
35
|
const metrics = [
|
|
36
|
-
['
|
|
37
|
-
['
|
|
38
|
-
['pass
|
|
36
|
+
['Mean Reward', `${(meanReward * 100).toFixed(1)}%`],
|
|
37
|
+
['Success Rate', report.success_rate != null ? `${(report.success_rate * 100).toFixed(1)}%` : '—'],
|
|
38
|
+
['pass@k', formatPassAtK(report.pass_at_k, report.pass_at_k_unavailable_reason)],
|
|
39
39
|
['Avg Duration', `${(avgDur / 1000).toFixed(1)}s`],
|
|
40
40
|
['Total Tokens', `~${totalTokens}`],
|
|
41
41
|
['Skills', report.skills_used?.join(', ') || 'none'],
|
|
@@ -47,7 +47,7 @@ export async function runCliPreview(resultsDir, opts) {
|
|
|
47
47
|
// ── Trials
|
|
48
48
|
for (const trial of trials) {
|
|
49
49
|
const evaluated = trial.reward !== undefined;
|
|
50
|
-
const tp = evaluated && trial.reward >= 0.5;
|
|
50
|
+
const tp = evaluated && (trial.runner_outcome === undefined ? trial.reward >= 0.5 : trial.runner_outcome === 'passed' && trial.reward === 1);
|
|
51
51
|
const trialStatus = !evaluated ? fmt.dim('N/A') : tp ? fmt.pass('PASS') : fmt.fail('FAIL');
|
|
52
52
|
const reward = fmt.bold(evaluated ? trial.reward.toFixed(2) : 'n/a');
|
|
53
53
|
const dur = `${((trial.duration_ms || 0) / 1000).toFixed(1)}s`;
|
|
@@ -120,6 +120,13 @@ export async function runCliPreview(resultsDir, opts) {
|
|
|
120
120
|
console.log();
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
+
function formatPassAtK(value, reason) {
|
|
124
|
+
if (typeof value === 'number')
|
|
125
|
+
return `${(value * 100).toFixed(1)}% (legacy v1)`;
|
|
126
|
+
if (value)
|
|
127
|
+
return Object.entries(value).map(([k, metric]) => `@${k} ${(metric * 100).toFixed(1)}%`).join(', ');
|
|
128
|
+
return reason ? `— (${reason.replaceAll('_', ' ')})` : '—';
|
|
129
|
+
}
|
|
123
130
|
function formatScorerStatus(status) {
|
|
124
131
|
switch (status) {
|
|
125
132
|
case 'error':
|
|
@@ -37,6 +37,13 @@ export function commentMarker(commentId) {
|
|
|
37
37
|
function pct(n) {
|
|
38
38
|
return `${(n * 100).toFixed(1)}%`;
|
|
39
39
|
}
|
|
40
|
+
function formatPassAtK(value, reason) {
|
|
41
|
+
if (typeof value === 'number')
|
|
42
|
+
return `${pct(value)} (legacy v1)`;
|
|
43
|
+
if (value)
|
|
44
|
+
return Object.entries(value).map(([k, metric]) => `pass@${k} ${pct(metric)}`).join(', ');
|
|
45
|
+
return reason ? `Unavailable (${reason.replaceAll('_', ' ')})` : 'Unavailable';
|
|
46
|
+
}
|
|
40
47
|
function durationSeconds(ms) {
|
|
41
48
|
return `${(ms / 1000).toFixed(1)}s`;
|
|
42
49
|
}
|
|
@@ -55,10 +62,7 @@ function escapeTableCell(value) {
|
|
|
55
62
|
* posted as a GitHub issue comment body.
|
|
56
63
|
*/
|
|
57
64
|
export function formatReportMarkdown(report, opts) {
|
|
58
|
-
const
|
|
59
|
-
const p = report.overall_pass_rate;
|
|
60
|
-
const overallPassAtK = totalTrials > 0 ? 1 - Math.pow(1 - p, totalTrials) : 0;
|
|
61
|
-
const overallPassPowK = totalTrials > 0 ? Math.pow(p, totalTrials) : 0;
|
|
65
|
+
const meanReward = report.overall_mean_reward ?? report.overall_pass_rate;
|
|
62
66
|
const icon = report.status === 'pass' ? '✅' : '❌';
|
|
63
67
|
const lines = [];
|
|
64
68
|
lines.push(commentMarker(opts.commentId));
|
|
@@ -67,9 +71,8 @@ export function formatReportMarkdown(report, opts) {
|
|
|
67
71
|
lines.push('');
|
|
68
72
|
lines.push(`**Last run:** \`${report.timestamp}\``);
|
|
69
73
|
lines.push('');
|
|
70
|
-
lines.push(`**
|
|
71
|
-
|
|
72
|
-
`**pass^${totalTrials}:** ${pct(overallPassPowK)}`);
|
|
74
|
+
lines.push(`**Mean reward:** ${pct(meanReward)}` +
|
|
75
|
+
(report.attempts_requested ? ` | **Attempts:** ${report.attempts_completed ?? 0}/${report.attempts_requested}` : ''));
|
|
73
76
|
if (opts.detailsUrl) {
|
|
74
77
|
lines.push('');
|
|
75
78
|
lines.push(`**Details:** [Open the full report](${formatLinkDestination(opts.detailsUrl)})`);
|
|
@@ -83,12 +86,12 @@ export function formatReportMarkdown(report, opts) {
|
|
|
83
86
|
lines.push(`Threshold: ${pct(report.threshold)} — ${report.status.toUpperCase()}`);
|
|
84
87
|
}
|
|
85
88
|
lines.push('');
|
|
86
|
-
lines.push('| Group |
|
|
89
|
+
lines.push('| Group | Mean reward | Success rate | pass@k | Skills | Avg duration |');
|
|
87
90
|
lines.push('|---|---|---|---|---|---|');
|
|
88
91
|
for (const group of report.groups) {
|
|
89
92
|
const skills = group.skills_used.length > 0 ? group.skills_used.join(', ') : '—';
|
|
90
93
|
const avg = computeAvgDuration(group.trials);
|
|
91
|
-
lines.push(`| ${escapeTableCell(group.task)} | ${pct(group.pass_rate)} | ${pct(group.
|
|
94
|
+
lines.push(`| ${escapeTableCell(group.task)} | ${pct(group.mean_reward ?? group.pass_rate ?? 0)} | ${group.success_rate === undefined ? '—' : pct(group.success_rate)} | ${escapeTableCell(formatPassAtK(group.pass_at_k, group.pass_at_k_unavailable_reason))} | ${escapeTableCell(skills)} | ${durationSeconds(avg)} |`);
|
|
92
95
|
}
|
|
93
96
|
if (report.selection) {
|
|
94
97
|
lines.push('');
|
package/dist/reporters/loader.js
CHANGED
|
@@ -7,7 +7,13 @@ async function hydrateTraces(report, traceFile, resolved) {
|
|
|
7
7
|
const traceTrials = await fs.readJSON(tracePath);
|
|
8
8
|
if (!Array.isArray(traceTrials) || !Array.isArray(report.trials))
|
|
9
9
|
return;
|
|
10
|
-
|
|
10
|
+
const tracesByAttemptId = new Map(traceTrials
|
|
11
|
+
.filter((trial) => typeof trial?.attempt_id === 'string')
|
|
12
|
+
.map((trial) => [trial.attempt_id, trial]));
|
|
13
|
+
report.trials = report.trials.map((trial, index) => ({
|
|
14
|
+
...trial,
|
|
15
|
+
...(trial.attempt_id ? tracesByAttemptId.get(trial.attempt_id) : traceTrials[index]),
|
|
16
|
+
}));
|
|
11
17
|
}
|
|
12
18
|
export async function loadReports(resultsDir, opts) {
|
|
13
19
|
const resolved = path.resolve(resultsDir);
|
|
@@ -25,8 +31,15 @@ export async function loadReports(resultsDir, opts) {
|
|
|
25
31
|
results.push({ file, ...raw });
|
|
26
32
|
continue;
|
|
27
33
|
}
|
|
34
|
+
if (raw.version !== 1 && raw.version !== 2)
|
|
35
|
+
continue;
|
|
28
36
|
for (const group of raw.groups) {
|
|
29
|
-
const report = {
|
|
37
|
+
const report = {
|
|
38
|
+
file,
|
|
39
|
+
timestamp: raw.timestamp,
|
|
40
|
+
...group,
|
|
41
|
+
status: groupStatus(raw, group),
|
|
42
|
+
};
|
|
30
43
|
if (!opts?.skipTraces && group.trace_file) {
|
|
31
44
|
await hydrateTraces(report, group.trace_file, resolved);
|
|
32
45
|
}
|
|
@@ -37,3 +50,15 @@ export async function loadReports(resultsDir, opts) {
|
|
|
37
50
|
}
|
|
38
51
|
return results;
|
|
39
52
|
}
|
|
53
|
+
function groupStatus(raw, group) {
|
|
54
|
+
if (group.status === 'pass' || group.status === 'fail')
|
|
55
|
+
return group.status;
|
|
56
|
+
const meanReward = group.mean_reward ?? group.pass_rate ?? 0;
|
|
57
|
+
if (typeof raw.threshold === 'number')
|
|
58
|
+
return meanReward >= raw.threshold ? 'pass' : 'fail';
|
|
59
|
+
const outcomes = Array.isArray(group.trials) ? group.trials.map((trial) => trial.runner_outcome) : [];
|
|
60
|
+
if (outcomes.length > 0 && outcomes.every((outcome) => (outcome === 'passed' || outcome === 'failed' || outcome === 'not-run'))) {
|
|
61
|
+
return outcomes.every((outcome) => outcome === 'passed') ? 'pass' : 'fail';
|
|
62
|
+
}
|
|
63
|
+
return meanReward >= 0.5 ? 'pass' : 'fail';
|
|
64
|
+
}
|
|
@@ -3,13 +3,21 @@ import { formatDiagnostics, formatDiagnosticsSummary } from './diagnostics.js';
|
|
|
3
3
|
export function printReportSummary(groups, opts = {}) {
|
|
4
4
|
console.log(`\n${fmt.bold('── pathgrade summary ')}${fmt.dim('─'.repeat(40))}\n`);
|
|
5
5
|
for (const group of groups) {
|
|
6
|
-
const
|
|
6
|
+
const rewardColor = group.mean_reward >= 0.5 ? fmt.green : fmt.red;
|
|
7
7
|
console.log(` ${fmt.bold(group.task)}`);
|
|
8
|
-
console.log(` ${fmt.dim('
|
|
9
|
-
console.log(` ${fmt.dim(
|
|
10
|
-
|
|
8
|
+
console.log(` ${fmt.dim('mean reward'.padEnd(12))} ${rewardColor((group.mean_reward * 100).toFixed(1) + '%')}`);
|
|
9
|
+
console.log(` ${fmt.dim('success'.padEnd(12))} ${group.success_rate === undefined ? '—' : `${(group.success_rate * 100).toFixed(1)}%`}`);
|
|
10
|
+
if (group.pass_at_k) {
|
|
11
|
+
for (const [k, value] of Object.entries(group.pass_at_k)) {
|
|
12
|
+
console.log(` ${fmt.dim(`pass@${k}`.padEnd(12))} ${(value * 100).toFixed(1)}%`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
const reason = group.pass_at_k_unavailable_reason?.replaceAll('_', ' ') ?? 'repeated binary attempts required';
|
|
17
|
+
console.log(` ${fmt.dim('pass@k'.padEnd(12))} — (${reason})`);
|
|
18
|
+
}
|
|
11
19
|
console.log(` ${fmt.dim('avg time'.padEnd(12))} ${(group.average_duration_ms / 1000).toFixed(1)}s`);
|
|
12
|
-
console.log(` ${fmt.dim('
|
|
20
|
+
console.log(` ${fmt.dim('attempts'.padEnd(12))} ${group.trial_count}`);
|
|
13
21
|
console.log();
|
|
14
22
|
for (const diagnostic of group.diagnostics) {
|
|
15
23
|
const shouldPrintFull = opts.forceVerbose
|
package/dist/reporting/core.d.ts
CHANGED