@wix/pathgrade 1.0.37 → 1.0.39
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 +3 -1
- package/dist/adapter-kit/index.d.ts +2 -2
- package/dist/adapter-kit/index.js +1 -1
- package/dist/adapters/jest/lifecycle.js +4 -2
- package/dist/adapters/node-test/index.js +4 -2
- package/dist/agents/claude/sdk-message-projector.js +4 -5
- package/dist/agents/claude/tool-permission-bridge.js +2 -1
- package/dist/agents/codex-app-server/item-projection.js +2 -2
- package/dist/agents/codex-app-server/mcp-approval-correlator.js +3 -2
- package/dist/agents/opencode.js +4 -5
- package/dist/commands/report.js +5 -25
- package/dist/internal/direct-mcp-v2/acp-author-projector.js +20 -7
- package/dist/reporters/cli.js +20 -1
- package/dist/reporters/github-comment.js +18 -7
- package/dist/reporters/loader.d.ts +4 -0
- package/dist/reporters/loader.js +26 -9
- package/dist/reporting/comparison-contract.js +20 -6
- package/dist/reporting/core.js +18 -8
- package/dist/reporting/report-parser.js +57 -2
- package/dist/reporting/types.d.ts +2 -1
- package/dist/runners/adapter.d.ts +4 -2
- package/dist/runners/lifecycle-hooks.js +6 -1
- package/dist/runners/orchestrator.js +3 -4
- package/dist/runners/repeated-invocation.js +2 -5
- package/dist/runners/report-projection.js +1 -0
- package/dist/runners/vitest-lifecycle.d.ts +1 -0
- package/dist/runners/vitest-lifecycle.js +21 -4
- package/dist/sdk/agent-flow.d.ts +51 -0
- package/dist/sdk/agent-flow.js +23 -0
- package/dist/sdk/agent.js +10 -1
- package/dist/sdk/evaluate.d.ts +2 -1
- package/dist/sdk/evaluate.js +114 -4
- package/dist/sdk/index.d.ts +4 -2
- package/dist/sdk/index.js +2 -2
- package/dist/sdk/judge-prompt-builder.js +11 -7
- package/dist/sdk/lifecycle.d.ts +3 -1
- package/dist/sdk/lifecycle.js +68 -5
- package/dist/sdk/mcp-event-input.d.ts +1 -0
- package/dist/sdk/mcp-event-input.js +3 -0
- package/dist/sdk/mcp-evidence.js +16 -3
- package/dist/sdk/mcp-safety.js +2 -2
- package/dist/sdk/result-capture.d.ts +14 -1
- package/dist/sdk/result-capture.js +49 -0
- package/dist/sdk/scripted-mcp-events.js +3 -2
- package/dist/sdk/tool-event-log.js +18 -3
- package/dist/sdk/tool-event-secrets.d.ts +4 -0
- package/dist/sdk/tool-event-secrets.js +33 -2
- package/dist/sdk/types.d.ts +6 -0
- package/dist/tool-event-results.d.ts +3 -0
- package/dist/tool-event-results.js +153 -23
- package/dist/types.d.ts +13 -7
- package/docs/agent-flow-evaluation.md +31 -0
- package/package.json +3 -2
|
@@ -6,18 +6,44 @@ export function parsePathgradeReport(value) {
|
|
|
6
6
|
|| (value.version === 2 && (typeof value.overall_mean_reward !== 'number' || !validAttemptCounts(value)))
|
|
7
7
|
|| !optionalNumber(value.threshold)
|
|
8
8
|
|| (value.status !== 'pass' && value.status !== 'fail')
|
|
9
|
+
|| !optionalGateStatus(value.runner_status)
|
|
10
|
+
|| !optionalThresholdStatus(value.threshold_status)
|
|
9
11
|
|| (value.run_kind !== undefined && value.run_kind !== 'evaluation' && value.run_kind !== 'no-affected')
|
|
10
12
|
|| !Array.isArray(value.groups)
|
|
11
13
|
|| !value.groups.every(group => isReportGroup(group, value.version))
|
|
12
14
|
|| !isSelection(value.selection)) {
|
|
13
15
|
throw new Error('PathGrade results.json is missing or has an unsupported schema');
|
|
14
16
|
}
|
|
15
|
-
|
|
17
|
+
const threshold = value.threshold;
|
|
18
|
+
const groups = value.groups.map(group => {
|
|
19
|
+
const runnerStatus = deriveRunnerStatus(group);
|
|
20
|
+
const thresholdStatus = deriveThresholdStatus(group.mean_reward ?? group.pass_rate, threshold);
|
|
21
|
+
return {
|
|
22
|
+
...group,
|
|
23
|
+
runner_status: runnerStatus,
|
|
24
|
+
threshold_status: thresholdStatus,
|
|
25
|
+
status: composeStatus(runnerStatus, thresholdStatus),
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
const legacyFailure = value.runner_status === undefined && value.status === 'fail' && groups.length === 0;
|
|
29
|
+
const runnerStatus = value.runner_status !== 'fail' && !legacyFailure
|
|
30
|
+
&& (groups.length > 0 || value.runner_status === 'pass' || value.status === 'pass')
|
|
31
|
+
&& groups.every(group => group.runner_status === 'pass') ? 'pass' : 'fail';
|
|
32
|
+
const thresholdStatus = deriveThresholdStatus(value.overall_mean_reward ?? value.overall_pass_rate, threshold);
|
|
33
|
+
return {
|
|
34
|
+
...value,
|
|
35
|
+
groups,
|
|
36
|
+
runner_status: runnerStatus,
|
|
37
|
+
threshold_status: thresholdStatus,
|
|
38
|
+
status: composeStatus(runnerStatus, thresholdStatus),
|
|
39
|
+
};
|
|
16
40
|
}
|
|
17
41
|
function isReportGroup(value, version) {
|
|
18
42
|
return isRecord(value)
|
|
19
43
|
&& typeof value.task === 'string'
|
|
20
44
|
&& (version === 1 ? legacyMetrics(value) : schemaV2Metrics(value))
|
|
45
|
+
&& optionalGateStatus(value.runner_status)
|
|
46
|
+
&& optionalThresholdStatus(value.threshold_status)
|
|
21
47
|
&& optionalString(value.source_file)
|
|
22
48
|
&& typeof value.trace_file === 'string'
|
|
23
49
|
&& Array.isArray(value.skills_used)
|
|
@@ -26,6 +52,34 @@ function isReportGroup(value, version) {
|
|
|
26
52
|
&& value.trials.every(isTrial)
|
|
27
53
|
&& (value.comparison_contract === undefined || isComparisonContract(value.comparison_contract));
|
|
28
54
|
}
|
|
55
|
+
function deriveRunnerStatus(value) {
|
|
56
|
+
if (value.runner_status === 'fail')
|
|
57
|
+
return 'fail';
|
|
58
|
+
const outcomes = value.trials
|
|
59
|
+
.map(trial => trial.runner_outcome)
|
|
60
|
+
.filter(outcome => outcome !== undefined);
|
|
61
|
+
if (outcomes.length > 0)
|
|
62
|
+
return outcomes.every(outcome => outcome === 'passed') ? 'pass' : 'fail';
|
|
63
|
+
if (value.runner_status === 'pass')
|
|
64
|
+
return value.runner_status;
|
|
65
|
+
if (value.status === 'pass' || value.status === 'fail')
|
|
66
|
+
return value.status;
|
|
67
|
+
return value.pass_rate === 1 ? 'pass' : 'fail';
|
|
68
|
+
}
|
|
69
|
+
function deriveThresholdStatus(reward, threshold) {
|
|
70
|
+
if (threshold === undefined)
|
|
71
|
+
return 'not_configured';
|
|
72
|
+
return typeof reward === 'number' && reward >= threshold ? 'pass' : 'fail';
|
|
73
|
+
}
|
|
74
|
+
function composeStatus(runnerStatus, thresholdStatus) {
|
|
75
|
+
return runnerStatus === 'fail' || thresholdStatus === 'fail' ? 'fail' : 'pass';
|
|
76
|
+
}
|
|
77
|
+
function optionalGateStatus(value) {
|
|
78
|
+
return value === undefined || value === 'pass' || value === 'fail';
|
|
79
|
+
}
|
|
80
|
+
function optionalThresholdStatus(value) {
|
|
81
|
+
return optionalGateStatus(value) || value === 'not_configured';
|
|
82
|
+
}
|
|
29
83
|
function isTrial(value) {
|
|
30
84
|
return isRecord(value)
|
|
31
85
|
&& typeof value.trial_id === 'number'
|
|
@@ -40,7 +94,8 @@ function isTrial(value) {
|
|
|
40
94
|
&& value.scorer_results.every(scorer => isRecord(scorer)
|
|
41
95
|
&& typeof scorer.scorer_type === 'string'
|
|
42
96
|
&& typeof scorer.score === 'number'
|
|
43
|
-
&& typeof scorer.weight === 'number'
|
|
97
|
+
&& typeof scorer.weight === 'number'
|
|
98
|
+
&& (scorer.status === undefined || scorer.status === 'ok' || scorer.status === 'error' || scorer.status === 'skipped'));
|
|
44
99
|
}
|
|
45
100
|
function legacyMetrics(value) {
|
|
46
101
|
return typeof value.pass_rate === 'number'
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { DiagnosticsReport } from '../sdk/diagnostics.js';
|
|
2
2
|
import type { AgentExecutionMetadata, EvaluationResultKind } from '../sdk/types.js';
|
|
3
|
-
import type { AttemptOutcome } from '../runners/model.js';
|
|
3
|
+
import type { AttemptOutcome, RunStatus } from '../runners/model.js';
|
|
4
4
|
import type { PathgradeReport, PathgradeSelectionReport, TrialResult } from '../types.js';
|
|
5
5
|
export type ReportCaseState = 'passed' | 'failed' | 'skipped' | 'pending';
|
|
6
6
|
export interface ReportRunInput {
|
|
7
|
+
runStatus?: RunStatus;
|
|
7
8
|
threshold?: number;
|
|
8
9
|
selection?: PathgradeSelectionReport;
|
|
9
10
|
attemptsRequested?: number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PathgradeSelectionReport } from '../types.js';
|
|
2
|
-
import type { Agent, PathgradeTestMeta, RecordedEvalResult } from '../sdk/types.js';
|
|
2
|
+
import type { Agent, AgentFlowTrace, PathgradeTestMeta, RecordedEvalResult } from '../sdk/types.js';
|
|
3
3
|
import type { CaseContextScope } from '../sdk/case-context.js';
|
|
4
4
|
import type { NormalizedRunSnapshot } from './model.js';
|
|
5
5
|
export declare const runnerAdapterContractVersion = 1;
|
|
@@ -61,5 +61,7 @@ export interface AdapterCaseContext {
|
|
|
61
61
|
}
|
|
62
62
|
export interface EvalResultEvent {
|
|
63
63
|
result: RecordedEvalResult;
|
|
64
|
-
agent
|
|
64
|
+
agent?: Agent;
|
|
65
|
+
flow?: AgentFlowTrace;
|
|
66
|
+
case?: import('../sdk/case-context.js').CaseContext;
|
|
65
67
|
}
|
|
@@ -2,7 +2,12 @@ import { lifecycleCore } from '../sdk/lifecycle.js';
|
|
|
2
2
|
import { runWithCaseContext } from '../sdk/case-context.js';
|
|
3
3
|
export function createRunnerLifecycleHooks() {
|
|
4
4
|
return {
|
|
5
|
-
onResult: event =>
|
|
5
|
+
onResult: event => {
|
|
6
|
+
if (event.flow)
|
|
7
|
+
lifecycleCore.recordFlowResult(event.result, event.flow, event.case?.caseId);
|
|
8
|
+
else if (event.agent)
|
|
9
|
+
lifecycleCore.recordResult(event.result, event.agent);
|
|
10
|
+
},
|
|
6
11
|
withCaseContext: (context, run) => runWithCaseContext(toCaseContext(context), run),
|
|
7
12
|
flushCase: caseId => lifecycleCore.flushCase({ caseId }),
|
|
8
13
|
cleanupRun: () => lifecycleCore.cleanupAll(),
|
|
@@ -43,8 +43,8 @@ export async function runWithAdapter(input) {
|
|
|
43
43
|
if (loadedSelection) {
|
|
44
44
|
built = buildPathgradeReport({
|
|
45
45
|
threshold: options.threshold,
|
|
46
|
+
...reportInput,
|
|
46
47
|
selection: loadedSelection,
|
|
47
|
-
groups: reportInput.groups,
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
50
|
const mode = options.reporterMode ?? 'cli';
|
|
@@ -56,14 +56,13 @@ export async function runWithAdapter(input) {
|
|
|
56
56
|
if (mode === 'browser') {
|
|
57
57
|
await options.openBrowser?.();
|
|
58
58
|
}
|
|
59
|
-
if (options.threshold != null && built.report.
|
|
59
|
+
if (options.threshold != null && built.report.threshold_status === 'fail') {
|
|
60
60
|
options.onThresholdFailure?.({
|
|
61
61
|
overallPassRate: built.report.overall_pass_rate,
|
|
62
62
|
threshold: options.threshold,
|
|
63
63
|
});
|
|
64
|
-
return runExitCode === 0 ? 1 : runExitCode;
|
|
65
64
|
}
|
|
66
|
-
return runExitCode;
|
|
65
|
+
return built.report.status === 'fail' && runExitCode === 0 ? 1 : runExitCode;
|
|
67
66
|
}
|
|
68
67
|
finally {
|
|
69
68
|
await lifecycle.cleanupRun();
|
|
@@ -80,13 +80,10 @@ export function withRepeatedAttempts(input) {
|
|
|
80
80
|
if (input.config.reporter === 'browser') {
|
|
81
81
|
await (input.openBrowser ?? openBrowserViewer)();
|
|
82
82
|
}
|
|
83
|
-
if (input.config.ci.threshold != null && built.report.
|
|
83
|
+
if (input.config.ci.threshold != null && built.report.threshold_status === 'fail') {
|
|
84
84
|
process.stdout.write(`\n CI THRESHOLD FAILED avg score ${built.report.overall_pass_rate.toFixed(3)} < threshold ${input.config.ci.threshold}\n\n`);
|
|
85
|
-
resultCode = firstNonzeroExit || 1;
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
resultCode = firstNonzeroExit;
|
|
89
85
|
}
|
|
86
|
+
resultCode = firstNonzeroExit || (built.report.status === 'fail' ? 1 : 0);
|
|
90
87
|
}
|
|
91
88
|
}
|
|
92
89
|
catch (error) {
|
|
@@ -41,6 +41,7 @@ export function projectNormalizedRunSnapshotToReportInput(snapshot, options = {}
|
|
|
41
41
|
}
|
|
42
42
|
return {
|
|
43
43
|
...(options.selection ? { selection: options.selection } : {}),
|
|
44
|
+
runStatus: snapshot.model.run.status,
|
|
44
45
|
attemptsRequested: snapshot.model.run.attemptsRequested ?? 1,
|
|
45
46
|
attemptsCompleted: snapshot.model.run.attemptsCompleted ?? 1,
|
|
46
47
|
groups: [...groups.values()],
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { subscribeToEvaluationResults } from '../sdk/result-capture.js';
|
|
3
3
|
import { getCurrentCaseContext, installCaseContextProvider, runWithCaseContext, } from '../sdk/case-context.js';
|
|
4
4
|
import { lifecycleCore } from '../sdk/lifecycle.js';
|
|
5
5
|
export function createVitestLifecycleHooks() {
|
|
6
6
|
return {
|
|
7
|
-
onResult: event =>
|
|
7
|
+
onResult: event => {
|
|
8
|
+
if (event.flow)
|
|
9
|
+
recordVitestFlowResult(event.result, event.flow, event.case);
|
|
10
|
+
else if (event.agent)
|
|
11
|
+
recordVitestResult(event.result, event.agent);
|
|
12
|
+
},
|
|
8
13
|
withCaseContext: (context, run) => runWithCaseContext(toCaseContext(context), run),
|
|
9
14
|
flushCase: caseId => lifecycleCore.flushCase({ caseId }),
|
|
10
15
|
cleanupRun: () => lifecycleCore.cleanupAll(),
|
|
@@ -18,7 +23,11 @@ export function installVitestLifecycle(input) {
|
|
|
18
23
|
let resultsUnsubscribed = false;
|
|
19
24
|
input.aroundEach?.(async (runTest, { task }) => lifecycle.withCaseContext(caseContextForTask(task), runTest));
|
|
20
25
|
input.afterEach(async ({ task }) => {
|
|
21
|
-
const
|
|
26
|
+
const filePath = filePathForTask(task);
|
|
27
|
+
const results = [
|
|
28
|
+
...(filePath ? await lifecycle.flushCase(`file:${filePath}`) : []),
|
|
29
|
+
...await lifecycle.flushCase(task.id),
|
|
30
|
+
];
|
|
22
31
|
if (results.length > 0) {
|
|
23
32
|
task.meta.pathgrade = results;
|
|
24
33
|
}
|
|
@@ -69,7 +78,9 @@ export function resetVitestLifecycle(handle) {
|
|
|
69
78
|
handle?.restore();
|
|
70
79
|
}
|
|
71
80
|
function defaultSubscribeToResults(callback) {
|
|
72
|
-
return
|
|
81
|
+
return subscribeToEvaluationResults(event => callback(event.sourceKind === 'flow'
|
|
82
|
+
? { result: event.result, flow: event.flow, case: event.case }
|
|
83
|
+
: { result: event.result, agent: event.agent, case: event.case }), { owner: 'adapter', key: 'vitest-lifecycle' });
|
|
73
84
|
}
|
|
74
85
|
function defaultInstallFileContextProvider() {
|
|
75
86
|
return installCaseContextProvider(currentFileContext);
|
|
@@ -148,3 +159,9 @@ function recordVitestResult(result, agent) {
|
|
|
148
159
|
}
|
|
149
160
|
lifecycleCore.recordResult(result, agent);
|
|
150
161
|
}
|
|
162
|
+
function recordVitestFlowResult(result, flow, capturedCase) {
|
|
163
|
+
const caseId = capturedCase?.scope === 'runner-case'
|
|
164
|
+
? capturedCase.caseId
|
|
165
|
+
: currentTaskId() || capturedCase?.caseId;
|
|
166
|
+
lifecycleCore.recordFlowResult(result, flow, caseId || undefined);
|
|
167
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type AgentFlowEvidenceAvailability = 'complete' | 'partial' | 'unavailable';
|
|
2
|
+
export interface AgentFlowEvidenceCompleteness {
|
|
3
|
+
topology: AgentFlowEvidenceAvailability;
|
|
4
|
+
outcomes: AgentFlowEvidenceAvailability;
|
|
5
|
+
timing: AgentFlowEvidenceAvailability;
|
|
6
|
+
usage: AgentFlowEvidenceAvailability;
|
|
7
|
+
runtimeIdentity: AgentFlowEvidenceAvailability;
|
|
8
|
+
}
|
|
9
|
+
export interface AgentFlowParticipant {
|
|
10
|
+
id: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
role?: string;
|
|
13
|
+
runtime?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface AgentFlowInteraction {
|
|
16
|
+
id: string;
|
|
17
|
+
sequence: number;
|
|
18
|
+
sourceParticipantId: string;
|
|
19
|
+
targetParticipantId: string;
|
|
20
|
+
operation: string;
|
|
21
|
+
parentInteractionId?: string;
|
|
22
|
+
taskId?: string;
|
|
23
|
+
contextId?: string;
|
|
24
|
+
state?: string;
|
|
25
|
+
inputText?: string;
|
|
26
|
+
outputText?: string;
|
|
27
|
+
error?: string;
|
|
28
|
+
startedAt?: string;
|
|
29
|
+
endedAt?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface AgentFlowTrace {
|
|
32
|
+
version: 1;
|
|
33
|
+
protocol?: string;
|
|
34
|
+
rootParticipantId: string;
|
|
35
|
+
participants: readonly AgentFlowParticipant[];
|
|
36
|
+
interactions: readonly AgentFlowInteraction[];
|
|
37
|
+
outcome: {
|
|
38
|
+
state: string;
|
|
39
|
+
outputText?: string;
|
|
40
|
+
error?: string;
|
|
41
|
+
};
|
|
42
|
+
completeness: AgentFlowEvidenceCompleteness;
|
|
43
|
+
}
|
|
44
|
+
export interface AgentFlowSummary {
|
|
45
|
+
participant_count: number;
|
|
46
|
+
interaction_count: number;
|
|
47
|
+
max_observed_depth: number;
|
|
48
|
+
root_state: string;
|
|
49
|
+
completeness: AgentFlowEvidenceCompleteness;
|
|
50
|
+
}
|
|
51
|
+
export declare function summarizeFlow(flow: AgentFlowTrace): AgentFlowSummary;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function summarizeFlow(flow) {
|
|
2
|
+
const parents = new Map(flow.interactions.map(interaction => [interaction.id, interaction.parentInteractionId]));
|
|
3
|
+
const depth = (id) => {
|
|
4
|
+
let current = parents.get(id);
|
|
5
|
+
let value = 1;
|
|
6
|
+
const seen = new Set([id]);
|
|
7
|
+
while (current && !seen.has(current)) {
|
|
8
|
+
seen.add(current);
|
|
9
|
+
value += 1;
|
|
10
|
+
current = parents.get(current);
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
};
|
|
14
|
+
return {
|
|
15
|
+
participant_count: flow.participants.length,
|
|
16
|
+
interaction_count: flow.interactions.length,
|
|
17
|
+
max_observed_depth: flow.interactions.length > 0
|
|
18
|
+
? Math.max(...flow.interactions.map(interaction => depth(interaction.id)))
|
|
19
|
+
: 0,
|
|
20
|
+
root_state: flow.outcome.state,
|
|
21
|
+
completeness: structuredClone(flow.completeness),
|
|
22
|
+
};
|
|
23
|
+
}
|
package/dist/sdk/agent.js
CHANGED
|
@@ -20,6 +20,8 @@ import { collectOpenCodeMcpToolNames } from '../agents/opencode/contract.js';
|
|
|
20
20
|
import { collectSensitiveEnvValues } from '../tool-event-results.js';
|
|
21
21
|
import { createAskUserHandler } from './ask-bus/handler.js';
|
|
22
22
|
import { resolveAgentRuntimeOptions } from './agent-runtime-options.js';
|
|
23
|
+
import { extractToolEventsFromLog } from '../tool-events.js';
|
|
24
|
+
import { clearToolEventRuntimeMetadata } from './tool-event-secrets.js';
|
|
23
25
|
/**
|
|
24
26
|
* Test-only injection point: override the sink used by the next emitter
|
|
25
27
|
* built inside `createAgent`. Pass `null` to restore the default (stderr).
|
|
@@ -392,7 +394,14 @@ class AgentImpl {
|
|
|
392
394
|
}
|
|
393
395
|
}
|
|
394
396
|
finally {
|
|
395
|
-
|
|
397
|
+
try {
|
|
398
|
+
await this.ws.dispose();
|
|
399
|
+
}
|
|
400
|
+
finally {
|
|
401
|
+
for (const event of extractToolEventsFromLog(this._log)) {
|
|
402
|
+
clearToolEventRuntimeMetadata(event);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
396
405
|
}
|
|
397
406
|
}
|
|
398
407
|
}
|
package/dist/sdk/evaluate.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Agent, Scorer, RecordedEvalResult, ScorerResultEntry, EvaluateOptions } from './types.js';
|
|
1
|
+
import type { Agent, Scorer, RecordedEvalResult, ScorerResultEntry, EvaluateOptions, AgentFlowTrace } from './types.js';
|
|
2
2
|
export type OnScorerErrorMode = NonNullable<EvaluateOptions['onScorerError']>;
|
|
3
3
|
export declare class EvalScorerError extends Error {
|
|
4
4
|
readonly scorerErrors: ScorerResultEntry[];
|
|
@@ -9,6 +9,7 @@ type EvaluateFromSnapshot = (snapshotPath: string, scorers: Scorer[], opts?: Eva
|
|
|
9
9
|
type EvaluateFn = ((agent: Agent, scorers: Scorer[], opts?: EvaluateOptions) => Promise<RecordedEvalResult>) & {
|
|
10
10
|
fromSnapshot: EvaluateFromSnapshot;
|
|
11
11
|
};
|
|
12
|
+
export declare function evaluateFlow(flow: AgentFlowTrace, scorers: Scorer[], opts?: EvaluateOptions): Promise<RecordedEvalResult>;
|
|
12
13
|
/** Internal runConversation hook: step scorers must not consume final-run attribution. */
|
|
13
14
|
export declare function evaluateStepScorers(agent: Agent, scorers: Scorer[], opts?: EvaluateOptions): Promise<RecordedEvalResult>;
|
|
14
15
|
export declare const evaluate: EvaluateFn;
|
package/dist/sdk/evaluate.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { countShellCommandsFromLog, extractSkillsFromLog, extractToolEventsFromLog } from '../tool-events.js';
|
|
2
2
|
import { createScorerRevision } from '../reporting/comparison-contract.js';
|
|
3
3
|
import { getRuntime } from './eval-runtime.js';
|
|
4
|
-
import { emitEvalResult } from './result-capture.js';
|
|
4
|
+
import { emitEvalResult, emitFlowEvalResult } from './result-capture.js';
|
|
5
5
|
import { runJudgePipeline } from './judge-pipeline.js';
|
|
6
6
|
import { runScorer } from './run-scorer.js';
|
|
7
7
|
import { createLLMClient } from '../utils/llm.js';
|
|
8
8
|
import { sandboxExec } from '../providers/sandbox-exec.js';
|
|
9
9
|
import { buildTranscript, loadRunSnapshot, WorkspaceMissingError } from './snapshots.js';
|
|
10
|
+
import { sanitizePersistenceValue } from '../tool-event-results.js';
|
|
11
|
+
import { getOriginalMcpInput } from './mcp-event-input.js';
|
|
12
|
+
import { cloneToolEventWithRuntimeMetadata, collectToolEventSensitiveValues, } from './tool-event-secrets.js';
|
|
10
13
|
import fs from 'fs-extra';
|
|
11
14
|
import path from 'path';
|
|
15
|
+
import { summarizeFlow } from './agent-flow.js';
|
|
12
16
|
export class EvalScorerError extends Error {
|
|
13
17
|
scorerErrors;
|
|
14
18
|
result;
|
|
@@ -19,6 +23,60 @@ export class EvalScorerError extends Error {
|
|
|
19
23
|
this.scorerErrors = scorerErrors;
|
|
20
24
|
}
|
|
21
25
|
}
|
|
26
|
+
export async function evaluateFlow(flow, scorers, opts) {
|
|
27
|
+
if (scorers.some(scorer => scorer.type === 'tool_usage')) {
|
|
28
|
+
throw new TypeError('tool_usage scorers require an Agent and cannot evaluate an AgentFlowTrace');
|
|
29
|
+
}
|
|
30
|
+
if (scorers.some(scorer => scorer.type === 'judge' && scorer.tools && scorer.tools.length > 0)) {
|
|
31
|
+
throw new TypeError('tool-enabled judge scorers require an Agent workspace and cannot evaluate an AgentFlowTrace');
|
|
32
|
+
}
|
|
33
|
+
const trackedLLM = opts?.llm ?? createLLMClient({ adapters: [{
|
|
34
|
+
name: 'runtime', isAvailable: async () => true,
|
|
35
|
+
call: (prompt, callOpts) => getRuntime().llm.call(prompt, callOpts),
|
|
36
|
+
}] });
|
|
37
|
+
const unavailable = async () => {
|
|
38
|
+
throw new WorkspaceMissingError('AgentFlowTrace does not include a workspace');
|
|
39
|
+
};
|
|
40
|
+
const ctx = {
|
|
41
|
+
workspace: '',
|
|
42
|
+
log: [],
|
|
43
|
+
transcript: JSON.stringify(flow),
|
|
44
|
+
toolEvents: [],
|
|
45
|
+
runCommand: unavailable,
|
|
46
|
+
artifacts: { list: () => [], read: unavailable, latest: async () => null },
|
|
47
|
+
flow,
|
|
48
|
+
};
|
|
49
|
+
const scoringStartedAt = performance.now();
|
|
50
|
+
const beforeTokenUsage = trackedLLM.tokenUsage ?? { inputTokens: 0, outputTokens: 0 };
|
|
51
|
+
const { result: evalResult, tokens: deltaTokenUsage } = trackedLLM.measure
|
|
52
|
+
? await trackedLLM.measure(() => evaluateWithContext(ctx, scorers, { ...opts, llm: trackedLLM }))
|
|
53
|
+
: await (async () => {
|
|
54
|
+
const result = await evaluateWithContext(ctx, scorers, { ...opts, llm: trackedLLM });
|
|
55
|
+
const afterTokenUsage = trackedLLM.tokenUsage ?? beforeTokenUsage;
|
|
56
|
+
return { result, tokens: {
|
|
57
|
+
inputTokens: Math.max(0, afterTokenUsage.inputTokens - beforeTokenUsage.inputTokens),
|
|
58
|
+
outputTokens: Math.max(0, afterTokenUsage.outputTokens - beforeTokenUsage.outputTokens),
|
|
59
|
+
} };
|
|
60
|
+
})();
|
|
61
|
+
const scorerRevision = createScorerRevision(scorers);
|
|
62
|
+
const recordedResult = {
|
|
63
|
+
...evalResult,
|
|
64
|
+
tokenUsage: deltaTokenUsage,
|
|
65
|
+
resultKind: 'evaluated',
|
|
66
|
+
scoringDurationMs: Math.max(0, performance.now() - scoringStartedAt),
|
|
67
|
+
recordedAt: new Date().toISOString(),
|
|
68
|
+
...(opts?.evaluationDefinitionKey ? { evaluationDefinitionKey: opts.evaluationDefinitionKey } : {}),
|
|
69
|
+
...(scorerRevision ? { scorerRevision } : {}),
|
|
70
|
+
trial: {
|
|
71
|
+
...buildTrialResult([], { ...evalResult, tokenUsage: deltaTokenUsage }),
|
|
72
|
+
flow_summary: summarizeFlow(flow),
|
|
73
|
+
flow_trace: structuredClone(flow),
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
emitFlowEvalResult({ result: recordedResult, flow });
|
|
77
|
+
maybeThrowOnScorerErrors(recordedResult, opts?.onScorerError ?? 'skip');
|
|
78
|
+
return recordedResult;
|
|
79
|
+
}
|
|
22
80
|
/**
|
|
23
81
|
* Run scorers against a trial and compute a weighted average score.
|
|
24
82
|
*
|
|
@@ -99,6 +157,9 @@ export function evaluateStepScorers(agent, scorers, opts) {
|
|
|
99
157
|
return evaluateAgent(agent, scorers, opts, false);
|
|
100
158
|
}
|
|
101
159
|
async function fromSnapshot(snapshotPath, scorers, opts) {
|
|
160
|
+
if (opts?.deterministicToolEvidence === 'live') {
|
|
161
|
+
throw new TypeError("evaluate.fromSnapshot() does not support deterministicToolEvidence: 'live'");
|
|
162
|
+
}
|
|
102
163
|
const snapshot = await loadRunSnapshot(snapshotPath);
|
|
103
164
|
const trackedLLM = opts?.llm ?? createLLMClient({ adapters: [{
|
|
104
165
|
name: 'runtime', isAvailable: async () => true,
|
|
@@ -146,6 +207,9 @@ async function evaluateWithContext(ctx, scorers, opts) {
|
|
|
146
207
|
call: (prompt, callOpts) => getRuntime().llm.call(prompt, callOpts),
|
|
147
208
|
}] });
|
|
148
209
|
const onScorerError = opts?.onScorerError ?? 'skip';
|
|
210
|
+
const deterministicCtx = opts?.deterministicToolEvidence === 'live'
|
|
211
|
+
? createLiveDeterministicContext(ctx)
|
|
212
|
+
: ctx;
|
|
149
213
|
const phase1 = [];
|
|
150
214
|
const phase2 = [];
|
|
151
215
|
const phase3 = [];
|
|
@@ -164,7 +228,7 @@ async function evaluateWithContext(ctx, scorers, opts) {
|
|
|
164
228
|
}
|
|
165
229
|
}
|
|
166
230
|
const results = [];
|
|
167
|
-
const phase1Results = await Promise.all(phase1.map((g) => runScorer(g,
|
|
231
|
+
const phase1Results = await Promise.all(phase1.map((g) => runScorer(g, deterministicCtx)));
|
|
168
232
|
results.push(...phase1Results);
|
|
169
233
|
const anyCheckFailed = failFast && phase1Results.some((r) => r.type === 'check' && r.status !== 'error' && r.score === 0);
|
|
170
234
|
if (anyCheckFailed) {
|
|
@@ -175,7 +239,7 @@ async function evaluateWithContext(ctx, scorers, opts) {
|
|
|
175
239
|
else {
|
|
176
240
|
const judgeResults = await runJudgePipeline(phase2, ctx, { llm: trackedLLM });
|
|
177
241
|
results.push(...judgeResults);
|
|
178
|
-
const phase3Results = await Promise.all(phase3.map((g) => runScorer(g,
|
|
242
|
+
const phase3Results = await Promise.all(phase3.map((g) => runScorer(g, deterministicCtx)));
|
|
179
243
|
results.push(...phase3Results);
|
|
180
244
|
}
|
|
181
245
|
const tokenUsage = trackedLLM.tokenUsage ?? { inputTokens: 0, outputTokens: 0 };
|
|
@@ -185,12 +249,58 @@ async function evaluateWithContext(ctx, scorers, opts) {
|
|
|
185
249
|
const totalWeight = scoringResults.reduce((sum, r) => sum + r.weight, 0);
|
|
186
250
|
const weightedSum = scoringResults.reduce((sum, r) => sum + r.score * r.weight, 0);
|
|
187
251
|
const score = totalWeight > 0 ? weightedSum / totalWeight : 0;
|
|
188
|
-
return {
|
|
252
|
+
return sanitizePersistenceValue({
|
|
189
253
|
score,
|
|
190
254
|
scorers: results,
|
|
191
255
|
tokenUsage,
|
|
256
|
+
}, collectToolEventSensitiveValues(ctx.toolEvents));
|
|
257
|
+
}
|
|
258
|
+
function createLiveDeterministicContext(ctx) {
|
|
259
|
+
return {
|
|
260
|
+
...ctx,
|
|
261
|
+
toolEvents: ctx.toolEvents.map((event) => {
|
|
262
|
+
const originalInput = event.action === 'mcp_tool_call'
|
|
263
|
+
? getOriginalMcpInput(event)
|
|
264
|
+
: undefined;
|
|
265
|
+
const mcp = normalizedMcpClassification(event);
|
|
266
|
+
return originalInput
|
|
267
|
+
? cloneToolEventWithRuntimeMetadata(event, { arguments: {
|
|
268
|
+
...event.arguments,
|
|
269
|
+
server: event.arguments?.server,
|
|
270
|
+
tool: event.arguments?.tool,
|
|
271
|
+
status: event.arguments?.status,
|
|
272
|
+
...structuredClone(originalInput),
|
|
273
|
+
}, ...(mcp ? { mcp } : {}) })
|
|
274
|
+
: event;
|
|
275
|
+
}),
|
|
192
276
|
};
|
|
193
277
|
}
|
|
278
|
+
function normalizedMcpClassification(event) {
|
|
279
|
+
if (event.mcp)
|
|
280
|
+
return event.mcp;
|
|
281
|
+
const args = event.arguments ?? {};
|
|
282
|
+
const separator = event.providerToolName.indexOf('.');
|
|
283
|
+
const serverName = typeof args.server === 'string' ? args.server
|
|
284
|
+
: separator > 0 ? event.providerToolName.slice(0, separator) : undefined;
|
|
285
|
+
const toolName = typeof args.tool === 'string' ? args.tool
|
|
286
|
+
: separator > 0 && separator < event.providerToolName.length - 1
|
|
287
|
+
? event.providerToolName.slice(separator + 1) : undefined;
|
|
288
|
+
const status = event.status ?? args.status;
|
|
289
|
+
if (!serverName || !toolName)
|
|
290
|
+
return undefined;
|
|
291
|
+
if (status === 'completed')
|
|
292
|
+
return { serverName, toolName, invocation: 'confirmed', outcome: 'completed' };
|
|
293
|
+
if (status === 'error' || status === 'failed') {
|
|
294
|
+
return { serverName, toolName, invocation: 'confirmed', outcome: 'tool_error' };
|
|
295
|
+
}
|
|
296
|
+
if (status === 'user_denied' || status === 'policy_denied') {
|
|
297
|
+
return { serverName, toolName, invocation: 'not_invoked', outcome: status };
|
|
298
|
+
}
|
|
299
|
+
if (status === 'protocol_error') {
|
|
300
|
+
return { serverName, toolName, invocation: 'unknown', outcome: 'protocol_error' };
|
|
301
|
+
}
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
194
304
|
function maybeThrowOnScorerErrors(result, mode) {
|
|
195
305
|
if (mode !== 'fail')
|
|
196
306
|
return;
|
package/dist/sdk/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type { McpMockApprovalRule, McpMockJsonValue } from './mcp-mock-approvals
|
|
|
9
9
|
export type { CompileScenarioResult, EvidenceEnvelope, JsonObject, JsonPrimitive, JsonValue, MatchExpr, MatchPredicate, McpContent, ScenarioArtifact, ScenarioCase, ScenarioDiagnostic, ScenarioEvidence, ScenarioMachineV2, ScenarioRejection, ScenarioSuccess, ScenarioToolError, } from './scenario-machine-v2.js';
|
|
10
10
|
export type { ExpectedScenarioCaseMatch, ScenarioCaseMatchEvidence, ScenarioStateTransitionEvidence, } from './scenario-evidence.js';
|
|
11
11
|
export { decideMcpToolCall, redactMcpSecrets, } from './mcp-safety.js';
|
|
12
|
-
export { evaluate, EvalScorerError } from './evaluate.js';
|
|
12
|
+
export { evaluate, evaluateFlow, EvalScorerError } from './evaluate.js';
|
|
13
13
|
export { RUN_SNAPSHOT_VERSION, buildRunSnapshot, loadRunSnapshot, SnapshotParseError, SnapshotVersionError, WorkspaceMissingError, } from './snapshots.js';
|
|
14
14
|
export { createPersona } from './persona.js';
|
|
15
15
|
export { createConversationWindow } from './conversation-window.js';
|
|
@@ -26,11 +26,13 @@ export { createAskBus, requireAskBusForLiveBatches, AskBusTimeoutError } from '.
|
|
|
26
26
|
export { toAskUserToolEvent } from './ask-bus/projection.js';
|
|
27
27
|
export type { AskUserToolEvent, AskUserToolEventArguments, AskUserToolEventQuestionArgument, } from './ask-bus/projection.js';
|
|
28
28
|
export { buildAskBatchLogEntries } from './agent-result-log.js';
|
|
29
|
-
export { emitEvalResult, resetAllResultObserversForTests, resetUserResultObservers, subscribeToEvalResults, } from './result-capture.js';
|
|
29
|
+
export { emitEvalResult, emitFlowEvalResult, resetAllResultObserversForTests, resetUserResultObservers, subscribeToEvalResults, subscribeToEvaluationResults, } from './result-capture.js';
|
|
30
30
|
export { getAgentCapabilities } from './types.js';
|
|
31
31
|
export type { AgentTransport, AgentCapabilities, AgentName, McpRunMode, McpSafetyOptions, McpToolPolicy, McpToolPolicyRule, } from './types.js';
|
|
32
32
|
export type { AskBus, AskBatch, AskQuestion, AskOption, AskAnswer, AskResolution, AskBatchSnapshot, AskAnswerSnapshot, AskResolutionSnapshot, AskHandle, AskHandler, AskSource, AskLifecycle, AskAnswerSource, Unsubscribe as AskBusUnsubscribe, } from './ask-bus/types.js';
|
|
33
33
|
export type { Agent, AgentOptions, DebugOptions, Message, Scorer, CheckScorer, ScoreScorer, JudgeScorer, ToolUsageScorer, ScorerContext, EvalResult, ScorerResultEntry, ScorerStatus, ChatSession, ConversationResult, ConverseOptions, UntilPredicate, UntilContext, Reaction, TextReaction, AskUserReaction, AskUserQuestion, AskUserOption, ReactionPreviewEntry, TextReactionPreviewEntry, AskUserReactionPreviewEntry, ReactionPreviewResult, ReactionPreviewTurn, StepScorer, Persona, PersonaConfig, ConversationWindowConfig, TurnDetail, ReactionFiredEntry, PathgradePluginOptions, PathgradeMeta, TurnTiming, TokenUsage, EvaluateOptions, ReactionPreviewStatus, ScoreResult, JudgeInput, CodeJudgeToolName, ToolExpectation, SessionArtifactMatchOptions, SessionArtifactContent, SessionArtifacts, RecordedEvalResult, PathgradeTestMeta, EvaluationResultKind, AgentExecutionMetadata, AgentExecutionTransport, AgentInteractionMode, } from './types.js';
|
|
34
|
+
export type { AgentFlowTrace, AgentFlowParticipant, AgentFlowInteraction, AgentFlowEvidenceAvailability, AgentFlowEvidenceCompleteness, AgentFlowSummary, } from './agent-flow.js';
|
|
35
|
+
export type { EvaluationResultEvent, EvaluationResultObserver } from './result-capture.js';
|
|
34
36
|
export type { ConversationWindow, ConversationWindowOptions } from './conversation-window.js';
|
|
35
37
|
export type { JudgePipelineOptions } from './judge-pipeline.js';
|
|
36
38
|
export type { RunScorerOptions } from './run-scorer.js';
|
package/dist/sdk/index.js
CHANGED
|
@@ -7,7 +7,7 @@ export { compileScenario, compileScenarioText } from './scenario-machine-v2.js';
|
|
|
7
7
|
export { findScenarioCaseMatches, getFinalScenarioState, getScenarioStateTimeline, ScenarioEvidenceError, wasScenarioCaseMatched, } from './scenario-evidence.js';
|
|
8
8
|
export { getMcpToolCall, isMcpToolCall, findMcpToolCalls, getMcpStartupStatus, isMcpStartupStatus, getMcpApproval, isMcpApproval, findMcpApprovals, getMcpInvocation, wasMcpToolInvoked, } from './mcp-evidence.js';
|
|
9
9
|
export { decideMcpToolCall, redactMcpSecrets, } from './mcp-safety.js';
|
|
10
|
-
export { evaluate, EvalScorerError } from './evaluate.js';
|
|
10
|
+
export { evaluate, evaluateFlow, EvalScorerError } from './evaluate.js';
|
|
11
11
|
export { RUN_SNAPSHOT_VERSION, buildRunSnapshot, loadRunSnapshot, SnapshotParseError, SnapshotVersionError, WorkspaceMissingError, } from './snapshots.js';
|
|
12
12
|
export { createPersona } from './persona.js';
|
|
13
13
|
export { createConversationWindow } from './conversation-window.js';
|
|
@@ -23,7 +23,7 @@ export { parsePathgradeReport } from '../reporting/report-parser.js';
|
|
|
23
23
|
export { createAskBus, requireAskBusForLiveBatches, AskBusTimeoutError } from './ask-bus/bus.js';
|
|
24
24
|
export { toAskUserToolEvent } from './ask-bus/projection.js';
|
|
25
25
|
export { buildAskBatchLogEntries } from './agent-result-log.js';
|
|
26
|
-
export { emitEvalResult, resetAllResultObserversForTests, resetUserResultObservers, subscribeToEvalResults, } from './result-capture.js';
|
|
26
|
+
export { emitEvalResult, emitFlowEvalResult, resetAllResultObserversForTests, resetUserResultObservers, subscribeToEvalResults, subscribeToEvaluationResults, } from './result-capture.js';
|
|
27
27
|
export { getAgentCapabilities } from './types.js';
|
|
28
28
|
export { COMPARISON_CONTRACT_VERSION, NORMALIZED_RUN_MODEL_VERSION, PATHGRADE_REPORT_VERSION, TASK_INVENTORY_VERSION, } from '../reporting/reliability-contract.js';
|
|
29
29
|
export { createAgentLLM, createLLMClient, ProviderNotSupportedError } from '../utils/llm.js';
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { sanitizePersistenceValue } from '../tool-event-results.js';
|
|
2
|
-
import {
|
|
2
|
+
import { collectToolEventSensitiveValues, } from './tool-event-secrets.js';
|
|
3
3
|
export function buildJudgePrompt(scorer, ctx, input) {
|
|
4
4
|
const sections = [];
|
|
5
|
-
|
|
5
|
+
const sensitiveValues = collectToolEventSensitiveValues(ctx.toolEvents);
|
|
6
|
+
sections.push(`## Session Transcript\n${sanitizePersistenceValue(ctx.transcript, sensitiveValues)}`);
|
|
6
7
|
if (scorer.includeToolEvents && ctx.toolEvents.length > 0) {
|
|
7
8
|
sections.push(`## Tool Events\n${formatToolEvents(ctx)}`);
|
|
8
9
|
}
|
|
9
10
|
if (input) {
|
|
10
|
-
|
|
11
|
+
const sanitizedInput = sanitizePersistenceValue(input, sensitiveValues);
|
|
12
|
+
for (const [key, value] of Object.entries(sanitizedInput)) {
|
|
11
13
|
const body = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
|
|
12
14
|
sections.push(`## ${key}\n${body}`);
|
|
13
15
|
}
|
|
@@ -22,10 +24,10 @@ ${scorer.rubric}
|
|
|
22
24
|
Respond with ONLY a JSON object: {"score": <number>, "details": "<brief explanation>"}`;
|
|
23
25
|
}
|
|
24
26
|
function formatToolEvents(ctx) {
|
|
27
|
+
const sensitiveValues = collectToolEventSensitiveValues(ctx.toolEvents);
|
|
25
28
|
return ctx.toolEvents
|
|
26
29
|
.map((event) => {
|
|
27
30
|
const turn = event.turnNumber ? `turn ${event.turnNumber}` : 'instruction';
|
|
28
|
-
const sensitiveValues = getToolEventSensitiveValues(event);
|
|
29
31
|
const details = [
|
|
30
32
|
event.status ? ` status: ${event.status}` : undefined,
|
|
31
33
|
event.mcp ? ` mcp: ${JSON.stringify(event.mcp)}` : undefined,
|
|
@@ -41,7 +43,8 @@ function formatToolEvents(ctx) {
|
|
|
41
43
|
}
|
|
42
44
|
export function buildBatchedJudgePrompt(judges, ctx, inputs) {
|
|
43
45
|
const sections = [];
|
|
44
|
-
|
|
46
|
+
const sensitiveValues = collectToolEventSensitiveValues(ctx.toolEvents);
|
|
47
|
+
sections.push(`## Session Transcript\n${sanitizePersistenceValue(ctx.transcript, sensitiveValues)}`);
|
|
45
48
|
if (judges.some((j) => j.includeToolEvents) && ctx.toolEvents.length > 0) {
|
|
46
49
|
sections.push(`## Tool Events\n${formatToolEvents(ctx)}`);
|
|
47
50
|
}
|
|
@@ -49,7 +52,8 @@ export function buildBatchedJudgePrompt(judges, ctx, inputs) {
|
|
|
49
52
|
const parts = [`### Rubric ${i + 1}: "${j.name}"\n${j.rubric}`];
|
|
50
53
|
const input = inputs[i];
|
|
51
54
|
if (input) {
|
|
52
|
-
|
|
55
|
+
const sanitizedInput = sanitizePersistenceValue(input, sensitiveValues);
|
|
56
|
+
for (const [key, value] of Object.entries(sanitizedInput)) {
|
|
53
57
|
const body = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
|
|
54
58
|
parts.push(`#### ${key}\n${body}`);
|
|
55
59
|
}
|
|
@@ -77,7 +81,7 @@ export function buildToolUseJudgePrompt(scorer, ctx) {
|
|
|
77
81
|
].join('\n');
|
|
78
82
|
const parts = [];
|
|
79
83
|
parts.push('## Session Transcript');
|
|
80
|
-
parts.push(ctx.transcript);
|
|
84
|
+
parts.push(sanitizePersistenceValue(ctx.transcript, collectToolEventSensitiveValues(ctx.toolEvents)));
|
|
81
85
|
if (scorer.includeToolEvents && ctx.toolEvents.length > 0) {
|
|
82
86
|
parts.push('## Tool Events');
|
|
83
87
|
parts.push(formatToolEvents(ctx));
|