@brainbase-labs/cli 0.25.0-eng1209.1 → 0.25.0-eng1209.11
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 +14 -7
- package/dist/index.js +572 -112
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,21 +72,28 @@ synchronously. Schema v1 supports:
|
|
|
72
72
|
- `workspace_assertion`: file existence, absence, SHA-256, or content checks.
|
|
73
73
|
- `sandbox_command`: a bounded argv command rooted in the workspace or hidden
|
|
74
74
|
tests directory. Exit zero passes; any other exit code is a valid failed
|
|
75
|
-
verdict. Schema v1 allows
|
|
76
|
-
read-only assertions
|
|
75
|
+
verdict. Schema v1 allows up to 20 command evaluators and runs them after all
|
|
76
|
+
read-only assertions. Each command receives either the verified live workspace
|
|
77
|
+
for legacy read-only evaluation or its own isolated frozen workspace copy.
|
|
77
78
|
|
|
78
79
|
Valid failed verdicts still produce a successful evaluation phase. Invalid
|
|
79
|
-
specs, digest/path violations, missing environment,
|
|
80
|
-
and output-budget violations fail the phase.
|
|
80
|
+
specs, digest/path violations, missing environment, phase-budget exhaustion,
|
|
81
|
+
and output-budget violations fail the phase. Individual command launch failures,
|
|
82
|
+
timeouts, and forced terminations are recorded as errored evaluator results so
|
|
83
|
+
the remaining evaluators can still run. Results are written atomically
|
|
81
84
|
with mode `0600`, include the raw spec digest and checksummed evidence/log
|
|
82
85
|
input/output references with explicit `staging`, `workspace`, `tests`, or
|
|
83
86
|
`logs` roots, redact declared secret values from command logs, require the
|
|
84
87
|
result path to be `<logs_root>/result.json`, and treat a matching successful
|
|
85
88
|
phase result as authoritative on replay.
|
|
86
89
|
|
|
87
|
-
On any phase-level execution failure, MAS must tear down the task sandbox.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
On any phase-level execution failure, MAS must tear down the task sandbox.
|
|
91
|
+
Failed and timed-out commands clean up the process group and descendants the
|
|
92
|
+
CLI can observe. Successful evaluator commands receive the same cleanup so one
|
|
93
|
+
check cannot contaminate the next. Successful hydration setup commands may
|
|
94
|
+
intentionally leave services running for the agent turn; the runtime lifecycle
|
|
95
|
+
remains their final cleanup boundary. Isolated evaluator workspace copies omit
|
|
96
|
+
`.git` and unsafe absolute or escaping symlinks.
|
|
90
97
|
|
|
91
98
|
## Development
|
|
92
99
|
|
package/dist/index.js
CHANGED
|
@@ -36008,7 +36008,7 @@ function padStart(s, n) {
|
|
|
36008
36008
|
// package.json
|
|
36009
36009
|
var package_default = {
|
|
36010
36010
|
name: "@brainbase-labs/cli",
|
|
36011
|
-
version: "0.25.0-eng1209.
|
|
36011
|
+
version: "0.25.0-eng1209.11",
|
|
36012
36012
|
description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
|
|
36013
36013
|
type: "module",
|
|
36014
36014
|
bin: {
|
|
@@ -78485,13 +78485,20 @@ var MAX_TRAJECTORY_BYTES = 100 * 1024 * 1024;
|
|
|
78485
78485
|
var MAX_REMOTE_INPUT_BYTES = 2 * 1024 * 1024 * 1024;
|
|
78486
78486
|
var MAX_ARCHIVE_EXTRACTED_BYTES = 2 * 1024 * 1024 * 1024;
|
|
78487
78487
|
var MAX_ARCHIVE_SCAN_BYTES = 2 * 1024 * 1024 * 1024;
|
|
78488
|
+
var MAX_HYDRATE_COMMANDS = 150;
|
|
78489
|
+
var MAX_PHASE_RESULT_BYTES = 32 * 1024 * 1024;
|
|
78488
78490
|
var MAX_SANDBOX_COMMANDS = 20;
|
|
78489
|
-
var MAX_CRITERIA_PER_EVALUATOR =
|
|
78490
|
-
var MAX_CRITERIA_RESULT_BYTES = 1024 * 1024;
|
|
78491
|
+
var MAX_CRITERIA_PER_EVALUATOR = 200;
|
|
78492
|
+
var MAX_CRITERIA_RESULT_BYTES = 24 * 1024 * 1024;
|
|
78493
|
+
var MAX_CRITERIA_RESULT_PAYLOAD_BYTES = 20 * 1024 * 1024;
|
|
78491
78494
|
var MAX_CRITERION_EXPLANATION_LENGTH = 16384;
|
|
78492
78495
|
var MAX_CRITERION_EVIDENCE_IDS = 100;
|
|
78493
78496
|
var MAX_CRITERION_EVIDENCE_ID_LENGTH = 256;
|
|
78494
78497
|
var MAX_ALLOWED_EVIDENCE_IDS = 1e4;
|
|
78498
|
+
var COMMAND_PROCESS_MARKER_ENV = "BRAINBASE_BENCHMARK_COMMAND_MARKER";
|
|
78499
|
+
var COMMAND_PROCESS_POLL_MS = 10;
|
|
78500
|
+
var COMMAND_PROCESS_CLEANUP_MS = 500;
|
|
78501
|
+
var JUDGE_ERROR_PREFIX = "BRAINBASE_BENCHMARK_JUDGE_ERROR_V1:";
|
|
78495
78502
|
var RESERVED_WORKSPACE_PATHS = new Set([
|
|
78496
78503
|
".brainbase",
|
|
78497
78504
|
".git",
|
|
@@ -78658,7 +78665,7 @@ var TrajectoryEvidenceSchema = EvidenceFileSchema.extend({
|
|
|
78658
78665
|
var HydrateSpecSchema = BaseSpecSchema.extend({
|
|
78659
78666
|
phase: exports_external.literal("hydrate"),
|
|
78660
78667
|
materials: exports_external.array(MaterialSchema).max(1e4).default([]),
|
|
78661
|
-
setup_commands: exports_external.array(CommandSchema).max(
|
|
78668
|
+
setup_commands: exports_external.array(CommandSchema).max(MAX_HYDRATE_COMMANDS).default([])
|
|
78662
78669
|
}).strict();
|
|
78663
78670
|
var CandidateOutputSchema = exports_external.object({
|
|
78664
78671
|
id: IdSchema,
|
|
@@ -78712,6 +78719,20 @@ var BenchmarkSpecSchema = exports_external.discriminatedUnion("phase", [
|
|
|
78712
78719
|
HydrateSpecSchema,
|
|
78713
78720
|
EvaluateSpecSchema
|
|
78714
78721
|
]).superRefine((value, context) => {
|
|
78722
|
+
if (Object.hasOwn(value.environment, COMMAND_PROCESS_MARKER_ENV)) {
|
|
78723
|
+
context.addIssue({
|
|
78724
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78725
|
+
path: ["environment", COMMAND_PROCESS_MARKER_ENV],
|
|
78726
|
+
message: `${COMMAND_PROCESS_MARKER_ENV} is reserved for command supervision`
|
|
78727
|
+
});
|
|
78728
|
+
}
|
|
78729
|
+
if (value.secret_env.includes(COMMAND_PROCESS_MARKER_ENV)) {
|
|
78730
|
+
context.addIssue({
|
|
78731
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78732
|
+
path: ["secret_env"],
|
|
78733
|
+
message: `${COMMAND_PROCESS_MARKER_ENV} is reserved for command supervision`
|
|
78734
|
+
});
|
|
78735
|
+
}
|
|
78715
78736
|
for (const name of Object.keys(value.environment)) {
|
|
78716
78737
|
if (SENSITIVE_ENV_NAME_RE.test(name)) {
|
|
78717
78738
|
context.addIssue({
|
|
@@ -78785,12 +78806,12 @@ var BenchmarkSpecSchema = exports_external.discriminatedUnion("phase", [
|
|
|
78785
78806
|
{ items: value.candidate_outputs, path: "candidate_outputs" }
|
|
78786
78807
|
];
|
|
78787
78808
|
for (const { items, path: issuePath } of uniqueLists) {
|
|
78788
|
-
const ids = items.map((item) => item.id);
|
|
78809
|
+
const ids = items.map((item) => issuePath === "candidate_outputs" ? item.id.toLowerCase() : item.id);
|
|
78789
78810
|
if (new Set(ids).size !== ids.length) {
|
|
78790
78811
|
context.addIssue({
|
|
78791
78812
|
code: exports_external.ZodIssueCode.custom,
|
|
78792
78813
|
path: [issuePath],
|
|
78793
|
-
message: `${issuePath} ids must be unique`
|
|
78814
|
+
message: `${issuePath} ids must be unique${issuePath === "candidate_outputs" ? " ignoring case" : ""}`
|
|
78794
78815
|
});
|
|
78795
78816
|
}
|
|
78796
78817
|
}
|
|
@@ -78853,6 +78874,7 @@ var BENCHMARK_CAPABILITIES = {
|
|
|
78853
78874
|
"remote_input_references_v1",
|
|
78854
78875
|
"archive_file_materials_v1",
|
|
78855
78876
|
"structured_criterion_results_v1",
|
|
78877
|
+
"structured_judge_errors_v1",
|
|
78856
78878
|
"multiple_sandbox_commands_v1",
|
|
78857
78879
|
"sandbox_command_workspace_modes_v1",
|
|
78858
78880
|
"candidate_outputs_v1",
|
|
@@ -78866,6 +78888,8 @@ var BENCHMARK_CAPABILITIES = {
|
|
|
78866
78888
|
],
|
|
78867
78889
|
limits: {
|
|
78868
78890
|
max_secret_bindings: 100,
|
|
78891
|
+
max_hydrate_commands: MAX_HYDRATE_COMMANDS,
|
|
78892
|
+
max_phase_result_bytes: MAX_PHASE_RESULT_BYTES,
|
|
78869
78893
|
max_evaluators: 1000,
|
|
78870
78894
|
max_sandbox_commands: MAX_SANDBOX_COMMANDS,
|
|
78871
78895
|
max_criteria_per_evaluator: MAX_CRITERIA_PER_EVALUATOR
|
|
@@ -78882,6 +78906,19 @@ class BenchmarkPhaseError extends Error {
|
|
|
78882
78906
|
this.name = "BenchmarkPhaseError";
|
|
78883
78907
|
}
|
|
78884
78908
|
}
|
|
78909
|
+
|
|
78910
|
+
class BenchmarkCommandExecutionError extends BenchmarkPhaseError {
|
|
78911
|
+
stdout;
|
|
78912
|
+
stderr;
|
|
78913
|
+
durationMs;
|
|
78914
|
+
constructor(code, message, stdout, stderr, durationMs) {
|
|
78915
|
+
super(code, message);
|
|
78916
|
+
this.stdout = stdout;
|
|
78917
|
+
this.stderr = stderr;
|
|
78918
|
+
this.durationMs = durationMs;
|
|
78919
|
+
this.name = "BenchmarkCommandExecutionError";
|
|
78920
|
+
}
|
|
78921
|
+
}
|
|
78885
78922
|
var ZIP_EOCD_SIGNATURE = 101010256;
|
|
78886
78923
|
var ZIP_CENTRAL_SIGNATURE = 33639248;
|
|
78887
78924
|
var ZIP_LOCAL_SIGNATURE = 67324752;
|
|
@@ -79114,10 +79151,15 @@ async function verifyRecordsUnchanged(records, spec) {
|
|
|
79114
79151
|
const relative = safeRelPath(record3.path);
|
|
79115
79152
|
assertNoSymlinkTraversal(root, relative);
|
|
79116
79153
|
const candidate = path88.resolve(root, relative);
|
|
79117
|
-
if (!isWithin(root, candidate)
|
|
79154
|
+
if (!isWithin(root, candidate)) {
|
|
79155
|
+
throw new BenchmarkPhaseError("evidence_tampered", `evidence was removed during evaluation: ${record3.root}:${record3.path}`);
|
|
79156
|
+
}
|
|
79157
|
+
let stat;
|
|
79158
|
+
try {
|
|
79159
|
+
stat = fs81.lstatSync(candidate);
|
|
79160
|
+
} catch {
|
|
79118
79161
|
throw new BenchmarkPhaseError("evidence_tampered", `evidence was removed during evaluation: ${record3.root}:${record3.path}`);
|
|
79119
79162
|
}
|
|
79120
|
-
const stat = fs81.lstatSync(candidate);
|
|
79121
79163
|
if (record3.kind === "symlink") {
|
|
79122
79164
|
const target = stat.isSymbolicLink() ? fs81.readlinkSync(candidate) : null;
|
|
79123
79165
|
if (target === null || Buffer.byteLength(target) !== record3.size || sha256(target) !== record3.sha256) {
|
|
@@ -79212,6 +79254,15 @@ function removeRemoteHydrationInputs(spec) {
|
|
|
79212
79254
|
removedSources.add(material.source);
|
|
79213
79255
|
}
|
|
79214
79256
|
}
|
|
79257
|
+
function removePrivateHydrationSourcesBeforeSetup(spec, context) {
|
|
79258
|
+
removeRemoteHydrationInputs(spec);
|
|
79259
|
+
for (const temporary of context.temporaryRoots) {
|
|
79260
|
+
fs81.rmSync(temporary, { recursive: true, force: true });
|
|
79261
|
+
}
|
|
79262
|
+
context.temporaryRoots.clear();
|
|
79263
|
+
context.verifiedInputs.clear();
|
|
79264
|
+
context.preparedArchiveFiles.clear();
|
|
79265
|
+
}
|
|
79215
79266
|
async function downloadInputReference(stagingRoot, input, context) {
|
|
79216
79267
|
const cacheKey = inputCacheKey(input);
|
|
79217
79268
|
const cached2 = context.verifiedInputs.get(cacheKey);
|
|
@@ -79893,7 +79944,7 @@ function buildEnvironment(spec, secretNames, additions = {}) {
|
|
|
79893
79944
|
Object.assign(env3, additions);
|
|
79894
79945
|
return env3;
|
|
79895
79946
|
}
|
|
79896
|
-
function redactCommandOutput(data, spec) {
|
|
79947
|
+
function redactCommandOutput(data, spec, additionalSecrets = []) {
|
|
79897
79948
|
let value = data.toString("utf8");
|
|
79898
79949
|
const sensitiveNames = new Set([
|
|
79899
79950
|
...spec.secret_env,
|
|
@@ -79904,6 +79955,10 @@ function redactCommandOutput(data, spec) {
|
|
|
79904
79955
|
if (secret)
|
|
79905
79956
|
value = value.split(secret).join("[REDACTED]");
|
|
79906
79957
|
}
|
|
79958
|
+
for (const secret of additionalSecrets) {
|
|
79959
|
+
if (secret)
|
|
79960
|
+
value = value.split(secret).join("[REDACTED]");
|
|
79961
|
+
}
|
|
79907
79962
|
return Buffer.from(value);
|
|
79908
79963
|
}
|
|
79909
79964
|
function descendantPids(parentPid) {
|
|
@@ -79955,7 +80010,99 @@ function terminate(child) {
|
|
|
79955
80010
|
child.kill("SIGKILL");
|
|
79956
80011
|
}
|
|
79957
80012
|
}
|
|
79958
|
-
|
|
80013
|
+
function markedProcessPids(marker) {
|
|
80014
|
+
const assignment = `${COMMAND_PROCESS_MARKER_ENV}=${marker}`;
|
|
80015
|
+
if (process.platform === "linux") {
|
|
80016
|
+
const matches2 = [];
|
|
80017
|
+
let entries;
|
|
80018
|
+
try {
|
|
80019
|
+
entries = fs81.readdirSync("/proc");
|
|
80020
|
+
} catch {
|
|
80021
|
+
return matches2;
|
|
80022
|
+
}
|
|
80023
|
+
for (const entry of entries) {
|
|
80024
|
+
if (!/^\d+$/.test(entry))
|
|
80025
|
+
continue;
|
|
80026
|
+
const pid = Number(entry);
|
|
80027
|
+
if (pid === process.pid)
|
|
80028
|
+
continue;
|
|
80029
|
+
try {
|
|
80030
|
+
const environment = fs81.readFileSync(path88.join("/proc", entry, "environ"), "utf8");
|
|
80031
|
+
if (environment.split("\x00").includes(assignment))
|
|
80032
|
+
matches2.push(pid);
|
|
80033
|
+
} catch {}
|
|
80034
|
+
}
|
|
80035
|
+
return matches2;
|
|
80036
|
+
}
|
|
80037
|
+
if (process.platform === "darwin") {
|
|
80038
|
+
try {
|
|
80039
|
+
const output = execFileSync2("ps", ["eww", "-axo", "pid=,command="], {
|
|
80040
|
+
encoding: "utf8",
|
|
80041
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
80042
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
80043
|
+
});
|
|
80044
|
+
const matches2 = [];
|
|
80045
|
+
for (const line of output.split(`
|
|
80046
|
+
`)) {
|
|
80047
|
+
const match = line.match(/^\s*(\d+)\s+(.*)$/);
|
|
80048
|
+
if (!match || !match[2].includes(assignment))
|
|
80049
|
+
continue;
|
|
80050
|
+
const pid = Number(match[1]);
|
|
80051
|
+
if (Number.isInteger(pid) && pid !== process.pid)
|
|
80052
|
+
matches2.push(pid);
|
|
80053
|
+
}
|
|
80054
|
+
return matches2;
|
|
80055
|
+
} catch {
|
|
80056
|
+
return [];
|
|
80057
|
+
}
|
|
80058
|
+
}
|
|
80059
|
+
return [];
|
|
80060
|
+
}
|
|
80061
|
+
function processExists(pid) {
|
|
80062
|
+
if (process.platform === "linux") {
|
|
80063
|
+
try {
|
|
80064
|
+
const stat = fs81.readFileSync(path88.join("/proc", String(pid), "stat"), "utf8");
|
|
80065
|
+
const commandEnd = stat.lastIndexOf(")");
|
|
80066
|
+
const state = commandEnd >= 0 ? stat.slice(commandEnd + 2, commandEnd + 3) : "";
|
|
80067
|
+
if (state === "Z" || state === "X")
|
|
80068
|
+
return false;
|
|
80069
|
+
} catch (error2) {
|
|
80070
|
+
if (error2.code === "ENOENT")
|
|
80071
|
+
return false;
|
|
80072
|
+
}
|
|
80073
|
+
}
|
|
80074
|
+
try {
|
|
80075
|
+
process.kill(pid, 0);
|
|
80076
|
+
return true;
|
|
80077
|
+
} catch (error2) {
|
|
80078
|
+
return error2.code !== "ESRCH";
|
|
80079
|
+
}
|
|
80080
|
+
}
|
|
80081
|
+
async function terminateCommandProcesses(child, marker, observedDescendants) {
|
|
80082
|
+
if (child.pid !== undefined) {
|
|
80083
|
+
for (const pid of descendantPids(child.pid))
|
|
80084
|
+
observedDescendants.add(pid);
|
|
80085
|
+
}
|
|
80086
|
+
terminate(child);
|
|
80087
|
+
const deadline = Date.now() + COMMAND_PROCESS_CLEANUP_MS;
|
|
80088
|
+
while (true) {
|
|
80089
|
+
for (const pid of markedProcessPids(marker))
|
|
80090
|
+
observedDescendants.add(pid);
|
|
80091
|
+
const active = [...observedDescendants].filter(processExists);
|
|
80092
|
+
for (const pid of active) {
|
|
80093
|
+
try {
|
|
80094
|
+
process.kill(pid, "SIGKILL");
|
|
80095
|
+
} catch {}
|
|
80096
|
+
}
|
|
80097
|
+
if (active.length === 0)
|
|
80098
|
+
return;
|
|
80099
|
+
if (Date.now() >= deadline) {
|
|
80100
|
+
throw new BenchmarkPhaseError("command_cleanup_failed", "command descendants remained after bounded cleanup");
|
|
80101
|
+
}
|
|
80102
|
+
await new Promise((resolve) => setTimeout(resolve, COMMAND_PROCESS_POLL_MS));
|
|
80103
|
+
}
|
|
80104
|
+
}
|
|
80105
|
+
async function runCommand(command, root, spec, context, options = {}) {
|
|
79959
80106
|
const cwdRel = normalizedRootRelative(command.cwd);
|
|
79960
80107
|
assertNoSymlinkTraversal(root, cwdRel);
|
|
79961
80108
|
const cwd2 = path88.resolve(root, cwdRel);
|
|
@@ -79971,29 +80118,59 @@ async function runCommand(command, root, spec, context, additions = {}) {
|
|
|
79971
80118
|
const remainingMs = context.deadline - Date.now();
|
|
79972
80119
|
if (remainingMs <= 0)
|
|
79973
80120
|
throw new BenchmarkPhaseError("phase_timeout", "phase budget expired");
|
|
79974
|
-
const
|
|
80121
|
+
const reachesPhaseDeadline = command.timeout_ms === undefined || command.timeout_ms >= remainingMs;
|
|
80122
|
+
const timeoutMs2 = reachesPhaseDeadline ? remainingMs : command.timeout_ms;
|
|
79975
80123
|
const started = Date.now();
|
|
80124
|
+
const commandMarker = crypto6.randomBytes(32).toString("hex");
|
|
79976
80125
|
return await new Promise((resolve, reject2) => {
|
|
79977
80126
|
const child = spawn4(command.argv[0], command.argv.slice(1), {
|
|
79978
80127
|
cwd: cwd2,
|
|
79979
|
-
env: buildEnvironment(spec, command.secret_env,
|
|
80128
|
+
env: buildEnvironment(spec, command.secret_env, {
|
|
80129
|
+
...options.additions,
|
|
80130
|
+
[COMMAND_PROCESS_MARKER_ENV]: commandMarker
|
|
80131
|
+
}),
|
|
79980
80132
|
stdio: ["ignore", "pipe", "pipe"],
|
|
79981
80133
|
detached: process.platform !== "win32"
|
|
79982
80134
|
});
|
|
79983
80135
|
const stdout = [];
|
|
79984
80136
|
const stderr = [];
|
|
80137
|
+
const observedDescendants = new Set;
|
|
79985
80138
|
let captured = 0;
|
|
79986
80139
|
let settled = false;
|
|
79987
80140
|
let timer;
|
|
80141
|
+
let observer;
|
|
80142
|
+
let cleanup;
|
|
80143
|
+
const observeDescendants = () => {
|
|
80144
|
+
if (child.pid === undefined)
|
|
80145
|
+
return;
|
|
80146
|
+
for (const pid of descendantPids(child.pid))
|
|
80147
|
+
observedDescendants.add(pid);
|
|
80148
|
+
};
|
|
80149
|
+
const cleanupProcesses = () => {
|
|
80150
|
+
if (observer)
|
|
80151
|
+
clearInterval(observer);
|
|
80152
|
+
cleanup ??= terminateCommandProcesses(child, commandMarker, observedDescendants);
|
|
80153
|
+
return cleanup;
|
|
80154
|
+
};
|
|
80155
|
+
const sanitizedOutput = (chunks) => redactCommandOutput(Buffer.concat(chunks), spec, [commandMarker]);
|
|
80156
|
+
const sanitizedError = (error2) => {
|
|
80157
|
+
if (!(error2 instanceof BenchmarkCommandExecutionError))
|
|
80158
|
+
return error2;
|
|
80159
|
+
return new BenchmarkCommandExecutionError(error2.code, error2.message, sanitizedOutput(stdout), sanitizedOutput(stderr), error2.durationMs);
|
|
80160
|
+
};
|
|
79988
80161
|
const fail = (error2) => {
|
|
79989
80162
|
if (settled)
|
|
79990
80163
|
return;
|
|
79991
80164
|
settled = true;
|
|
79992
80165
|
if (timer)
|
|
79993
80166
|
clearTimeout(timer);
|
|
79994
|
-
|
|
79995
|
-
reject2(error2);
|
|
80167
|
+
cleanupProcesses().then(() => reject2(sanitizedError(error2)), reject2);
|
|
79996
80168
|
};
|
|
80169
|
+
if (process.platform !== "linux") {
|
|
80170
|
+
observeDescendants();
|
|
80171
|
+
observer = setInterval(observeDescendants, COMMAND_PROCESS_POLL_MS);
|
|
80172
|
+
observer.unref();
|
|
80173
|
+
}
|
|
79997
80174
|
const capture = (target, chunk2) => {
|
|
79998
80175
|
if (settled)
|
|
79999
80176
|
return;
|
|
@@ -80005,29 +80182,48 @@ async function runCommand(command, root, spec, context, additions = {}) {
|
|
|
80005
80182
|
}
|
|
80006
80183
|
target.push(chunk2);
|
|
80007
80184
|
};
|
|
80185
|
+
const finish = (code, signal) => {
|
|
80186
|
+
if (observer)
|
|
80187
|
+
clearInterval(observer);
|
|
80188
|
+
if (code === null) {
|
|
80189
|
+
reject2(new BenchmarkCommandExecutionError("command_terminated", `command was terminated by ${signal ?? "an unknown signal"}: ${command.id}`, sanitizedOutput(stdout), sanitizedOutput(stderr), Date.now() - started));
|
|
80190
|
+
return;
|
|
80191
|
+
}
|
|
80192
|
+
resolve({
|
|
80193
|
+
exitCode: code,
|
|
80194
|
+
stdout: sanitizedOutput(stdout),
|
|
80195
|
+
stderr: sanitizedOutput(stderr),
|
|
80196
|
+
durationMs: Date.now() - started,
|
|
80197
|
+
redactions: [commandMarker]
|
|
80198
|
+
});
|
|
80199
|
+
};
|
|
80008
80200
|
child.stdout?.on("data", (chunk2) => capture(stdout, chunk2));
|
|
80009
80201
|
child.stderr?.on("data", (chunk2) => capture(stderr, chunk2));
|
|
80010
80202
|
child.on("error", (error2) => {
|
|
80011
|
-
fail(new
|
|
80203
|
+
fail(new BenchmarkCommandExecutionError("command_start_failed", `failed to start ${command.id}: ${error2.message}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
|
|
80012
80204
|
});
|
|
80013
80205
|
timer = setTimeout(() => {
|
|
80014
|
-
|
|
80206
|
+
if (reachesPhaseDeadline) {
|
|
80207
|
+
fail(new BenchmarkPhaseError("phase_timeout", "phase budget expired"));
|
|
80208
|
+
return;
|
|
80209
|
+
}
|
|
80210
|
+
fail(new BenchmarkCommandExecutionError("command_timeout", `command timed out: ${command.id}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
|
|
80015
80211
|
}, timeoutMs2);
|
|
80016
|
-
child.on("
|
|
80212
|
+
child.on("exit", (code, signal) => {
|
|
80017
80213
|
if (settled)
|
|
80018
80214
|
return;
|
|
80019
80215
|
settled = true;
|
|
80020
80216
|
clearTimeout(timer);
|
|
80021
|
-
if (code === null) {
|
|
80022
|
-
|
|
80023
|
-
|
|
80217
|
+
if (options.descendantCleanup === "always" || code === null || code !== 0) {
|
|
80218
|
+
cleanupProcesses().then(() => finish(code, signal), reject2);
|
|
80219
|
+
} else {
|
|
80220
|
+
if (observer)
|
|
80221
|
+
clearInterval(observer);
|
|
80222
|
+
child.unref();
|
|
80223
|
+
child.stdout?.unref?.();
|
|
80224
|
+
child.stderr?.unref?.();
|
|
80225
|
+
finish(code, signal);
|
|
80024
80226
|
}
|
|
80025
|
-
resolve({
|
|
80026
|
-
exitCode: code,
|
|
80027
|
-
stdout: Buffer.concat(stdout),
|
|
80028
|
-
stderr: Buffer.concat(stderr),
|
|
80029
|
-
durationMs: Date.now() - started
|
|
80030
|
-
});
|
|
80031
80227
|
});
|
|
80032
80228
|
});
|
|
80033
80229
|
}
|
|
@@ -80100,11 +80296,14 @@ async function executeHydrate(spec, context) {
|
|
|
80100
80296
|
throw error2;
|
|
80101
80297
|
}
|
|
80102
80298
|
}
|
|
80299
|
+
removePrivateHydrationSourcesBeforeSetup(spec, context);
|
|
80103
80300
|
for (const command of spec.setup_commands) {
|
|
80104
80301
|
assertBudget(context);
|
|
80105
80302
|
let result2;
|
|
80106
80303
|
try {
|
|
80107
|
-
result2 = await runCommand(command, spec.workspace_root, spec, context
|
|
80304
|
+
result2 = await runCommand(command, spec.workspace_root, spec, context, {
|
|
80305
|
+
descendantCleanup: "failure_only"
|
|
80306
|
+
});
|
|
80108
80307
|
} catch (error2) {
|
|
80109
80308
|
context.steps.push({
|
|
80110
80309
|
id: command.id,
|
|
@@ -80155,7 +80354,6 @@ async function executeHydrate(spec, context) {
|
|
|
80155
80354
|
}
|
|
80156
80355
|
outputs.splice(0, outputs.length, ...finalOutputs);
|
|
80157
80356
|
context.outputs.splice(0, context.outputs.length, ...finalOutputs);
|
|
80158
|
-
removeRemoteHydrationInputs(spec);
|
|
80159
80357
|
assertBudget(context);
|
|
80160
80358
|
return outputs;
|
|
80161
80359
|
}
|
|
@@ -80220,6 +80418,49 @@ async function workspaceManifest(spec, context) {
|
|
|
80220
80418
|
}
|
|
80221
80419
|
return records.sort((a3, b4) => a3.path.localeCompare(b4.path));
|
|
80222
80420
|
}
|
|
80421
|
+
async function verifyWorkspaceManifestUnchanged(expected, spec, context) {
|
|
80422
|
+
await verifyRecordsUnchanged(expected, spec);
|
|
80423
|
+
const actual = await workspaceManifest(spec, context);
|
|
80424
|
+
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
|
|
80425
|
+
throw new BenchmarkPhaseError("evidence_tampered", "candidate workspace changed during read-only evaluation");
|
|
80426
|
+
}
|
|
80427
|
+
}
|
|
80428
|
+
function treeBytes(root, context) {
|
|
80429
|
+
let totalBytes = 0;
|
|
80430
|
+
const stack = [path88.resolve(root)];
|
|
80431
|
+
while (stack.length > 0) {
|
|
80432
|
+
const directory = stack.pop();
|
|
80433
|
+
const entries = fs81.readdirSync(directory, { withFileTypes: true });
|
|
80434
|
+
for (const entry of entries) {
|
|
80435
|
+
assertBudget(context);
|
|
80436
|
+
const candidate = path88.join(directory, entry.name);
|
|
80437
|
+
const stat = fs81.lstatSync(candidate);
|
|
80438
|
+
if (stat.isDirectory()) {
|
|
80439
|
+
stack.push(candidate);
|
|
80440
|
+
continue;
|
|
80441
|
+
}
|
|
80442
|
+
if (stat.isFile()) {
|
|
80443
|
+
totalBytes += stat.size;
|
|
80444
|
+
continue;
|
|
80445
|
+
}
|
|
80446
|
+
if (stat.isSymbolicLink()) {
|
|
80447
|
+
totalBytes += Buffer.byteLength(fs81.readlinkSync(candidate));
|
|
80448
|
+
continue;
|
|
80449
|
+
}
|
|
80450
|
+
throw new BenchmarkPhaseError("unsafe_path", `evaluate output contains an unsupported filesystem entry: ${path88.relative(root, candidate)}`);
|
|
80451
|
+
}
|
|
80452
|
+
}
|
|
80453
|
+
return totalBytes;
|
|
80454
|
+
}
|
|
80455
|
+
function assertEvaluateOutputBudget(spec, context, additionalBytes = 0) {
|
|
80456
|
+
const actual = treeBytes(spec.logs_root, context) + additionalBytes;
|
|
80457
|
+
if (actual > spec.workspace_limits.max_total_bytes) {
|
|
80458
|
+
throw new BenchmarkPhaseError("workspace_limit_exceeded", "evaluate output bytes exceed the workspace byte limit", {
|
|
80459
|
+
actual,
|
|
80460
|
+
max_total_bytes: spec.workspace_limits.max_total_bytes
|
|
80461
|
+
});
|
|
80462
|
+
}
|
|
80463
|
+
}
|
|
80223
80464
|
function candidateGlob(pattern) {
|
|
80224
80465
|
let source = "^";
|
|
80225
80466
|
for (let index = 0;index < pattern.length; index += 1) {
|
|
@@ -80250,6 +80491,8 @@ function manifestDirectories(manifest, context) {
|
|
|
80250
80491
|
const directories = new Set;
|
|
80251
80492
|
for (const entry of manifest) {
|
|
80252
80493
|
assertBudget(context);
|
|
80494
|
+
if (entry.kind === "symlink")
|
|
80495
|
+
continue;
|
|
80253
80496
|
let current = path88.posix.dirname(entry.path);
|
|
80254
80497
|
while (current !== ".") {
|
|
80255
80498
|
assertBudget(context);
|
|
@@ -80267,9 +80510,8 @@ function candidateOutputFiles(output, manifest, context) {
|
|
|
80267
80510
|
assertBudget(context);
|
|
80268
80511
|
if (!matcher.test(entry.path))
|
|
80269
80512
|
continue;
|
|
80270
|
-
if (entry.kind === "symlink")
|
|
80271
|
-
|
|
80272
|
-
}
|
|
80513
|
+
if (entry.kind === "symlink")
|
|
80514
|
+
continue;
|
|
80273
80515
|
matched.push(entry);
|
|
80274
80516
|
}
|
|
80275
80517
|
return {
|
|
@@ -80277,12 +80519,6 @@ function candidateOutputFiles(output, manifest, context) {
|
|
|
80277
80519
|
files: matched
|
|
80278
80520
|
};
|
|
80279
80521
|
}
|
|
80280
|
-
for (const entry of manifest) {
|
|
80281
|
-
assertBudget(context);
|
|
80282
|
-
if (entry.kind === "symlink" && matcher.test(entry.path)) {
|
|
80283
|
-
throw new BenchmarkPhaseError("unsafe_path", `candidate output ${output.id} matches a symlink`);
|
|
80284
|
-
}
|
|
80285
|
-
}
|
|
80286
80522
|
const directories = [];
|
|
80287
80523
|
for (const directory of manifestDirectories(manifest, context)) {
|
|
80288
80524
|
assertBudget(context);
|
|
@@ -80297,9 +80533,8 @@ function candidateOutputFiles(output, manifest, context) {
|
|
|
80297
80533
|
assertBudget(context);
|
|
80298
80534
|
if (!entry.path.startsWith(prefix))
|
|
80299
80535
|
continue;
|
|
80300
|
-
if (entry.kind === "symlink")
|
|
80301
|
-
|
|
80302
|
-
}
|
|
80536
|
+
if (entry.kind === "symlink")
|
|
80537
|
+
continue;
|
|
80303
80538
|
selected.set(entry.path, entry);
|
|
80304
80539
|
}
|
|
80305
80540
|
}
|
|
@@ -80332,7 +80567,7 @@ async function copyCandidateOutput(output, manifest, spec, context) {
|
|
|
80332
80567
|
for (const frozenFile of selected.files) {
|
|
80333
80568
|
assertBudget(context);
|
|
80334
80569
|
const source = path88.resolve(spec.workspace_root, safeRelPath(frozenFile.path));
|
|
80335
|
-
const destination = path88.resolve(spec.logs_root, "candidate-
|
|
80570
|
+
const destination = path88.resolve(spec.logs_root, "candidate-outputs", output.id, safeRelPath(frozenFile.path));
|
|
80336
80571
|
if (fs81.existsSync(destination)) {
|
|
80337
80572
|
throw new BenchmarkPhaseError("destination_conflict", `candidate output destination already exists: ${output.id}/${frozenFile.path}`);
|
|
80338
80573
|
}
|
|
@@ -80345,21 +80580,29 @@ async function copyCandidateOutput(output, manifest, spec, context) {
|
|
|
80345
80580
|
}
|
|
80346
80581
|
return copied;
|
|
80347
80582
|
}
|
|
80348
|
-
async function copyFrozenWorkspace(manifest, spec, context) {
|
|
80349
|
-
for (const entry of manifest) {
|
|
80350
|
-
assertBudget(context);
|
|
80351
|
-
if (entry.kind === "symlink") {
|
|
80352
|
-
throw new BenchmarkPhaseError("unsupported_workspace_symlink", `sandbox evaluator workspace cannot safely reproduce symlink: ${entry.path}`);
|
|
80353
|
-
}
|
|
80354
|
-
}
|
|
80583
|
+
async function copyFrozenWorkspace(manifest, spec, context, sourceRoot = spec.workspace_root, symlinkPolicy = "preserve") {
|
|
80355
80584
|
const destinationRoot = fs81.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-workspace-"));
|
|
80356
80585
|
fs81.chmodSync(destinationRoot, 448);
|
|
80357
80586
|
context.temporaryRoots.add(destinationRoot);
|
|
80358
80587
|
for (const frozenFile of manifest) {
|
|
80359
80588
|
assertBudget(context);
|
|
80360
|
-
const source = path88.resolve(
|
|
80589
|
+
const source = path88.resolve(sourceRoot, safeRelPath(frozenFile.path));
|
|
80361
80590
|
const destination = path88.resolve(destinationRoot, safeRelPath(frozenFile.path));
|
|
80362
|
-
|
|
80591
|
+
if (frozenFile.kind === "symlink") {
|
|
80592
|
+
const stat = fs81.lstatSync(source);
|
|
80593
|
+
const target = stat.isSymbolicLink() ? fs81.readlinkSync(source) : null;
|
|
80594
|
+
if (target === null || Buffer.byteLength(target) !== frozenFile.size || sha256(target) !== frozenFile.sha256) {
|
|
80595
|
+
throw new BenchmarkPhaseError("evidence_tampered", `workspace changed after it was frozen: ${frozenFile.path}`);
|
|
80596
|
+
}
|
|
80597
|
+
const resolvedTarget = path88.resolve(path88.dirname(source), target);
|
|
80598
|
+
if (symlinkPolicy === "contained_relative_only" && (path88.isAbsolute(target) || !isWithin(sourceRoot, resolvedTarget))) {
|
|
80599
|
+
continue;
|
|
80600
|
+
}
|
|
80601
|
+
fs81.mkdirSync(path88.dirname(destination), { recursive: true, mode: 448 });
|
|
80602
|
+
fs81.symlinkSync(target, destination);
|
|
80603
|
+
continue;
|
|
80604
|
+
}
|
|
80605
|
+
await atomicCopy(source, destination, frozenFile.mode, sourceRoot);
|
|
80363
80606
|
const copied = await recordFile(destinationRoot, destination, "workspace");
|
|
80364
80607
|
if (copied.sha256 !== frozenFile.sha256 || copied.size !== frozenFile.size || copied.mode !== frozenFile.mode) {
|
|
80365
80608
|
throw new BenchmarkPhaseError("evidence_tampered", `workspace changed after it was frozen: ${frozenFile.path}`);
|
|
@@ -80367,13 +80610,52 @@ async function copyFrozenWorkspace(manifest, spec, context) {
|
|
|
80367
80610
|
}
|
|
80368
80611
|
return destinationRoot;
|
|
80369
80612
|
}
|
|
80370
|
-
function
|
|
80613
|
+
async function copyEvaluatorTests(spec, evaluator, context) {
|
|
80614
|
+
const destinationRoot = fs81.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-tests-"));
|
|
80615
|
+
fs81.chmodSync(destinationRoot, 448);
|
|
80616
|
+
context.temporaryRoots.add(destinationRoot);
|
|
80617
|
+
const sourceRoot = evaluatorTestsPath(evaluator, spec.tests_root);
|
|
80618
|
+
const relativeRoot = evaluator.tests_path ? normalizedRootRelative(evaluator.tests_path) : ".";
|
|
80619
|
+
const destinationStart = relativeRoot === "." ? destinationRoot : path88.resolve(destinationRoot, relativeRoot);
|
|
80620
|
+
fs81.mkdirSync(destinationStart, { recursive: true, mode: 448 });
|
|
80621
|
+
const stack = [{ source: sourceRoot, destination: destinationStart }];
|
|
80622
|
+
while (stack.length > 0) {
|
|
80623
|
+
assertBudget(context);
|
|
80624
|
+
const current = stack.pop();
|
|
80625
|
+
const entries = fs81.readdirSync(current.source, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
80626
|
+
for (const entry of entries) {
|
|
80627
|
+
assertBudget(context);
|
|
80628
|
+
const source = path88.join(current.source, entry.name);
|
|
80629
|
+
const destination = path88.join(current.destination, entry.name);
|
|
80630
|
+
const stat = fs81.lstatSync(source);
|
|
80631
|
+
if (stat.isSymbolicLink()) {
|
|
80632
|
+
throw new BenchmarkPhaseError("unsafe_path", `evaluator reference tree contains a symlink: ${path88.relative(spec.tests_root, source)}`);
|
|
80633
|
+
}
|
|
80634
|
+
if (stat.isDirectory()) {
|
|
80635
|
+
fs81.mkdirSync(destination, { recursive: true, mode: stat.mode & 511 });
|
|
80636
|
+
stack.push({ source, destination });
|
|
80637
|
+
continue;
|
|
80638
|
+
}
|
|
80639
|
+
if (!stat.isFile()) {
|
|
80640
|
+
throw new BenchmarkPhaseError("unsafe_path", `evaluator reference tree contains an unsupported filesystem entry: ${path88.relative(spec.tests_root, source)}`);
|
|
80641
|
+
}
|
|
80642
|
+
await atomicCopy(source, destination, stat.mode & 511, spec.tests_root);
|
|
80643
|
+
const sourceRecord = await recordFile(spec.tests_root, source, "tests");
|
|
80644
|
+
const copiedRecord = await recordFile(destinationRoot, destination, "tests");
|
|
80645
|
+
if (copiedRecord.path !== sourceRecord.path || copiedRecord.sha256 !== sourceRecord.sha256 || copiedRecord.size !== sourceRecord.size || copiedRecord.mode !== sourceRecord.mode) {
|
|
80646
|
+
throw new BenchmarkPhaseError("evidence_tampered", `evaluator reference changed while it was copied: ${sourceRecord.path}`);
|
|
80647
|
+
}
|
|
80648
|
+
}
|
|
80649
|
+
}
|
|
80650
|
+
return destinationRoot;
|
|
80651
|
+
}
|
|
80652
|
+
function evaluatorTestsPath(evaluator, testsRoot) {
|
|
80371
80653
|
if (!evaluator.tests_path)
|
|
80372
|
-
return
|
|
80654
|
+
return testsRoot;
|
|
80373
80655
|
const relative = normalizedRootRelative(evaluator.tests_path);
|
|
80374
|
-
assertNoSymlinkTraversal(
|
|
80375
|
-
const candidate = path88.resolve(
|
|
80376
|
-
if (!isWithin(
|
|
80656
|
+
assertNoSymlinkTraversal(testsRoot, relative);
|
|
80657
|
+
const candidate = path88.resolve(testsRoot, relative);
|
|
80658
|
+
if (!isWithin(testsRoot, candidate) || !fs81.existsSync(candidate)) {
|
|
80377
80659
|
throw new BenchmarkPhaseError("invalid_tests_path", `evaluator tests_path does not exist: ${evaluator.tests_path}`);
|
|
80378
80660
|
}
|
|
80379
80661
|
const stat = fs81.lstatSync(candidate);
|
|
@@ -80398,8 +80680,39 @@ var CriterionResultSchema = exports_external.object({
|
|
|
80398
80680
|
});
|
|
80399
80681
|
var CriteriaResultFileSchema = exports_external.object({
|
|
80400
80682
|
criteria: exports_external.array(CriterionResultSchema).max(MAX_CRITERIA_PER_EVALUATOR)
|
|
80683
|
+
}).strict().superRefine((value, context) => {
|
|
80684
|
+
if (Buffer.byteLength(JSON.stringify(value)) > MAX_CRITERIA_RESULT_PAYLOAD_BYTES) {
|
|
80685
|
+
context.addIssue({
|
|
80686
|
+
code: exports_external.ZodIssueCode.custom,
|
|
80687
|
+
path: ["criteria"],
|
|
80688
|
+
message: "criterion result exceeds the 20 MiB aggregate payload limit"
|
|
80689
|
+
});
|
|
80690
|
+
}
|
|
80691
|
+
});
|
|
80692
|
+
var JudgeErrorSchema = exports_external.object({
|
|
80693
|
+
code: exports_external.enum([
|
|
80694
|
+
"judge_provider_quota_exhausted",
|
|
80695
|
+
"judge_rate_limited",
|
|
80696
|
+
"judge_provider_authentication_failed",
|
|
80697
|
+
"judge_provider_unavailable",
|
|
80698
|
+
"judge_request_failed"
|
|
80699
|
+
]),
|
|
80700
|
+
message: exports_external.string().min(1).max(500)
|
|
80401
80701
|
}).strict();
|
|
80402
|
-
function
|
|
80702
|
+
function structuredJudgeError(stderr) {
|
|
80703
|
+
for (const line of stderr.toString("utf8").split(/\r?\n/).reverse()) {
|
|
80704
|
+
const marker = line.indexOf(JUDGE_ERROR_PREFIX);
|
|
80705
|
+
if (marker < 0)
|
|
80706
|
+
continue;
|
|
80707
|
+
try {
|
|
80708
|
+
const parsed = JudgeErrorSchema.safeParse(JSON.parse(line.slice(marker + JUDGE_ERROR_PREFIX.length)));
|
|
80709
|
+
if (parsed.success)
|
|
80710
|
+
return parsed.data;
|
|
80711
|
+
} catch {}
|
|
80712
|
+
}
|
|
80713
|
+
return null;
|
|
80714
|
+
}
|
|
80715
|
+
function readCriterionResults(resultPath, criterionKeys, allowedEvidenceIds, spec, additionalSecrets = []) {
|
|
80403
80716
|
let opened;
|
|
80404
80717
|
try {
|
|
80405
80718
|
opened = openRegularFileNoFollow(resultPath, "criterion result", path88.dirname(resultPath));
|
|
@@ -80411,7 +80724,7 @@ function readCriterionResults(resultPath, criterionKeys, allowedEvidenceIds) {
|
|
|
80411
80724
|
}
|
|
80412
80725
|
try {
|
|
80413
80726
|
if (opened.stat.size > MAX_CRITERIA_RESULT_BYTES) {
|
|
80414
|
-
throw new BenchmarkPhaseError("invalid_criterion_result", "criterion result exceeds the
|
|
80727
|
+
throw new BenchmarkPhaseError("invalid_criterion_result", "criterion result exceeds the 24 MiB file limit");
|
|
80415
80728
|
}
|
|
80416
80729
|
let raw;
|
|
80417
80730
|
try {
|
|
@@ -80443,7 +80756,13 @@ function readCriterionResults(resultPath, criterionKeys, allowedEvidenceIds) {
|
|
|
80443
80756
|
if (unknownEvidenceIds.length > 0) {
|
|
80444
80757
|
throw new BenchmarkPhaseError("invalid_criterion_result", "criterion result cites evidence outside the evaluator plan", { unknown_evidence_ids: unknownEvidenceIds });
|
|
80445
80758
|
}
|
|
80446
|
-
return criterionKeys.map((key2) =>
|
|
80759
|
+
return criterionKeys.map((key2) => {
|
|
80760
|
+
const criterion = byKey.get(key2);
|
|
80761
|
+
return {
|
|
80762
|
+
...criterion,
|
|
80763
|
+
explanation: redactCommandOutput(Buffer.from(criterion.explanation), spec, additionalSecrets).toString("utf8")
|
|
80764
|
+
};
|
|
80765
|
+
});
|
|
80447
80766
|
} finally {
|
|
80448
80767
|
fs81.closeSync(opened.fd);
|
|
80449
80768
|
}
|
|
@@ -80466,7 +80785,7 @@ function eventType(event) {
|
|
|
80466
80785
|
}
|
|
80467
80786
|
return;
|
|
80468
80787
|
}
|
|
80469
|
-
async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, frozenEvidence, context) {
|
|
80788
|
+
async function evaluateOne(evaluator, spec, manifest, frozenWorkspaceRoot, finalOutput, trajectory, frozenEvidence, context) {
|
|
80470
80789
|
const started = Date.now();
|
|
80471
80790
|
const base2 = {
|
|
80472
80791
|
id: evaluator.id,
|
|
@@ -80501,16 +80820,33 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
|
|
|
80501
80820
|
cwd: ".",
|
|
80502
80821
|
secret_env: []
|
|
80503
80822
|
}, spec.tests_root, spec, context, {
|
|
80504
|
-
|
|
80505
|
-
|
|
80506
|
-
|
|
80823
|
+
additions: {
|
|
80824
|
+
BB_REGEX_INPUT: frozenEvidence.finalOutputPath,
|
|
80825
|
+
BB_REGEX_PATTERN_B64: Buffer.from(assertion.pattern).toString("base64"),
|
|
80826
|
+
BB_REGEX_FLAGS: assertion.flags
|
|
80827
|
+
},
|
|
80828
|
+
descendantCleanup: "always"
|
|
80507
80829
|
});
|
|
80508
80830
|
if (regexResult.exitCode === 2) {
|
|
80509
80831
|
throw new BenchmarkPhaseError("invalid_evaluator", `invalid regex in evaluator: ${evaluator.id}`);
|
|
80510
80832
|
}
|
|
80511
80833
|
verdict = regexResult.exitCode === 0;
|
|
80512
80834
|
}
|
|
80513
|
-
return {
|
|
80835
|
+
return {
|
|
80836
|
+
...base2,
|
|
80837
|
+
status: verdict ? "passed" : "failed",
|
|
80838
|
+
verdict,
|
|
80839
|
+
duration_ms: Date.now() - started,
|
|
80840
|
+
details: {
|
|
80841
|
+
operator: assertion.operator,
|
|
80842
|
+
actual_size_bytes: Buffer.byteLength(finalOutput),
|
|
80843
|
+
...assertion.operator === "exact" ? {
|
|
80844
|
+
expected_size_bytes: Buffer.byteLength(assertion.expected),
|
|
80845
|
+
actual_sha256: sha256(finalOutput),
|
|
80846
|
+
expected_sha256: sha256(assertion.expected)
|
|
80847
|
+
} : {}
|
|
80848
|
+
}
|
|
80849
|
+
};
|
|
80514
80850
|
}
|
|
80515
80851
|
if (evaluator.type === "trajectory_assertion") {
|
|
80516
80852
|
const count = trajectory.filter((event) => eventType(event) === evaluator.event_type).length;
|
|
@@ -80525,8 +80861,8 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
|
|
|
80525
80861
|
}
|
|
80526
80862
|
if (evaluator.type === "workspace_assertion") {
|
|
80527
80863
|
const relative = workspaceRel(evaluator.path);
|
|
80528
|
-
assertNoSymlinkTraversal(
|
|
80529
|
-
const candidate = path88.resolve(
|
|
80864
|
+
assertNoSymlinkTraversal(frozenWorkspaceRoot, relative);
|
|
80865
|
+
const candidate = path88.resolve(frozenWorkspaceRoot, relative);
|
|
80530
80866
|
let stat = null;
|
|
80531
80867
|
try {
|
|
80532
80868
|
stat = fs81.lstatSync(candidate);
|
|
@@ -80540,15 +80876,17 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
|
|
|
80540
80876
|
}
|
|
80541
80877
|
const exists2 = stat !== null;
|
|
80542
80878
|
let verdict = false;
|
|
80879
|
+
let actualSha256;
|
|
80543
80880
|
if (evaluator.assertion.operator === "exists")
|
|
80544
80881
|
verdict = exists2;
|
|
80545
80882
|
if (evaluator.assertion.operator === "not_exists")
|
|
80546
80883
|
verdict = !exists2;
|
|
80547
80884
|
if (evaluator.assertion.operator === "sha256") {
|
|
80548
80885
|
if (stat?.isFile()) {
|
|
80549
|
-
const opened = openRegularFileNoFollow(candidate, `workspace assertion ${relative}`,
|
|
80886
|
+
const opened = openRegularFileNoFollow(candidate, `workspace assertion ${relative}`, frozenWorkspaceRoot);
|
|
80550
80887
|
try {
|
|
80551
|
-
|
|
80888
|
+
actualSha256 = await sha256OfDescriptor(opened.fd);
|
|
80889
|
+
verdict = actualSha256 === evaluator.assertion.expected;
|
|
80552
80890
|
} finally {
|
|
80553
80891
|
fs81.closeSync(opened.fd);
|
|
80554
80892
|
}
|
|
@@ -80556,7 +80894,7 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
|
|
|
80556
80894
|
}
|
|
80557
80895
|
if (evaluator.assertion.operator === "contains") {
|
|
80558
80896
|
if (stat?.isFile()) {
|
|
80559
|
-
const opened = openRegularFileNoFollow(candidate, `workspace assertion ${relative}`,
|
|
80897
|
+
const opened = openRegularFileNoFollow(candidate, `workspace assertion ${relative}`, frozenWorkspaceRoot);
|
|
80560
80898
|
try {
|
|
80561
80899
|
verdict = readDescriptor(opened.fd).toString("utf8").includes(evaluator.assertion.expected);
|
|
80562
80900
|
} finally {
|
|
@@ -80564,15 +80902,36 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
|
|
|
80564
80902
|
}
|
|
80565
80903
|
}
|
|
80566
80904
|
}
|
|
80567
|
-
return {
|
|
80905
|
+
return {
|
|
80906
|
+
...base2,
|
|
80907
|
+
status: verdict ? "passed" : "failed",
|
|
80908
|
+
verdict,
|
|
80909
|
+
duration_ms: Date.now() - started,
|
|
80910
|
+
details: {
|
|
80911
|
+
operator: evaluator.assertion.operator,
|
|
80912
|
+
path: relative,
|
|
80913
|
+
exists: exists2,
|
|
80914
|
+
...stat ? {
|
|
80915
|
+
actual_kind: stat.isFile() ? "file" : stat.isDirectory() ? "directory" : "other",
|
|
80916
|
+
actual_size_bytes: stat.size
|
|
80917
|
+
} : {},
|
|
80918
|
+
...actualSha256 ? { actual_sha256: actualSha256 } : {}
|
|
80919
|
+
}
|
|
80920
|
+
};
|
|
80568
80921
|
}
|
|
80569
80922
|
let isolatedWorkspace;
|
|
80923
|
+
let isolatedTestsRoot;
|
|
80570
80924
|
let privateResultRoot;
|
|
80925
|
+
const usesLiveWorkspace = evaluator.workspace_mode === "read_only";
|
|
80571
80926
|
try {
|
|
80572
|
-
const evaluatorWorkspace = await copyFrozenWorkspace(manifest, spec, context);
|
|
80573
|
-
|
|
80574
|
-
|
|
80575
|
-
|
|
80927
|
+
const evaluatorWorkspace = usesLiveWorkspace ? spec.workspace_root : await copyFrozenWorkspace(manifest, spec, context, frozenWorkspaceRoot, "contained_relative_only");
|
|
80928
|
+
if (!usesLiveWorkspace)
|
|
80929
|
+
isolatedWorkspace = evaluatorWorkspace;
|
|
80930
|
+
else
|
|
80931
|
+
await verifyWorkspaceManifestUnchanged(manifest, spec, context);
|
|
80932
|
+
isolatedTestsRoot = await copyEvaluatorTests(spec, evaluator, context);
|
|
80933
|
+
const testsPath = evaluatorTestsPath(evaluator, isolatedTestsRoot);
|
|
80934
|
+
const commandRoot = evaluator.root === "workspace" ? evaluatorWorkspace : isolatedTestsRoot;
|
|
80576
80935
|
const command = { id: evaluator.id, ...evaluator.command };
|
|
80577
80936
|
const environment = {
|
|
80578
80937
|
BRAINBASE_BENCHMARK_WORKSPACE: evaluatorWorkspace,
|
|
@@ -80589,14 +80948,23 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
|
|
|
80589
80948
|
criterionResultPath = path88.join(privateResultRoot, "result.json");
|
|
80590
80949
|
environment.BRAINBASE_BENCHMARK_CRITERIA_RESULT = criterionResultPath;
|
|
80591
80950
|
}
|
|
80592
|
-
const result2 = await runCommand(command, commandRoot, spec, context,
|
|
80951
|
+
const result2 = await runCommand(command, commandRoot, spec, context, {
|
|
80952
|
+
additions: environment,
|
|
80953
|
+
descendantCleanup: "always"
|
|
80954
|
+
});
|
|
80593
80955
|
const stdout = await writeLog(spec.logs_root, `${evaluator.id}.stdout.log`, result2.stdout, spec);
|
|
80594
80956
|
const stderr = await writeLog(spec.logs_root, `${evaluator.id}.stderr.log`, result2.stderr, spec);
|
|
80595
80957
|
let criterionResults;
|
|
80596
80958
|
try {
|
|
80597
|
-
criterionResults = evaluator.criterion_keys && criterionResultPath ? readCriterionResults(criterionResultPath, evaluator.criterion_keys, evaluator.allowed_evidence_ids ?? []) : undefined;
|
|
80959
|
+
criterionResults = evaluator.criterion_keys && criterionResultPath ? readCriterionResults(criterionResultPath, evaluator.criterion_keys, evaluator.allowed_evidence_ids ?? [], spec, result2.redactions) : undefined;
|
|
80598
80960
|
} catch (error2) {
|
|
80599
|
-
|
|
80961
|
+
let normalized = stableError(error2);
|
|
80962
|
+
if (normalized?.code === "missing_criterion_result" && result2.exitCode !== 0) {
|
|
80963
|
+
normalized = structuredJudgeError(result2.stderr) ?? {
|
|
80964
|
+
code: "command_terminated",
|
|
80965
|
+
message: "sandbox evaluator terminated before writing its criterion result"
|
|
80966
|
+
};
|
|
80967
|
+
}
|
|
80600
80968
|
return {
|
|
80601
80969
|
...base2,
|
|
80602
80970
|
status: "errored",
|
|
@@ -80623,11 +80991,17 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
|
|
|
80623
80991
|
...criterionResults ? { criterion_results: criterionResults } : {}
|
|
80624
80992
|
};
|
|
80625
80993
|
} finally {
|
|
80626
|
-
|
|
80627
|
-
if (
|
|
80628
|
-
|
|
80629
|
-
|
|
80630
|
-
|
|
80994
|
+
try {
|
|
80995
|
+
if (usesLiveWorkspace) {
|
|
80996
|
+
await verifyWorkspaceManifestUnchanged(manifest, spec, context);
|
|
80997
|
+
}
|
|
80998
|
+
} finally {
|
|
80999
|
+
for (const temporary of [privateResultRoot, isolatedTestsRoot, isolatedWorkspace]) {
|
|
81000
|
+
if (!temporary)
|
|
81001
|
+
continue;
|
|
81002
|
+
fs81.rmSync(temporary, { recursive: true, force: true });
|
|
81003
|
+
context.temporaryRoots.delete(temporary);
|
|
81004
|
+
}
|
|
80631
81005
|
}
|
|
80632
81006
|
}
|
|
80633
81007
|
}
|
|
@@ -80672,6 +81046,7 @@ async function executeEvaluate(spec, context) {
|
|
|
80672
81046
|
];
|
|
80673
81047
|
outputs.push(...frozenEvidenceRecords);
|
|
80674
81048
|
context.outputs.push(...frozenEvidenceRecords);
|
|
81049
|
+
assertEvaluateOutputBudget(spec, context);
|
|
80675
81050
|
assertBudget(context);
|
|
80676
81051
|
const manifest = await workspaceManifest(spec, context);
|
|
80677
81052
|
const manifestPath2 = path88.join(spec.logs_root, "candidate-workspace-manifest.json");
|
|
@@ -80684,6 +81059,7 @@ async function executeEvaluate(spec, context) {
|
|
|
80684
81059
|
const manifestRecord = await recordFile(spec.logs_root, manifestPath2, "logs");
|
|
80685
81060
|
outputs.push(manifestRecord);
|
|
80686
81061
|
context.outputs.push(manifestRecord);
|
|
81062
|
+
assertEvaluateOutputBudget(spec, context);
|
|
80687
81063
|
for (const artifactRelInput of spec.candidate_artifacts) {
|
|
80688
81064
|
const artifactRel = workspaceRel(artifactRelInput);
|
|
80689
81065
|
assertNoSymlinkTraversal(spec.workspace_root, artifactRel);
|
|
@@ -80701,11 +81077,13 @@ async function executeEvaluate(spec, context) {
|
|
|
80701
81077
|
outputs.push(artifact);
|
|
80702
81078
|
context.outputs.push(artifact);
|
|
80703
81079
|
}
|
|
81080
|
+
assertEvaluateOutputBudget(spec, context);
|
|
80704
81081
|
for (const candidateOutput of spec.candidate_outputs) {
|
|
80705
81082
|
const copied = await copyCandidateOutput(candidateOutput, manifest, spec, context);
|
|
80706
81083
|
outputs.push(...copied);
|
|
80707
81084
|
context.outputs.push(...copied);
|
|
80708
81085
|
}
|
|
81086
|
+
assertEvaluateOutputBudget(spec, context);
|
|
80709
81087
|
if (spec.capture_workspace_archive) {
|
|
80710
81088
|
const regularFiles = manifest.filter((entry) => entry.kind !== "symlink").map((entry) => entry.path);
|
|
80711
81089
|
const archive = path88.join(spec.logs_root, "candidate-workspace.tar.gz");
|
|
@@ -80719,23 +81097,23 @@ async function executeEvaluate(spec, context) {
|
|
|
80719
81097
|
const archiveRecord = await recordFile(spec.logs_root, archive, "logs");
|
|
80720
81098
|
outputs.push(archiveRecord);
|
|
80721
81099
|
context.outputs.push(archiveRecord);
|
|
81100
|
+
assertEvaluateOutputBudget(spec, context);
|
|
80722
81101
|
}
|
|
80723
81102
|
await verifyRecordsUnchanged(manifest, spec);
|
|
81103
|
+
const frozenWorkspaceRoot = await copyFrozenWorkspace(manifest, spec, context, spec.workspace_root);
|
|
80724
81104
|
for (const reference of spec.references) {
|
|
80725
81105
|
const referenceOutputs = await copyMaterial(reference, spec.staging_root, spec.tests_root, "tests", false, context);
|
|
80726
81106
|
outputs.push(...referenceOutputs);
|
|
80727
81107
|
context.outputs.push(...referenceOutputs);
|
|
80728
81108
|
}
|
|
80729
|
-
const frozenOutputCount = context.outputs.length;
|
|
80730
81109
|
const evaluators = [];
|
|
80731
81110
|
const evaluatorOrder = new Map(spec.evaluators.map((evaluator, index) => [evaluator.id, index]));
|
|
80732
81111
|
const executionOrder = [...spec.evaluators].sort((left, right) => Number(left.type === "sandbox_command") - Number(right.type === "sandbox_command"));
|
|
80733
81112
|
for (const evaluator of executionOrder) {
|
|
80734
81113
|
assertBudget(context);
|
|
80735
81114
|
const started = Date.now();
|
|
80736
|
-
let requiredResultError;
|
|
80737
81115
|
try {
|
|
80738
|
-
const evaluated = await evaluateOne(evaluator, spec, manifest, finalOutput.buffer.toString("utf8"), trajectory, { finalOutputPath, trajectoryPath }, context);
|
|
81116
|
+
const evaluated = await evaluateOne(evaluator, spec, manifest, frozenWorkspaceRoot, finalOutput.buffer.toString("utf8"), trajectory, { finalOutputPath, trajectoryPath }, context);
|
|
80739
81117
|
assertBudget(context);
|
|
80740
81118
|
evaluators.push(evaluated);
|
|
80741
81119
|
context.evaluators.push(evaluated);
|
|
@@ -80747,13 +81125,14 @@ async function executeEvaluate(spec, context) {
|
|
|
80747
81125
|
outputs.push(evaluated.stderr);
|
|
80748
81126
|
context.outputs.push(evaluated.stderr);
|
|
80749
81127
|
}
|
|
80750
|
-
if (evaluated.status === "errored" && evaluator.required) {
|
|
80751
|
-
const errorCode = typeof evaluated.details?.error_code === "string" ? evaluated.details.error_code : "evaluator_execution_failed";
|
|
80752
|
-
const errorMessage2 = typeof evaluated.details?.error_message === "string" ? evaluated.details.error_message : "required evaluator execution failed";
|
|
80753
|
-
requiredResultError = new BenchmarkPhaseError(errorCode, errorMessage2);
|
|
80754
|
-
}
|
|
80755
81128
|
} catch (error2) {
|
|
80756
81129
|
const normalized = stableError(error2);
|
|
81130
|
+
let stdout;
|
|
81131
|
+
let stderr;
|
|
81132
|
+
if (error2 instanceof BenchmarkCommandExecutionError) {
|
|
81133
|
+
stdout = await writeLog(spec.logs_root, `${evaluator.id}.stdout.log`, error2.stdout, spec);
|
|
81134
|
+
stderr = await writeLog(spec.logs_root, `${evaluator.id}.stderr.log`, error2.stderr, spec);
|
|
81135
|
+
}
|
|
80757
81136
|
const errored = {
|
|
80758
81137
|
id: evaluator.id,
|
|
80759
81138
|
type: evaluator.type,
|
|
@@ -80763,26 +81142,38 @@ async function executeEvaluate(spec, context) {
|
|
|
80763
81142
|
verdict: null,
|
|
80764
81143
|
engine: "brainbase-cli",
|
|
80765
81144
|
engine_version: VERSION,
|
|
80766
|
-
duration_ms: Date.now() - started,
|
|
81145
|
+
duration_ms: error2 instanceof BenchmarkCommandExecutionError ? error2.durationMs : Date.now() - started,
|
|
80767
81146
|
details: {
|
|
80768
81147
|
error_code: normalized?.code ?? "phase_failed",
|
|
80769
81148
|
error_message: normalized?.message ?? "evaluator execution failed"
|
|
80770
|
-
}
|
|
81149
|
+
},
|
|
81150
|
+
...stdout ? { stdout } : {},
|
|
81151
|
+
...stderr ? { stderr } : {}
|
|
80771
81152
|
};
|
|
80772
81153
|
evaluators.push(errored);
|
|
80773
81154
|
context.evaluators.push(errored);
|
|
80774
|
-
if (
|
|
81155
|
+
if (stdout) {
|
|
81156
|
+
outputs.push(stdout);
|
|
81157
|
+
context.outputs.push(stdout);
|
|
81158
|
+
}
|
|
81159
|
+
if (stderr) {
|
|
81160
|
+
outputs.push(stderr);
|
|
81161
|
+
context.outputs.push(stderr);
|
|
81162
|
+
}
|
|
81163
|
+
if (!(error2 instanceof BenchmarkCommandExecutionError))
|
|
80775
81164
|
throw error2;
|
|
80776
81165
|
assertBudget(context);
|
|
80777
81166
|
}
|
|
80778
|
-
if (
|
|
80779
|
-
|
|
81167
|
+
if (evaluator.type === "sandbox_command") {
|
|
81168
|
+
assertEvaluateOutputBudget(spec, context);
|
|
81169
|
+
}
|
|
80780
81170
|
}
|
|
80781
|
-
await verifyRecordsUnchanged([...context.inputs, ...context.outputs
|
|
81171
|
+
await verifyRecordsUnchanged([...context.inputs, ...context.outputs], spec);
|
|
80782
81172
|
if (!verifyOwnedDirectory(spec.tests_root, "tests", spec) || !verifyOwnedDirectory(spec.logs_root, "logs", spec)) {
|
|
80783
81173
|
throw new BenchmarkPhaseError("evidence_tampered", "benchmark evaluator changed an owned phase directory marker");
|
|
80784
81174
|
}
|
|
80785
81175
|
assertBudget(context);
|
|
81176
|
+
assertEvaluateOutputBudget(spec, context);
|
|
80786
81177
|
evaluators.sort((left, right) => evaluatorOrder.get(left.id) - evaluatorOrder.get(right.id));
|
|
80787
81178
|
return { outputs, evaluators };
|
|
80788
81179
|
}
|
|
@@ -80809,6 +81200,67 @@ function stableError(error2) {
|
|
|
80809
81200
|
message: error2 instanceof Error ? error2.message : "unknown benchmark phase failure"
|
|
80810
81201
|
};
|
|
80811
81202
|
}
|
|
81203
|
+
function compactOversizedResult(result2) {
|
|
81204
|
+
const actualBytes = Buffer.byteLength(`${JSON.stringify(result2, null, 2)}
|
|
81205
|
+
`);
|
|
81206
|
+
if (actualBytes <= MAX_PHASE_RESULT_BYTES)
|
|
81207
|
+
return result2;
|
|
81208
|
+
return {
|
|
81209
|
+
schema_version: result2.schema_version,
|
|
81210
|
+
cli_version: result2.cli_version,
|
|
81211
|
+
phase: result2.phase,
|
|
81212
|
+
attempt_id: result2.attempt_id,
|
|
81213
|
+
phase_id: result2.phase_id,
|
|
81214
|
+
spec_digest: result2.spec_digest,
|
|
81215
|
+
status: "failed",
|
|
81216
|
+
started_at: result2.started_at,
|
|
81217
|
+
completed_at: result2.completed_at,
|
|
81218
|
+
duration_ms: result2.duration_ms,
|
|
81219
|
+
steps: [],
|
|
81220
|
+
inputs: [],
|
|
81221
|
+
outputs: [],
|
|
81222
|
+
error: {
|
|
81223
|
+
code: "result_too_large",
|
|
81224
|
+
message: "benchmark phase result exceeds the 32 MiB contract limit",
|
|
81225
|
+
details: {
|
|
81226
|
+
actual_bytes: actualBytes,
|
|
81227
|
+
max_bytes: MAX_PHASE_RESULT_BYTES,
|
|
81228
|
+
step_count: result2.steps.length,
|
|
81229
|
+
input_count: result2.inputs.length,
|
|
81230
|
+
output_count: result2.outputs.length,
|
|
81231
|
+
evaluator_count: result2.evaluators?.length ?? 0
|
|
81232
|
+
}
|
|
81233
|
+
}
|
|
81234
|
+
};
|
|
81235
|
+
}
|
|
81236
|
+
function readCachedPhaseResult(resultPath) {
|
|
81237
|
+
let opened;
|
|
81238
|
+
try {
|
|
81239
|
+
opened = openRegularFileNoFollow(resultPath, "cached benchmark phase result");
|
|
81240
|
+
} catch {
|
|
81241
|
+
return;
|
|
81242
|
+
}
|
|
81243
|
+
try {
|
|
81244
|
+
if (opened.stat.size > MAX_PHASE_RESULT_BYTES)
|
|
81245
|
+
return;
|
|
81246
|
+
const chunks = [];
|
|
81247
|
+
let offset = 0;
|
|
81248
|
+
while (offset <= MAX_PHASE_RESULT_BYTES) {
|
|
81249
|
+
const buffer = Buffer.alloc(Math.min(64 * 1024, MAX_PHASE_RESULT_BYTES + 1 - offset));
|
|
81250
|
+
const bytesRead = fs81.readSync(opened.fd, buffer, 0, buffer.length, offset);
|
|
81251
|
+
if (bytesRead === 0) {
|
|
81252
|
+
return JSON.parse(Buffer.concat(chunks, offset).toString("utf8"));
|
|
81253
|
+
}
|
|
81254
|
+
chunks.push(buffer.subarray(0, bytesRead));
|
|
81255
|
+
offset += bytesRead;
|
|
81256
|
+
}
|
|
81257
|
+
return;
|
|
81258
|
+
} catch {
|
|
81259
|
+
return;
|
|
81260
|
+
} finally {
|
|
81261
|
+
fs81.closeSync(opened.fd);
|
|
81262
|
+
}
|
|
81263
|
+
}
|
|
80812
81264
|
function rawIdentity(value) {
|
|
80813
81265
|
if (!value || typeof value !== "object") {
|
|
80814
81266
|
return { phase: "unknown", attemptId: null, phaseId: null };
|
|
@@ -80881,13 +81333,9 @@ function prepareBenchmarkPhaseInvocation(specPathInput, resultPathInput, expecte
|
|
|
80881
81333
|
timeout_ms: spec.budget.timeout_ms
|
|
80882
81334
|
};
|
|
80883
81335
|
let cachedResult;
|
|
80884
|
-
|
|
80885
|
-
|
|
80886
|
-
|
|
80887
|
-
if (cached2.schema_version === SCHEMA_VERSION && cached2.phase === spec.phase && cached2.attempt_id === spec.attempt_id && cached2.phase_id === spec.phase_id && cached2.spec_digest === digest && cached2.status === "succeeded") {
|
|
80888
|
-
cachedResult = cached2;
|
|
80889
|
-
}
|
|
80890
|
-
} catch {}
|
|
81336
|
+
const cached2 = readCachedPhaseResult(resultPath);
|
|
81337
|
+
if (cached2?.schema_version === SCHEMA_VERSION && cached2.phase === spec.phase && cached2.attempt_id === spec.attempt_id && cached2.phase_id === spec.phase_id && cached2.spec_digest === digest && cached2.status === "succeeded") {
|
|
81338
|
+
cachedResult = cached2;
|
|
80891
81339
|
}
|
|
80892
81340
|
if (cachedResult) {
|
|
80893
81341
|
if (spec.phase === "hydrate") {
|
|
@@ -80904,7 +81352,7 @@ function prepareBenchmarkPhaseInvocation(specPathInput, resultPathInput, expecte
|
|
|
80904
81352
|
} catch (error2) {
|
|
80905
81353
|
return {
|
|
80906
81354
|
ok: false,
|
|
80907
|
-
result: {
|
|
81355
|
+
result: compactOversizedResult({
|
|
80908
81356
|
schema_version: SCHEMA_VERSION,
|
|
80909
81357
|
cli_version: VERSION,
|
|
80910
81358
|
phase: identity2.phase === "unknown" ? expectedPhase : identity2.phase,
|
|
@@ -80919,7 +81367,7 @@ function prepareBenchmarkPhaseInvocation(specPathInput, resultPathInput, expecte
|
|
|
80919
81367
|
inputs: [],
|
|
80920
81368
|
outputs: [],
|
|
80921
81369
|
error: stableError(error2)
|
|
80922
|
-
}
|
|
81370
|
+
})
|
|
80923
81371
|
};
|
|
80924
81372
|
}
|
|
80925
81373
|
}
|
|
@@ -80968,6 +81416,7 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
80968
81416
|
let status = "failed";
|
|
80969
81417
|
let error2;
|
|
80970
81418
|
let context;
|
|
81419
|
+
let validatedSpec;
|
|
80971
81420
|
let resultPathValidated = false;
|
|
80972
81421
|
try {
|
|
80973
81422
|
let bytes;
|
|
@@ -80987,6 +81436,7 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
80987
81436
|
}
|
|
80988
81437
|
identity2 = rawIdentity(raw);
|
|
80989
81438
|
const spec = BenchmarkSpecSchema.parse(raw);
|
|
81439
|
+
validatedSpec = spec;
|
|
80990
81440
|
if (expectedPhase && spec.phase !== expectedPhase) {
|
|
80991
81441
|
throw new BenchmarkPhaseError("phase_mismatch", `the ${expectedPhase} command cannot execute a ${spec.phase} spec`);
|
|
80992
81442
|
}
|
|
@@ -80997,13 +81447,9 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
80997
81447
|
}
|
|
80998
81448
|
resultPathValidated = true;
|
|
80999
81449
|
let cachedResult;
|
|
81000
|
-
|
|
81001
|
-
|
|
81002
|
-
|
|
81003
|
-
if (cached2.schema_version === SCHEMA_VERSION && cached2.phase === spec.phase && cached2.attempt_id === spec.attempt_id && cached2.phase_id === spec.phase_id && cached2.spec_digest === digest && cached2.status === "succeeded") {
|
|
81004
|
-
cachedResult = cached2;
|
|
81005
|
-
}
|
|
81006
|
-
} catch {}
|
|
81450
|
+
const cached2 = readCachedPhaseResult(resultPath);
|
|
81451
|
+
if (cached2?.schema_version === SCHEMA_VERSION && cached2.phase === spec.phase && cached2.attempt_id === spec.attempt_id && cached2.phase_id === spec.phase_id && cached2.spec_digest === digest && cached2.status === "succeeded") {
|
|
81452
|
+
cachedResult = cached2;
|
|
81007
81453
|
}
|
|
81008
81454
|
if (cachedResult) {
|
|
81009
81455
|
if (spec.phase === "hydrate") {
|
|
@@ -81046,7 +81492,7 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
81046
81492
|
fs81.rmSync(temporary, { recursive: true, force: true });
|
|
81047
81493
|
}
|
|
81048
81494
|
}
|
|
81049
|
-
|
|
81495
|
+
let result2 = {
|
|
81050
81496
|
schema_version: SCHEMA_VERSION,
|
|
81051
81497
|
cli_version: VERSION,
|
|
81052
81498
|
phase: identity2.phase === "unknown" && expectedPhase ? expectedPhase : identity2.phase,
|
|
@@ -81064,8 +81510,22 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
81064
81510
|
...error2 ? { error: error2 } : {}
|
|
81065
81511
|
};
|
|
81066
81512
|
if (!resultPathValidated || !context?.logsOwned) {
|
|
81513
|
+
result2 = compactOversizedResult(result2);
|
|
81067
81514
|
return { exitCode: status === "succeeded" ? 0 : 1, result: result2 };
|
|
81068
81515
|
}
|
|
81516
|
+
if (status === "succeeded" && validatedSpec?.phase === "evaluate") {
|
|
81517
|
+
try {
|
|
81518
|
+
const resultBytes = Buffer.byteLength(`${JSON.stringify(result2, null, 2)}
|
|
81519
|
+
`);
|
|
81520
|
+
assertEvaluateOutputBudget(validatedSpec, context, resultBytes);
|
|
81521
|
+
} catch (budgetError) {
|
|
81522
|
+
status = "failed";
|
|
81523
|
+
result2.status = "failed";
|
|
81524
|
+
result2.error = stableError(budgetError);
|
|
81525
|
+
}
|
|
81526
|
+
}
|
|
81527
|
+
result2 = compactOversizedResult(result2);
|
|
81528
|
+
status = result2.status;
|
|
81069
81529
|
try {
|
|
81070
81530
|
writeJsonAtomic(resultPath, result2);
|
|
81071
81531
|
} catch (writeError) {
|