@wix/pathgrade 1.0.25 → 1.0.27
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 +44 -2
- package/dist/agents/claude/sdk-message-projector.js +5 -0
- package/dist/agents/claude/sdk-options.d.ts +1 -2
- package/dist/agents/claude.js +2 -1
- package/dist/agents/codex-app-server/agent.js +89 -52
- package/dist/agents/codex-app-server/scenario-mount.d.ts +40 -0
- package/dist/agents/codex-app-server/scenario-mount.js +238 -0
- package/dist/agents/codex.js +1 -0
- package/dist/agents/cursor-scenario.d.ts +20 -0
- package/dist/agents/cursor-scenario.js +96 -0
- package/dist/agents/cursor.js +21 -0
- package/dist/agents/opencode/scenario.d.ts +25 -0
- package/dist/agents/opencode/scenario.js +195 -0
- package/dist/agents/opencode.js +54 -2
- package/dist/commands/report.d.ts +10 -2
- package/dist/commands/report.js +25 -4
- package/dist/internal/direct-mcp-v2/acp-author-projector.d.ts +20 -0
- package/dist/internal/direct-mcp-v2/acp-author-projector.js +147 -0
- package/dist/internal/direct-mcp-v2/acp-author-session.d.ts +39 -0
- package/dist/internal/direct-mcp-v2/acp-author-session.js +201 -0
- package/dist/internal/direct-mcp-v2/acp-client.d.ts +70 -0
- package/dist/internal/direct-mcp-v2/acp-client.js +310 -0
- package/dist/internal/direct-mcp-v2/acp-direct-mcp.d.ts +38 -0
- package/dist/internal/direct-mcp-v2/acp-direct-mcp.js +326 -0
- package/dist/internal/direct-mcp-v2/claude-direct-mcp.d.ts +68 -0
- package/dist/internal/direct-mcp-v2/claude-direct-mcp.js +456 -0
- package/dist/internal/direct-mcp-v2/claude-profile.d.ts +9 -0
- package/dist/internal/direct-mcp-v2/claude-profile.js +97 -0
- package/dist/internal/direct-mcp-v2/cleanup.d.ts +62 -0
- package/dist/internal/direct-mcp-v2/cleanup.js +182 -0
- package/dist/internal/direct-mcp-v2/codex-direct-mcp.d.ts +41 -0
- package/dist/internal/direct-mcp-v2/codex-direct-mcp.js +436 -0
- package/dist/internal/direct-mcp-v2/codex-profile.d.ts +12 -0
- package/dist/internal/direct-mcp-v2/codex-profile.js +81 -0
- package/dist/internal/direct-mcp-v2/compiler.d.ts +12 -0
- package/dist/internal/direct-mcp-v2/compiler.js +449 -0
- package/dist/internal/direct-mcp-v2/cursor-direct-mcp.d.ts +14 -0
- package/dist/internal/direct-mcp-v2/cursor-direct-mcp.js +60 -0
- package/dist/internal/direct-mcp-v2/cursor-profile.d.ts +38 -0
- package/dist/internal/direct-mcp-v2/cursor-profile.js +79 -0
- package/dist/internal/direct-mcp-v2/evidence.d.ts +50 -0
- package/dist/internal/direct-mcp-v2/evidence.js +147 -0
- package/dist/internal/direct-mcp-v2/host-reducer.d.ts +61 -0
- package/dist/internal/direct-mcp-v2/host-reducer.js +214 -0
- package/dist/internal/direct-mcp-v2/json.d.ts +9 -0
- package/dist/internal/direct-mcp-v2/json.js +220 -0
- package/dist/internal/direct-mcp-v2/materialize.d.ts +39 -0
- package/dist/internal/direct-mcp-v2/materialize.js +22 -0
- package/dist/internal/direct-mcp-v2/opencode-direct-mcp.d.ts +14 -0
- package/dist/internal/direct-mcp-v2/opencode-direct-mcp.js +71 -0
- package/dist/internal/direct-mcp-v2/opencode-profile.d.ts +8 -0
- package/dist/internal/direct-mcp-v2/opencode-profile.js +52 -0
- package/dist/internal/direct-mcp-v2/projection.d.ts +40 -0
- package/dist/internal/direct-mcp-v2/projection.js +46 -0
- package/dist/internal/direct-mcp-v2/public-scenario-runtime.d.ts +51 -0
- package/dist/internal/direct-mcp-v2/public-scenario-runtime.js +353 -0
- package/dist/internal/direct-mcp-v2/runtime-lock.d.ts +51 -0
- package/dist/internal/direct-mcp-v2/runtime-lock.js +203 -0
- package/dist/internal/direct-mcp-v2/runtime-profile.d.ts +28 -0
- package/dist/internal/direct-mcp-v2/runtime-profile.js +163 -0
- package/dist/internal/direct-mcp-v2/scenario-http-host.d.ts +24 -0
- package/dist/internal/direct-mcp-v2/scenario-http-host.js +254 -0
- package/dist/internal/direct-mcp-v2/types.d.ts +233 -0
- package/dist/internal/direct-mcp-v2/types.js +1 -0
- package/dist/pathgrade.js +22 -1
- package/dist/reporters/github-comment.d.ts +12 -3
- package/dist/reporters/github-comment.js +80 -9
- package/dist/reporting/artifacts.js +5 -2
- package/dist/sdk/agent.js +56 -4
- package/dist/sdk/evaluate.d.ts +2 -0
- package/dist/sdk/evaluate.js +20 -12
- package/dist/sdk/index.d.ts +6 -0
- package/dist/sdk/index.js +4 -0
- package/dist/sdk/lifecycle.js +8 -3
- package/dist/sdk/managed-session.d.ts +6 -0
- package/dist/sdk/managed-session.js +157 -50
- package/dist/sdk/scenario-evidence.d.ts +27 -0
- package/dist/sdk/scenario-evidence.js +94 -0
- package/dist/sdk/scenario-machine-v2.d.ts +9 -0
- package/dist/sdk/scenario-machine-v2.js +37 -0
- package/dist/sdk/snapshots.d.ts +3 -0
- package/dist/sdk/snapshots.js +2 -0
- package/dist/sdk/types.d.ts +11 -1
- package/dist/tool-event-results.d.ts +1 -1
- package/dist/tool-event-results.js +2 -1
- package/dist/tool-events.d.ts +5 -0
- package/dist/tool-events.js +5 -0
- package/dist/types.d.ts +8 -0
- package/package.json +2 -2
package/dist/pathgrade.js
CHANGED
|
@@ -156,10 +156,27 @@ export async function runPathgradeCli(options = {}) {
|
|
|
156
156
|
const reportArgs = args.slice(1);
|
|
157
157
|
const resultsPathFlag = reportArgs.find(a => a.startsWith('--results-path='));
|
|
158
158
|
const commentIdFlag = reportArgs.find(a => a.startsWith('--comment-id='));
|
|
159
|
+
const detailsUrlFlag = reportArgs.find(a => a.startsWith('--details-url='));
|
|
160
|
+
const noticeFlag = reportArgs.find(a => a.startsWith('--notice='));
|
|
161
|
+
const expectedHeadShaFlag = reportArgs.find(a => a.startsWith('--expected-head-sha='));
|
|
159
162
|
const noComment = reportArgs.includes('--no-comment');
|
|
163
|
+
const strict = reportArgs.includes('--strict');
|
|
160
164
|
const resultsPath = resultsPathFlag ? resultsPathFlag.split('=').slice(1).join('=') : undefined;
|
|
161
165
|
const commentId = commentIdFlag ? commentIdFlag.split('=').slice(1).join('=') : undefined;
|
|
162
|
-
|
|
166
|
+
const detailsUrl = detailsUrlFlag ? detailsUrlFlag.split('=').slice(1).join('=') : undefined;
|
|
167
|
+
const notice = noticeFlag ? noticeFlag.split('=').slice(1).join('=') : undefined;
|
|
168
|
+
const expectedHeadSha = expectedHeadShaFlag
|
|
169
|
+
? expectedHeadShaFlag.split('=').slice(1).join('=')
|
|
170
|
+
: undefined;
|
|
171
|
+
await runReport(process.cwd(), {
|
|
172
|
+
resultsPath,
|
|
173
|
+
commentId,
|
|
174
|
+
detailsUrl,
|
|
175
|
+
notice,
|
|
176
|
+
expectedHeadSha,
|
|
177
|
+
noComment,
|
|
178
|
+
strict,
|
|
179
|
+
});
|
|
163
180
|
return;
|
|
164
181
|
}
|
|
165
182
|
if (command === 'run' || !command || command.startsWith('-')) {
|
|
@@ -234,6 +251,10 @@ function printHelp(cliName) {
|
|
|
234
251
|
[--results-path=<path>] Override results.json location
|
|
235
252
|
[--no-comment] Print markdown to stdout; do not post
|
|
236
253
|
[--comment-id=<id>] Override comment marker (default: $GITHUB_WORKFLOW:$GITHUB_JOB)
|
|
254
|
+
[--details-url=<url>] Add a provider-neutral full-report link
|
|
255
|
+
[--notice=<text>] Add a provider-neutral report status notice
|
|
256
|
+
[--expected-head-sha=<sha>] Skip stale PR comment updates
|
|
257
|
+
[--strict] Fail on missing context, results, or GitHub errors
|
|
237
258
|
${cliName} affected Print eval files affected by a change-set (one per line)
|
|
238
259
|
[--since=<ref>] Diff <ref>...HEAD (overrides git auto-detection)
|
|
239
260
|
[--changed-files=<path>] Newline-delimited repo-relative file list
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
import type { PathgradeReport, PathgradeSelectionReport } from '../types.js';
|
|
11
11
|
export interface FormatOptions {
|
|
12
12
|
commentId: string;
|
|
13
|
+
/** Provider-neutral URL for a richer external report. */
|
|
14
|
+
detailsUrl?: string;
|
|
15
|
+
/** Provider-neutral status notice rendered above report details. */
|
|
16
|
+
notice?: string;
|
|
13
17
|
}
|
|
14
18
|
/** Minimal body posted when `.pathgrade/results.json` is missing. */
|
|
15
19
|
export declare const MISSING_RESULTS_BODY = "Pathgrade evals did not produce results. Check the workflow logs.";
|
|
@@ -55,14 +59,19 @@ export interface PostOptions {
|
|
|
55
59
|
commentId: string;
|
|
56
60
|
/** Body WITHOUT the marker — the marker is prepended if not already present. */
|
|
57
61
|
body: string;
|
|
62
|
+
/** Throw instead of swallowing GitHub API failures. */
|
|
63
|
+
strict?: boolean;
|
|
64
|
+
/** Skip the update if the pull request has advanced to another head. */
|
|
65
|
+
expectedHeadSha?: string;
|
|
58
66
|
}
|
|
67
|
+
export type PostResult = 'created' | 'updated' | 'stale';
|
|
59
68
|
/**
|
|
60
69
|
* Find an existing PR comment carrying `<!-- pathgrade:${commentId} -->`
|
|
61
|
-
* and update it; otherwise create a new one.
|
|
62
|
-
*
|
|
70
|
+
* and update it; otherwise create a new one. Default mode logs and swallows
|
|
71
|
+
* transport errors; strict mode rethrows them for an owning orchestrator.
|
|
63
72
|
*
|
|
64
73
|
* Bodies longer than GitHub's 65,536-character limit are truncated with a
|
|
65
74
|
* sentinel pointing to workflow artifacts, preserving the leading dedup
|
|
66
75
|
* marker so subsequent runs still find and update this comment.
|
|
67
76
|
*/
|
|
68
|
-
export declare function postOrUpdateComment(ctx: PrContext, opts: PostOptions): Promise<
|
|
77
|
+
export declare function postOrUpdateComment(ctx: PrContext, opts: PostOptions): Promise<PostResult | undefined>;
|
|
@@ -26,6 +26,12 @@ export function formatNoAffectedEvalsMarkdown(selection) {
|
|
|
26
26
|
return lines.join('\n');
|
|
27
27
|
}
|
|
28
28
|
export function commentMarker(commentId) {
|
|
29
|
+
if (commentId.length === 0
|
|
30
|
+
|| commentId.length > 128
|
|
31
|
+
|| /[<>\r\n]/.test(commentId)
|
|
32
|
+
|| commentId.includes('--')) {
|
|
33
|
+
throw new Error('Pathgrade comment id must be 1-128 characters and cannot contain HTML comment delimiters');
|
|
34
|
+
}
|
|
29
35
|
return `<!-- pathgrade:${commentId} -->`;
|
|
30
36
|
}
|
|
31
37
|
function pct(n) {
|
|
@@ -59,9 +65,19 @@ export function formatReportMarkdown(report, opts) {
|
|
|
59
65
|
lines.push('');
|
|
60
66
|
lines.push(`### ${icon} Pathgrade report`);
|
|
61
67
|
lines.push('');
|
|
68
|
+
lines.push(`**Last run:** \`${report.timestamp}\``);
|
|
69
|
+
lines.push('');
|
|
62
70
|
lines.push(`**Pass rate:** ${pct(p)} | ` +
|
|
63
71
|
`**pass@${totalTrials}:** ${pct(overallPassAtK)} | ` +
|
|
64
72
|
`**pass^${totalTrials}:** ${pct(overallPassPowK)}`);
|
|
73
|
+
if (opts.detailsUrl) {
|
|
74
|
+
lines.push('');
|
|
75
|
+
lines.push(`**Details:** [Open the full report](${formatLinkDestination(opts.detailsUrl)})`);
|
|
76
|
+
}
|
|
77
|
+
if (opts.notice) {
|
|
78
|
+
lines.push('');
|
|
79
|
+
lines.push(`> ${opts.notice.replace(/[\r\n]+/g, ' ').trim()}`);
|
|
80
|
+
}
|
|
65
81
|
if (report.threshold != null) {
|
|
66
82
|
lines.push('');
|
|
67
83
|
lines.push(`Threshold: ${pct(report.threshold)} — ${report.status.toUpperCase()}`);
|
|
@@ -100,14 +116,14 @@ export function formatSelectionSection(selection) {
|
|
|
100
116
|
out.push('### Selection');
|
|
101
117
|
const total = selection.selected.length + selection.skipped.length;
|
|
102
118
|
if (selection.global_match) {
|
|
103
|
-
out.push(`
|
|
119
|
+
out.push(`Selected **all ${total}** eval files — global trigger \`${selection.global_match}\` matched.`);
|
|
104
120
|
return out.join('\n');
|
|
105
121
|
}
|
|
106
122
|
if (selection.skipped.length === 0 && selection.selected.length > 0) {
|
|
107
|
-
out.push(`
|
|
123
|
+
out.push(`Selected **all ${total}** eval files — every eval had a matching change.`);
|
|
108
124
|
return out.join('\n');
|
|
109
125
|
}
|
|
110
|
-
out.push(`
|
|
126
|
+
out.push(`Selected **${selection.selected.length} of ${total}** eval files based on changes vs \`${selection.base_ref}\`.`);
|
|
111
127
|
if (selection.skipped.length > 0) {
|
|
112
128
|
out.push('');
|
|
113
129
|
out.push(`<details><summary>${selection.skipped.length} skipped (unaffected)</summary>`);
|
|
@@ -171,13 +187,36 @@ function apiHeaders(token) {
|
|
|
171
187
|
};
|
|
172
188
|
}
|
|
173
189
|
async function listPrComments(ctx) {
|
|
174
|
-
const
|
|
190
|
+
const comments = [];
|
|
191
|
+
for (let page = 1; page <= 100; page += 1) {
|
|
192
|
+
const url = `https://api.github.com/repos/${ctx.owner}/${ctx.repo}/issues/${ctx.prNumber}/comments?per_page=100&page=${page}`;
|
|
193
|
+
const res = await fetch(url, { headers: apiHeaders(ctx.token) });
|
|
194
|
+
if (!res.ok) {
|
|
195
|
+
throw new Error(`GET ${url} returned ${res.status}`);
|
|
196
|
+
}
|
|
197
|
+
const data = await res.json();
|
|
198
|
+
if (!Array.isArray(data)) {
|
|
199
|
+
throw new Error(`GET ${url} returned a non-array response`);
|
|
200
|
+
}
|
|
201
|
+
for (const value of data) {
|
|
202
|
+
if (isGithubComment(value))
|
|
203
|
+
comments.push(value);
|
|
204
|
+
}
|
|
205
|
+
if (data.length < 100)
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
return comments;
|
|
209
|
+
}
|
|
210
|
+
async function currentPrHeadSha(ctx) {
|
|
211
|
+
const url = `https://api.github.com/repos/${ctx.owner}/${ctx.repo}/pulls/${ctx.prNumber}`;
|
|
175
212
|
const res = await fetch(url, { headers: apiHeaders(ctx.token) });
|
|
176
|
-
if (!res.ok)
|
|
213
|
+
if (!res.ok)
|
|
177
214
|
throw new Error(`GET ${url} returned ${res.status}`);
|
|
215
|
+
const data = await res.json();
|
|
216
|
+
if (!isRecord(data) || !isRecord(data.head) || typeof data.head.sha !== 'string') {
|
|
217
|
+
throw new Error(`GET ${url} returned no pull-request head SHA`);
|
|
178
218
|
}
|
|
179
|
-
|
|
180
|
-
return Array.isArray(data) ? data : [];
|
|
219
|
+
return data.head.sha;
|
|
181
220
|
}
|
|
182
221
|
async function createPrComment(ctx, body) {
|
|
183
222
|
const url = `https://api.github.com/repos/${ctx.owner}/${ctx.repo}/issues/${ctx.prNumber}/comments`;
|
|
@@ -206,8 +245,8 @@ const GITHUB_COMMENT_MAX = 65536;
|
|
|
206
245
|
const TRUNCATION_SENTINEL = '\n\n_…output truncated to fit GitHub\'s comment size limit — see the workflow artifacts for the full report._';
|
|
207
246
|
/**
|
|
208
247
|
* Find an existing PR comment carrying `<!-- pathgrade:${commentId} -->`
|
|
209
|
-
* and update it; otherwise create a new one.
|
|
210
|
-
*
|
|
248
|
+
* and update it; otherwise create a new one. Default mode logs and swallows
|
|
249
|
+
* transport errors; strict mode rethrows them for an owning orchestrator.
|
|
211
250
|
*
|
|
212
251
|
* Bodies longer than GitHub's 65,536-character limit are truncated with a
|
|
213
252
|
* sentinel pointing to workflow artifacts, preserving the leading dedup
|
|
@@ -221,19 +260,51 @@ export async function postOrUpdateComment(ctx, opts) {
|
|
|
221
260
|
: withMarker.slice(0, GITHUB_COMMENT_MAX - TRUNCATION_SENTINEL.length) +
|
|
222
261
|
TRUNCATION_SENTINEL;
|
|
223
262
|
try {
|
|
263
|
+
if (opts.expectedHeadSha) {
|
|
264
|
+
const currentHead = await currentPrHeadSha(ctx);
|
|
265
|
+
if (currentHead !== opts.expectedHeadSha)
|
|
266
|
+
return 'stale';
|
|
267
|
+
}
|
|
224
268
|
const existing = await listPrComments(ctx);
|
|
225
269
|
const match = existing.find((c) => typeof c.body === 'string' && c.body.includes(marker));
|
|
226
270
|
if (match) {
|
|
227
271
|
await updatePrComment(ctx, match.id, body);
|
|
272
|
+
return 'updated';
|
|
228
273
|
}
|
|
229
274
|
else {
|
|
230
275
|
await createPrComment(ctx, body);
|
|
276
|
+
return 'created';
|
|
231
277
|
}
|
|
232
278
|
}
|
|
233
279
|
catch (err) {
|
|
234
280
|
const message = err instanceof Error ? err.message : String(err);
|
|
235
281
|
console.error(`pathgrade report: failed to post PR comment — ${message}`);
|
|
282
|
+
if (opts.strict)
|
|
283
|
+
throw err;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function formatLinkDestination(value) {
|
|
287
|
+
let url;
|
|
288
|
+
try {
|
|
289
|
+
url = new URL(value);
|
|
236
290
|
}
|
|
291
|
+
catch {
|
|
292
|
+
throw new Error('Pathgrade details URL must be an absolute HTTP(S) URL');
|
|
293
|
+
}
|
|
294
|
+
if (url.protocol !== 'https:' && url.protocol !== 'http:') {
|
|
295
|
+
throw new Error('Pathgrade details URL must be an absolute HTTP(S) URL');
|
|
296
|
+
}
|
|
297
|
+
return url.toString().replace(/\)/g, '%29');
|
|
298
|
+
}
|
|
299
|
+
function isRecord(value) {
|
|
300
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
301
|
+
}
|
|
302
|
+
function isGithubComment(value) {
|
|
303
|
+
return isRecord(value)
|
|
304
|
+
&& typeof value.id === 'number'
|
|
305
|
+
&& Number.isSafeInteger(value.id)
|
|
306
|
+
&& value.id > 0
|
|
307
|
+
&& typeof value.body === 'string';
|
|
237
308
|
}
|
|
238
309
|
function formatGroupDetails(group) {
|
|
239
310
|
const out = [];
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
|
+
import { collectSensitiveEnvValues, sanitizePersistenceValue, } from '../tool-event-results.js';
|
|
3
4
|
export async function writePathgradeArtifacts(artifactRoot, built) {
|
|
4
5
|
await fs.ensureDir(path.join(artifactRoot, 'traces'));
|
|
6
|
+
const sensitiveValues = collectSensitiveEnvValues(process.env);
|
|
7
|
+
const report = sanitizePersistenceValue(built.report, sensitiveValues);
|
|
5
8
|
const gitignorePath = path.join(artifactRoot, '.gitignore');
|
|
6
9
|
if (!(await fs.pathExists(gitignorePath))) {
|
|
7
10
|
await fs.writeFile(gitignorePath, '*\n');
|
|
8
11
|
}
|
|
9
12
|
const traceFiles = [];
|
|
10
13
|
for (const trace of built.traces) {
|
|
11
|
-
await fs.writeJson(path.join(artifactRoot, trace.traceFile), trace.trials, { spaces: 2 });
|
|
14
|
+
await fs.writeJson(path.join(artifactRoot, trace.traceFile), sanitizePersistenceValue(trace.trials, sensitiveValues), { spaces: 2 });
|
|
12
15
|
traceFiles.push(trace.traceFile);
|
|
13
16
|
}
|
|
14
17
|
const resultsPath = path.join(artifactRoot, 'results.json');
|
|
15
|
-
await fs.writeJson(resultsPath,
|
|
18
|
+
await fs.writeJson(resultsPath, report, { spaces: 2 });
|
|
16
19
|
return {
|
|
17
20
|
resultsPath,
|
|
18
21
|
traceFiles,
|
package/dist/sdk/agent.js
CHANGED
|
@@ -4,7 +4,7 @@ import { lifecycleCore } from './lifecycle.js';
|
|
|
4
4
|
import { ChatSessionImpl } from './chat.js';
|
|
5
5
|
import { runConversation } from './converse.js';
|
|
6
6
|
import { createPersona } from './persona.js';
|
|
7
|
-
import {
|
|
7
|
+
import { evaluateStepScorers } from './evaluate.js';
|
|
8
8
|
import { createManagedSession } from './managed-session.js';
|
|
9
9
|
import { createAgentLLM } from '../utils/llm.js';
|
|
10
10
|
import { buildRunSnapshot } from './snapshots.js';
|
|
@@ -19,6 +19,10 @@ import { cleanDebugRuns, DEFAULT_DEBUG_RETAIN_RUNS, prepareManagedDebugRun, } fr
|
|
|
19
19
|
import { collectOpenCodeMcpToolNames, validateOpenCodeDeclaration } from '../agents/opencode/contract.js';
|
|
20
20
|
import { collectSensitiveEnvValues } from '../tool-event-results.js';
|
|
21
21
|
import { compileMcpMockApprovalSession, } from './mcp-mock-approvals.js';
|
|
22
|
+
import { compileScenario } from './scenario-machine-v2.js';
|
|
23
|
+
import { startPublicCodexScenarioRuntime } from '../agents/codex-app-server/scenario-mount.js';
|
|
24
|
+
import { createPublicOpenCodeScenarioRuntimeFactory } from '../agents/opencode/scenario.js';
|
|
25
|
+
import { createPublicCursorScenarioRuntimeFactory } from '../agents/cursor-scenario.js';
|
|
22
26
|
import { createAskUserHandler } from './ask-bus/handler.js';
|
|
23
27
|
/**
|
|
24
28
|
* Test-only injection point: override the sink used by the next emitter
|
|
@@ -35,6 +39,7 @@ class AgentImpl {
|
|
|
35
39
|
llm;
|
|
36
40
|
timeoutSetting;
|
|
37
41
|
modelOpt;
|
|
42
|
+
resolvedModel;
|
|
38
43
|
conversationWindowOpt;
|
|
39
44
|
interactionMode = null;
|
|
40
45
|
_messages = [];
|
|
@@ -52,6 +57,9 @@ class AgentImpl {
|
|
|
52
57
|
activeChatSession;
|
|
53
58
|
scriptedMcp;
|
|
54
59
|
sensitiveValues;
|
|
60
|
+
scenarioArtifact;
|
|
61
|
+
scenarioRuntimeFactory;
|
|
62
|
+
_scenarioEvidence = [];
|
|
55
63
|
constructor(opts) {
|
|
56
64
|
this.ws = opts.workspace;
|
|
57
65
|
this.agentName = opts.agentName;
|
|
@@ -69,6 +77,8 @@ class AgentImpl {
|
|
|
69
77
|
this.opencodeMcpToolNames = opts.opencodeMcpToolNames;
|
|
70
78
|
this.scriptedMcp = opts.scriptedMcp;
|
|
71
79
|
this.sensitiveValues = opts.sensitiveValues;
|
|
80
|
+
this.scenarioArtifact = opts.scenarioArtifact;
|
|
81
|
+
this.scenarioRuntimeFactory = opts.scenarioRuntimeFactory;
|
|
72
82
|
}
|
|
73
83
|
get messages() {
|
|
74
84
|
return this._messages;
|
|
@@ -79,6 +89,9 @@ class AgentImpl {
|
|
|
79
89
|
get workspace() {
|
|
80
90
|
return this.ws.path;
|
|
81
91
|
}
|
|
92
|
+
get scenarioEvidence() {
|
|
93
|
+
return structuredClone(this._scenarioEvidence);
|
|
94
|
+
}
|
|
82
95
|
get executionMetadata() {
|
|
83
96
|
const interactionMode = this.interactionMode === 'startChat'
|
|
84
97
|
? 'start_chat'
|
|
@@ -88,7 +101,10 @@ class AgentImpl {
|
|
|
88
101
|
return {
|
|
89
102
|
name: this.agentName,
|
|
90
103
|
...(this.modelOpt ? { requestedModel: this.modelOpt } : {}),
|
|
91
|
-
|
|
104
|
+
...(this.resolvedModel ? { resolvedModel: this.resolvedModel } : {}),
|
|
105
|
+
transport: this.scenarioArtifact && (this.agentName === 'opencode' || this.agentName === 'cursor')
|
|
106
|
+
? 'acp'
|
|
107
|
+
: resolveExecutionTransport(this.agentName, this.transport),
|
|
92
108
|
...(interactionMode ? { interactionMode } : {}),
|
|
93
109
|
};
|
|
94
110
|
}
|
|
@@ -114,6 +130,9 @@ class AgentImpl {
|
|
|
114
130
|
...(this.opencodeExecutable !== undefined ? { opencodeExecutable: this.opencodeExecutable } : {}),
|
|
115
131
|
...(this.opencodeMcpToolNames !== undefined ? { opencodeMcpToolNames: this.opencodeMcpToolNames } : {}),
|
|
116
132
|
...(this.scriptedMcp !== undefined ? { scriptedMcp: this.scriptedMcp } : {}),
|
|
133
|
+
...(this.scenarioArtifact !== undefined ? { scenarioArtifact: this.scenarioArtifact } : {}),
|
|
134
|
+
...(this.scenarioRuntimeFactory !== undefined ? { scenarioRuntimeFactory: this.scenarioRuntimeFactory } : {}),
|
|
135
|
+
onScenarioEvidence: (evidence) => { this._scenarioEvidence = structuredClone(evidence); },
|
|
117
136
|
sensitiveValues: this.sensitiveValues,
|
|
118
137
|
});
|
|
119
138
|
}
|
|
@@ -128,6 +147,9 @@ class AgentImpl {
|
|
|
128
147
|
return Math.ceil((turns * 80_000 + 200_000) / 1000);
|
|
129
148
|
}
|
|
130
149
|
accumulateTurnUsage(turnResult) {
|
|
150
|
+
if (turnResult.resolvedModel) {
|
|
151
|
+
this.resolvedModel = turnResult.resolvedModel;
|
|
152
|
+
}
|
|
131
153
|
if (turnResult.inputTokens || turnResult.outputTokens) {
|
|
132
154
|
this.llm.addTokens?.(turnResult.inputTokens ?? 0, turnResult.outputTokens ?? 0);
|
|
133
155
|
}
|
|
@@ -286,7 +308,7 @@ class AgentImpl {
|
|
|
286
308
|
// scorer judge calls accumulate on the same tracker.
|
|
287
309
|
const agent = this;
|
|
288
310
|
const runStepScorers = async (scorers) => {
|
|
289
|
-
const result = await
|
|
311
|
+
const result = await evaluateStepScorers(agent, scorers, { llm: this.llm });
|
|
290
312
|
const score = result.score;
|
|
291
313
|
if (score === undefined) {
|
|
292
314
|
throw new Error('Step scorer evaluation did not produce a score');
|
|
@@ -362,6 +384,7 @@ class AgentImpl {
|
|
|
362
384
|
conversationResult: this.lastConversationResult,
|
|
363
385
|
workspace: dest,
|
|
364
386
|
sensitiveValues: this.sensitiveValues,
|
|
387
|
+
scenarioEvidence: this._scenarioEvidence,
|
|
365
388
|
});
|
|
366
389
|
await fs.writeJSON(path.join(dest, 'run-snapshot.json'), snapshot, { spaces: 2 });
|
|
367
390
|
}
|
|
@@ -409,10 +432,37 @@ export async function createAgent(opts) {
|
|
|
409
432
|
}
|
|
410
433
|
}
|
|
411
434
|
const agentName = resolveAgentName(opts, process.env);
|
|
435
|
+
let scenarioArtifact;
|
|
436
|
+
if (opts.mcpScenario !== undefined) {
|
|
437
|
+
if (opts.agent === undefined)
|
|
438
|
+
throw new Error('mcpScenario requires an explicit agent');
|
|
439
|
+
if (opts.mcpMock !== undefined || opts.mcpMockApprovalRules !== undefined || opts.mcpConfigFile !== undefined || opts.mcpSafety !== undefined) {
|
|
440
|
+
throw new Error('mcpScenario cannot be combined with legacy or live MCP options');
|
|
441
|
+
}
|
|
442
|
+
const compiled = compileScenario(opts.mcpScenario);
|
|
443
|
+
if (!compiled.ok)
|
|
444
|
+
throw new Error(`Scenario compilation failed: ${JSON.stringify(compiled.diagnostics)}`);
|
|
445
|
+
scenarioArtifact = compiled.artifact;
|
|
446
|
+
}
|
|
412
447
|
validateOpenCodeDeclaration(agentName, opts);
|
|
413
448
|
const transport = agentName === 'codex'
|
|
414
449
|
? resolveCodexTransport(opts, process.env)
|
|
415
450
|
: undefined;
|
|
451
|
+
if (scenarioArtifact && agentName === 'codex' && transport !== 'app-server') {
|
|
452
|
+
throw new Error('mcpScenario requires Codex transport app-server; Codex exec is unsupported');
|
|
453
|
+
}
|
|
454
|
+
const scenarioRuntimeFactory = !scenarioArtifact
|
|
455
|
+
? undefined
|
|
456
|
+
: agentName === 'codex'
|
|
457
|
+
? startPublicCodexScenarioRuntime
|
|
458
|
+
: agentName === 'opencode'
|
|
459
|
+
? createPublicOpenCodeScenarioRuntimeFactory({
|
|
460
|
+
opencodeExecutable: opts.opencodeExecutable,
|
|
461
|
+
requestedModel: opts.model,
|
|
462
|
+
})
|
|
463
|
+
: agentName === 'cursor'
|
|
464
|
+
? createPublicCursorScenarioRuntimeFactory({ requestedModel: opts.model })
|
|
465
|
+
: undefined;
|
|
416
466
|
let scriptedMcp;
|
|
417
467
|
if (opts.mcpMockApprovalRules !== undefined) {
|
|
418
468
|
if (opts.mcpMock === undefined)
|
|
@@ -434,7 +484,7 @@ export async function createAgent(opts) {
|
|
|
434
484
|
const timeoutSetting = opts.timeout ?? 300;
|
|
435
485
|
// Capture runner context now; adapters own installation and restoration.
|
|
436
486
|
const testCtx = opts.debug ? resolveCaseDebugContext() : { name: '', dir: '' };
|
|
437
|
-
const { timeout: _, mcpMock, mcpConfigFile, mcpMockApprovalRules: ________, agent: __, debug: ___, transport: _____, mcpSafety: ______, opencodeExecutable, ...rest } = opts;
|
|
487
|
+
const { timeout: _, mcpMock, mcpScenario: _________, mcpConfigFile, mcpMockApprovalRules: ________, agent: __, debug: ___, transport: _____, mcpSafety: ______, opencodeExecutable, ...rest } = opts;
|
|
438
488
|
const workspace = await prepareWorkspace({
|
|
439
489
|
...rest,
|
|
440
490
|
agent: agentName,
|
|
@@ -471,6 +521,8 @@ export async function createAgent(opts) {
|
|
|
471
521
|
opencodeExecutable,
|
|
472
522
|
opencodeMcpToolNames: agentName === 'opencode' ? collectOpenCodeMcpToolNames(mcpMock) : undefined,
|
|
473
523
|
scriptedMcp,
|
|
524
|
+
scenarioArtifact,
|
|
525
|
+
scenarioRuntimeFactory,
|
|
474
526
|
sensitiveValues: workspace.sensitiveValues ?? collectSensitiveEnvValues(workspace.env),
|
|
475
527
|
});
|
|
476
528
|
lifecycleCore.registerAgent(agent);
|
package/dist/sdk/evaluate.d.ts
CHANGED
|
@@ -9,5 +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
|
+
/** Internal runConversation hook: step scorers must not consume final-run attribution. */
|
|
13
|
+
export declare function evaluateStepScorers(agent: Agent, scorers: Scorer[], opts?: EvaluateOptions): Promise<RecordedEvalResult>;
|
|
12
14
|
export declare const evaluate: EvaluateFn;
|
|
13
15
|
export {};
|
package/dist/sdk/evaluate.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { extractSkillsFromLog, extractToolEventsFromLog } from '../tool-events.js';
|
|
1
|
+
import { countShellCommandsFromLog, extractSkillsFromLog, extractToolEventsFromLog } from '../tool-events.js';
|
|
2
2
|
import { getRuntime } from './eval-runtime.js';
|
|
3
3
|
import { emitEvalResult } from './result-capture.js';
|
|
4
4
|
import { runJudgePipeline } from './judge-pipeline.js';
|
|
@@ -33,7 +33,7 @@ export class EvalScorerError extends Error {
|
|
|
33
33
|
// Local per-call: no module-level state, no cleanup export needed.
|
|
34
34
|
function makeEvaluateAgent() {
|
|
35
35
|
const conversationAttributed = new WeakSet();
|
|
36
|
-
return async function evaluateAgent(agent, scorers, opts) {
|
|
36
|
+
return async function evaluateAgent(agent, scorers, opts, attributeConversation = true) {
|
|
37
37
|
const toolEvents = extractToolEventsFromLog(agent.log);
|
|
38
38
|
const ctx = {
|
|
39
39
|
workspace: agent.workspace,
|
|
@@ -42,15 +42,16 @@ function makeEvaluateAgent() {
|
|
|
42
42
|
toolEvents,
|
|
43
43
|
runCommand: (cmd) => agent.exec(cmd),
|
|
44
44
|
artifacts: createSessionArtifacts(agent.workspace, toolEvents),
|
|
45
|
+
scenarioEvidence: agent.scenarioEvidence,
|
|
45
46
|
};
|
|
46
47
|
const trackedLLM = opts?.llm ?? agent.llm;
|
|
47
48
|
// Snapshot conversation tokens BEFORE running scorers, for first-eval attribution.
|
|
48
|
-
const before =
|
|
49
|
+
const before = agent.llm.tokenUsage ?? { inputTokens: 0, outputTokens: 0 };
|
|
49
50
|
// Snapshot conversation cost too. AgentImpl's `sendTurn` accumulates
|
|
50
51
|
// per-turn `costUsd` onto `trackedLLM` via `addCost`, so by the time
|
|
51
52
|
// `evaluate()` runs the pre-evaluate cost is the conversation's
|
|
52
53
|
// accumulated agent-turn cost.
|
|
53
|
-
const beforeCostUsd =
|
|
54
|
+
const beforeCostUsd = agent.llm.costUsd ?? 0;
|
|
54
55
|
// measure() returns the delta consumed by this evaluate call.
|
|
55
56
|
const scoringStartedAt = performance.now();
|
|
56
57
|
const { result: evalResult, tokens: deltaTokenUsage } = trackedLLM.measure
|
|
@@ -59,12 +60,13 @@ function makeEvaluateAgent() {
|
|
|
59
60
|
const r = await evaluateWithContext(ctx, scorers, { ...opts, llm: trackedLLM });
|
|
60
61
|
return { result: r, tokens: r.tokenUsage ?? { inputTokens: 0, outputTokens: 0 } };
|
|
61
62
|
})();
|
|
62
|
-
// Attribute conversation tokens on the first evaluate() for this agent.
|
|
63
|
-
const isFirstEval = !conversationAttributed.has(agent);
|
|
63
|
+
// Attribute conversation tokens on the first public evaluate() for this agent.
|
|
64
|
+
const isFirstEval = attributeConversation && !conversationAttributed.has(agent);
|
|
64
65
|
const conversationTokens = isFirstEval && (before.inputTokens > 0 || before.outputTokens > 0)
|
|
65
66
|
? { conversation_input_tokens: before.inputTokens, conversation_output_tokens: before.outputTokens }
|
|
66
67
|
: undefined;
|
|
67
|
-
//
|
|
68
|
+
// Agent usage is independent of the optional scorer LLM. Same
|
|
69
|
+
// first-eval attribution rule for cost; omit it when unavailable.
|
|
68
70
|
// no conversation cost was captured (Codex / Cursor today).
|
|
69
71
|
const conversationCost = isFirstEval && beforeCostUsd > 0
|
|
70
72
|
? { conversation_cost_usd: beforeCostUsd }
|
|
@@ -80,7 +82,7 @@ function makeEvaluateAgent() {
|
|
|
80
82
|
...(opts?.evaluationDefinitionKey
|
|
81
83
|
? { evaluationDefinitionKey: opts.evaluationDefinitionKey }
|
|
82
84
|
: {}),
|
|
83
|
-
trial: buildTrialResult(agent.log, { ...evalResult, tokenUsage: deltaTokenUsage }, conversationTokens, conversationCost),
|
|
85
|
+
trial: buildTrialResult(agent.log, { ...evalResult, tokenUsage: deltaTokenUsage }, agent.scenarioEvidence, conversationTokens, conversationCost),
|
|
84
86
|
};
|
|
85
87
|
emitEvalResult({ result: recordedResult, agent });
|
|
86
88
|
maybeThrowOnScorerErrors(recordedResult, opts?.onScorerError ?? 'skip');
|
|
@@ -88,6 +90,10 @@ function makeEvaluateAgent() {
|
|
|
88
90
|
};
|
|
89
91
|
}
|
|
90
92
|
const evaluateAgent = makeEvaluateAgent();
|
|
93
|
+
/** Internal runConversation hook: step scorers must not consume final-run attribution. */
|
|
94
|
+
export function evaluateStepScorers(agent, scorers, opts) {
|
|
95
|
+
return evaluateAgent(agent, scorers, opts, false);
|
|
96
|
+
}
|
|
91
97
|
async function fromSnapshot(snapshotPath, scorers, opts) {
|
|
92
98
|
const snapshot = await loadRunSnapshot(snapshotPath);
|
|
93
99
|
const trackedLLM = opts?.llm ?? createLLMClient({ adapters: [{
|
|
@@ -110,6 +116,7 @@ async function fromSnapshot(snapshotPath, scorers, opts) {
|
|
|
110
116
|
return sandboxExec(cmd, { cwd: workspace, env: getProcessEnv() });
|
|
111
117
|
},
|
|
112
118
|
artifacts: createSessionArtifacts(snapshot.workspace ?? '', snapshot.toolEvents),
|
|
119
|
+
scenarioEvidence: snapshot.scenarioEvidence ?? [],
|
|
113
120
|
};
|
|
114
121
|
const scoringStartedAt = performance.now();
|
|
115
122
|
const evalResult = await evaluateWithContext(ctx, scorers, { ...opts, llm: trackedLLM });
|
|
@@ -121,7 +128,7 @@ async function fromSnapshot(snapshotPath, scorers, opts) {
|
|
|
121
128
|
...(opts?.evaluationDefinitionKey
|
|
122
129
|
? { evaluationDefinitionKey: opts.evaluationDefinitionKey }
|
|
123
130
|
: {}),
|
|
124
|
-
trial: buildTrialResult(snapshot.log, evalResult),
|
|
131
|
+
trial: buildTrialResult(snapshot.log, evalResult, snapshot.scenarioEvidence),
|
|
125
132
|
};
|
|
126
133
|
maybeThrowOnScorerErrors(recordedResult, opts?.onScorerError ?? 'skip');
|
|
127
134
|
return recordedResult;
|
|
@@ -265,8 +272,8 @@ function matchesArtifactPattern(artifactPath, pattern) {
|
|
|
265
272
|
return artifactPath.includes(pattern);
|
|
266
273
|
return pattern.test(artifactPath);
|
|
267
274
|
}
|
|
268
|
-
function buildTrialResult(log, result, conversationTokens, conversationCost) {
|
|
269
|
-
const nCommands = log
|
|
275
|
+
function buildTrialResult(log, result, scenarioEvidence, conversationTokens, conversationCost) {
|
|
276
|
+
const nCommands = countShellCommandsFromLog(log);
|
|
270
277
|
const skills = extractSkillsFromLog(log);
|
|
271
278
|
return {
|
|
272
279
|
trial_id: 0,
|
|
@@ -285,6 +292,7 @@ function buildTrialResult(log, result, conversationTokens, conversationCost) {
|
|
|
285
292
|
// field.
|
|
286
293
|
...conversationCost,
|
|
287
294
|
session_log: [...log],
|
|
295
|
+
...(scenarioEvidence && scenarioEvidence.length > 0 ? { scenario_evidence: structuredClone(scenarioEvidence) } : {}),
|
|
288
296
|
...(skills.length > 0 ? { skills_used: skills } : {}),
|
|
289
297
|
};
|
|
290
298
|
}
|
|
@@ -297,7 +305,7 @@ function getProcessEnv() {
|
|
|
297
305
|
}
|
|
298
306
|
return env;
|
|
299
307
|
}
|
|
300
|
-
export const evaluate = Object.assign(evaluateAgent, {
|
|
308
|
+
export const evaluate = Object.assign((agent, scorers, opts) => evaluateAgent(agent, scorers, opts), {
|
|
301
309
|
fromSnapshot,
|
|
302
310
|
});
|
|
303
311
|
function toTrialScorerResult(result) {
|
package/dist/sdk/index.d.ts
CHANGED
|
@@ -2,8 +2,12 @@ export { createAgent } from './agent.js';
|
|
|
2
2
|
export { resolveAgentName, resolveCodexTransport, InvalidTransportEnvError, } from './agent-resolution.js';
|
|
3
3
|
export { AgentCrashError } from './agent-crash.js';
|
|
4
4
|
export { check, score, judge, toolUsage } from './scorers.js';
|
|
5
|
+
export { compileScenario, compileScenarioText } from './scenario-machine-v2.js';
|
|
6
|
+
export { findScenarioCaseMatches, getFinalScenarioState, getScenarioStateTimeline, ScenarioEvidenceError, wasScenarioCaseMatched, } from './scenario-evidence.js';
|
|
5
7
|
export { getMcpToolCall, isMcpToolCall, findMcpToolCalls, getMcpStartupStatus, isMcpStartupStatus, getMcpApproval, isMcpApproval, findMcpApprovals, getMcpInvocation, wasMcpToolInvoked, } from './mcp-evidence.js';
|
|
6
8
|
export type { McpMockApprovalRule, McpMockJsonValue } from './mcp-mock-approvals.js';
|
|
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
|
+
export type { ExpectedScenarioCaseMatch, ScenarioCaseMatchEvidence, ScenarioStateTransitionEvidence, } from './scenario-evidence.js';
|
|
7
11
|
export { decideMcpToolCall, redactMcpSecrets, } from './mcp-safety.js';
|
|
8
12
|
export { evaluate, EvalScorerError } from './evaluate.js';
|
|
9
13
|
export { RUN_SNAPSHOT_VERSION, buildRunSnapshot, loadRunSnapshot, SnapshotParseError, SnapshotVersionError, WorkspaceMissingError, } from './snapshots.js';
|
|
@@ -15,6 +19,8 @@ export { previewReactions } from './reaction-preview.js';
|
|
|
15
19
|
export { setRuntime, resetRuntime } from './eval-runtime.js';
|
|
16
20
|
export { DEFAULT_COPY_IGNORE } from '../providers/copy-filter.js';
|
|
17
21
|
export { extractToolEventsFromLog } from '../tool-events.js';
|
|
22
|
+
export { collectSensitiveEnvValues, sanitizePersistenceValue, } from '../tool-event-results.js';
|
|
23
|
+
export { parseEnvFile } from '../utils/env.js';
|
|
18
24
|
export { createAskBus, requireAskBusForLiveBatches, AskBusTimeoutError } from './ask-bus/bus.js';
|
|
19
25
|
export { toAskUserToolEvent } from './ask-bus/projection.js';
|
|
20
26
|
export type { AskUserToolEvent, AskUserToolEventArguments, AskUserToolEventQuestionArgument, } from './ask-bus/projection.js';
|
package/dist/sdk/index.js
CHANGED
|
@@ -3,6 +3,8 @@ export { createAgent } from './agent.js';
|
|
|
3
3
|
export { resolveAgentName, resolveCodexTransport, InvalidTransportEnvError, } from './agent-resolution.js';
|
|
4
4
|
export { AgentCrashError } from './agent-crash.js';
|
|
5
5
|
export { check, score, judge, toolUsage } from './scorers.js';
|
|
6
|
+
export { compileScenario, compileScenarioText } from './scenario-machine-v2.js';
|
|
7
|
+
export { findScenarioCaseMatches, getFinalScenarioState, getScenarioStateTimeline, ScenarioEvidenceError, wasScenarioCaseMatched, } from './scenario-evidence.js';
|
|
6
8
|
export { getMcpToolCall, isMcpToolCall, findMcpToolCalls, getMcpStartupStatus, isMcpStartupStatus, getMcpApproval, isMcpApproval, findMcpApprovals, getMcpInvocation, wasMcpToolInvoked, } from './mcp-evidence.js';
|
|
7
9
|
export { decideMcpToolCall, redactMcpSecrets, } from './mcp-safety.js';
|
|
8
10
|
export { evaluate, EvalScorerError } from './evaluate.js';
|
|
@@ -15,6 +17,8 @@ export { previewReactions } from './reaction-preview.js';
|
|
|
15
17
|
export { setRuntime, resetRuntime } from './eval-runtime.js';
|
|
16
18
|
export { DEFAULT_COPY_IGNORE } from '../providers/copy-filter.js';
|
|
17
19
|
export { extractToolEventsFromLog } from '../tool-events.js';
|
|
20
|
+
export { collectSensitiveEnvValues, sanitizePersistenceValue, } from '../tool-event-results.js';
|
|
21
|
+
export { parseEnvFile } from '../utils/env.js';
|
|
18
22
|
export { createAskBus, requireAskBusForLiveBatches, AskBusTimeoutError } from './ask-bus/bus.js';
|
|
19
23
|
export { toAskUserToolEvent } from './ask-bus/projection.js';
|
|
20
24
|
export { buildAskBatchLogEntries } from './agent-result-log.js';
|
package/dist/sdk/lifecycle.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getCurrentCaseContext } from './case-context.js';
|
|
2
2
|
import { buildDiagnosticsReport } from './diagnostics.js';
|
|
3
|
+
import { countShellCommandsFromLog } from '../tool-events.js';
|
|
3
4
|
const pendingAgents = new Set();
|
|
4
5
|
const agentOwners = new WeakMap();
|
|
5
6
|
const agentResults = new WeakMap();
|
|
@@ -131,7 +132,7 @@ async function flushCase(input) {
|
|
|
131
132
|
function synthesizeTrialFromAgent(agent) {
|
|
132
133
|
if (agent.log.length === 0)
|
|
133
134
|
return null;
|
|
134
|
-
const nCommands = agent.log
|
|
135
|
+
const nCommands = countShellCommandsFromLog(agent.log);
|
|
135
136
|
const tokenUsage = agent.llm.tokenUsage;
|
|
136
137
|
const conversationEnd = [...agent.log].reverse().find((entry) => entry.type === 'conversation_end');
|
|
137
138
|
const completionReason = conversationEnd?.completion_reason ?? (agent.log.some((entry) => entry.type === 'agent_result') ? 'completed' : undefined);
|
|
@@ -147,8 +148,12 @@ function synthesizeTrialFromAgent(agent) {
|
|
|
147
148
|
n_commands: nCommands,
|
|
148
149
|
input_tokens: 0,
|
|
149
150
|
output_tokens: 0,
|
|
150
|
-
|
|
151
|
-
|
|
151
|
+
...(tokenUsage && (tokenUsage.inputTokens > 0 || tokenUsage.outputTokens > 0)
|
|
152
|
+
? {
|
|
153
|
+
conversation_input_tokens: tokenUsage.inputTokens,
|
|
154
|
+
conversation_output_tokens: tokenUsage.outputTokens,
|
|
155
|
+
}
|
|
156
|
+
: {}),
|
|
152
157
|
session_log: [...agent.log],
|
|
153
158
|
},
|
|
154
159
|
diagnostics: buildDiagnosticsReport({
|
|
@@ -5,6 +5,8 @@ import type { McpSafetyOptions } from './mcp-safety.js';
|
|
|
5
5
|
import type { LLMPort } from '../utils/llm-types.js';
|
|
6
6
|
import type { AskBus } from './ask-bus/types.js';
|
|
7
7
|
import type { CompiledMcpMockSession } from './mcp-mock-approvals.js';
|
|
8
|
+
import { type PublicScenarioRuntimeFactory } from '../internal/direct-mcp-v2/public-scenario-runtime.js';
|
|
9
|
+
import type { EvidenceEnvelope, ScenarioArtifact } from '../internal/direct-mcp-v2/types.js';
|
|
8
10
|
export interface ManagedSessionDeps {
|
|
9
11
|
ws: Workspace;
|
|
10
12
|
agentName: AgentName;
|
|
@@ -36,6 +38,10 @@ export interface ManagedSessionDeps {
|
|
|
36
38
|
opencodeMcpToolNames?: string[];
|
|
37
39
|
/** Trusted pre-workspace compiled generated-MCP declaration. */
|
|
38
40
|
scriptedMcp?: CompiledMcpMockSession;
|
|
41
|
+
scenarioArtifact?: ScenarioArtifact;
|
|
42
|
+
/** Provider adapter entrypoint; defaults to the preserved Claude bridge. */
|
|
43
|
+
scenarioRuntimeFactory?: PublicScenarioRuntimeFactory;
|
|
44
|
+
onScenarioEvidence?: (evidence: readonly EvidenceEnvelope[]) => void;
|
|
39
45
|
/** Runtime-only values that must not enter session logs or persisted results. */
|
|
40
46
|
sensitiveValues?: readonly string[];
|
|
41
47
|
}
|