@brainbase-labs/cli 0.25.0-eng1209.8 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -7
- package/dist/index.js +195 -73
- 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
|
|
36011
|
+
version: "0.25.0",
|
|
36012
36012
|
description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
|
|
36013
36013
|
type: "module",
|
|
36014
36014
|
bin: {
|
|
@@ -78485,9 +78485,12 @@ 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
78491
|
var MAX_CRITERIA_PER_EVALUATOR = 200;
|
|
78490
|
-
var MAX_CRITERIA_RESULT_BYTES = 1024 * 1024;
|
|
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;
|
|
@@ -78495,6 +78498,7 @@ var MAX_ALLOWED_EVIDENCE_IDS = 1e4;
|
|
|
78495
78498
|
var COMMAND_PROCESS_MARKER_ENV = "BRAINBASE_BENCHMARK_COMMAND_MARKER";
|
|
78496
78499
|
var COMMAND_PROCESS_POLL_MS = 10;
|
|
78497
78500
|
var COMMAND_PROCESS_CLEANUP_MS = 500;
|
|
78501
|
+
var JUDGE_ERROR_PREFIX = "BRAINBASE_BENCHMARK_JUDGE_ERROR_V1:";
|
|
78498
78502
|
var RESERVED_WORKSPACE_PATHS = new Set([
|
|
78499
78503
|
".brainbase",
|
|
78500
78504
|
".git",
|
|
@@ -78661,7 +78665,7 @@ var TrajectoryEvidenceSchema = EvidenceFileSchema.extend({
|
|
|
78661
78665
|
var HydrateSpecSchema = BaseSpecSchema.extend({
|
|
78662
78666
|
phase: exports_external.literal("hydrate"),
|
|
78663
78667
|
materials: exports_external.array(MaterialSchema).max(1e4).default([]),
|
|
78664
|
-
setup_commands: exports_external.array(CommandSchema).max(
|
|
78668
|
+
setup_commands: exports_external.array(CommandSchema).max(MAX_HYDRATE_COMMANDS).default([])
|
|
78665
78669
|
}).strict();
|
|
78666
78670
|
var CandidateOutputSchema = exports_external.object({
|
|
78667
78671
|
id: IdSchema,
|
|
@@ -78802,12 +78806,12 @@ var BenchmarkSpecSchema = exports_external.discriminatedUnion("phase", [
|
|
|
78802
78806
|
{ items: value.candidate_outputs, path: "candidate_outputs" }
|
|
78803
78807
|
];
|
|
78804
78808
|
for (const { items, path: issuePath } of uniqueLists) {
|
|
78805
|
-
const ids = items.map((item) => item.id);
|
|
78809
|
+
const ids = items.map((item) => issuePath === "candidate_outputs" ? item.id.toLowerCase() : item.id);
|
|
78806
78810
|
if (new Set(ids).size !== ids.length) {
|
|
78807
78811
|
context.addIssue({
|
|
78808
78812
|
code: exports_external.ZodIssueCode.custom,
|
|
78809
78813
|
path: [issuePath],
|
|
78810
|
-
message: `${issuePath} ids must be unique`
|
|
78814
|
+
message: `${issuePath} ids must be unique${issuePath === "candidate_outputs" ? " ignoring case" : ""}`
|
|
78811
78815
|
});
|
|
78812
78816
|
}
|
|
78813
78817
|
}
|
|
@@ -78870,6 +78874,7 @@ var BENCHMARK_CAPABILITIES = {
|
|
|
78870
78874
|
"remote_input_references_v1",
|
|
78871
78875
|
"archive_file_materials_v1",
|
|
78872
78876
|
"structured_criterion_results_v1",
|
|
78877
|
+
"structured_judge_errors_v1",
|
|
78873
78878
|
"multiple_sandbox_commands_v1",
|
|
78874
78879
|
"sandbox_command_workspace_modes_v1",
|
|
78875
78880
|
"candidate_outputs_v1",
|
|
@@ -78883,6 +78888,8 @@ var BENCHMARK_CAPABILITIES = {
|
|
|
78883
78888
|
],
|
|
78884
78889
|
limits: {
|
|
78885
78890
|
max_secret_bindings: 100,
|
|
78891
|
+
max_hydrate_commands: MAX_HYDRATE_COMMANDS,
|
|
78892
|
+
max_phase_result_bytes: MAX_PHASE_RESULT_BYTES,
|
|
78886
78893
|
max_evaluators: 1000,
|
|
78887
78894
|
max_sandbox_commands: MAX_SANDBOX_COMMANDS,
|
|
78888
78895
|
max_criteria_per_evaluator: MAX_CRITERIA_PER_EVALUATOR
|
|
@@ -80052,6 +80059,18 @@ function markedProcessPids(marker) {
|
|
|
80052
80059
|
return [];
|
|
80053
80060
|
}
|
|
80054
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
|
+
}
|
|
80055
80074
|
try {
|
|
80056
80075
|
process.kill(pid, 0);
|
|
80057
80076
|
return true;
|
|
@@ -80083,7 +80102,7 @@ async function terminateCommandProcesses(child, marker, observedDescendants) {
|
|
|
80083
80102
|
await new Promise((resolve) => setTimeout(resolve, COMMAND_PROCESS_POLL_MS));
|
|
80084
80103
|
}
|
|
80085
80104
|
}
|
|
80086
|
-
async function runCommand(command, root, spec, context,
|
|
80105
|
+
async function runCommand(command, root, spec, context, options = {}) {
|
|
80087
80106
|
const cwdRel = normalizedRootRelative(command.cwd);
|
|
80088
80107
|
assertNoSymlinkTraversal(root, cwdRel);
|
|
80089
80108
|
const cwd2 = path88.resolve(root, cwdRel);
|
|
@@ -80107,7 +80126,7 @@ async function runCommand(command, root, spec, context, additions = {}) {
|
|
|
80107
80126
|
const child = spawn4(command.argv[0], command.argv.slice(1), {
|
|
80108
80127
|
cwd: cwd2,
|
|
80109
80128
|
env: buildEnvironment(spec, command.secret_env, {
|
|
80110
|
-
...additions,
|
|
80129
|
+
...options.additions,
|
|
80111
80130
|
[COMMAND_PROCESS_MARKER_ENV]: commandMarker
|
|
80112
80131
|
}),
|
|
80113
80132
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -80163,13 +80182,23 @@ async function runCommand(command, root, spec, context, additions = {}) {
|
|
|
80163
80182
|
}
|
|
80164
80183
|
target.push(chunk2);
|
|
80165
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
|
+
};
|
|
80166
80200
|
child.stdout?.on("data", (chunk2) => capture(stdout, chunk2));
|
|
80167
80201
|
child.stderr?.on("data", (chunk2) => capture(stderr, chunk2));
|
|
80168
|
-
child.once("exit", () => {
|
|
80169
|
-
if (timer)
|
|
80170
|
-
clearTimeout(timer);
|
|
80171
|
-
cleanupProcesses();
|
|
80172
|
-
});
|
|
80173
80202
|
child.on("error", (error2) => {
|
|
80174
80203
|
fail(new BenchmarkCommandExecutionError("command_start_failed", `failed to start ${command.id}: ${error2.message}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
|
|
80175
80204
|
});
|
|
@@ -80180,24 +80209,21 @@ async function runCommand(command, root, spec, context, additions = {}) {
|
|
|
80180
80209
|
}
|
|
80181
80210
|
fail(new BenchmarkCommandExecutionError("command_timeout", `command timed out: ${command.id}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
|
|
80182
80211
|
}, timeoutMs2);
|
|
80183
|
-
child.on("
|
|
80212
|
+
child.on("exit", (code, signal) => {
|
|
80184
80213
|
if (settled)
|
|
80185
80214
|
return;
|
|
80186
80215
|
settled = true;
|
|
80187
80216
|
clearTimeout(timer);
|
|
80188
|
-
|
|
80189
|
-
|
|
80190
|
-
|
|
80191
|
-
|
|
80192
|
-
|
|
80193
|
-
|
|
80194
|
-
|
|
80195
|
-
|
|
80196
|
-
|
|
80197
|
-
|
|
80198
|
-
redactions: [commandMarker]
|
|
80199
|
-
});
|
|
80200
|
-
}, reject2);
|
|
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);
|
|
80226
|
+
}
|
|
80201
80227
|
});
|
|
80202
80228
|
});
|
|
80203
80229
|
}
|
|
@@ -80275,7 +80301,9 @@ async function executeHydrate(spec, context) {
|
|
|
80275
80301
|
assertBudget(context);
|
|
80276
80302
|
let result2;
|
|
80277
80303
|
try {
|
|
80278
|
-
result2 = await runCommand(command, spec.workspace_root, spec, context
|
|
80304
|
+
result2 = await runCommand(command, spec.workspace_root, spec, context, {
|
|
80305
|
+
descendantCleanup: "failure_only"
|
|
80306
|
+
});
|
|
80279
80307
|
} catch (error2) {
|
|
80280
80308
|
context.steps.push({
|
|
80281
80309
|
id: command.id,
|
|
@@ -80463,6 +80491,8 @@ function manifestDirectories(manifest, context) {
|
|
|
80463
80491
|
const directories = new Set;
|
|
80464
80492
|
for (const entry of manifest) {
|
|
80465
80493
|
assertBudget(context);
|
|
80494
|
+
if (entry.kind === "symlink")
|
|
80495
|
+
continue;
|
|
80466
80496
|
let current = path88.posix.dirname(entry.path);
|
|
80467
80497
|
while (current !== ".") {
|
|
80468
80498
|
assertBudget(context);
|
|
@@ -80480,9 +80510,8 @@ function candidateOutputFiles(output, manifest, context) {
|
|
|
80480
80510
|
assertBudget(context);
|
|
80481
80511
|
if (!matcher.test(entry.path))
|
|
80482
80512
|
continue;
|
|
80483
|
-
if (entry.kind === "symlink")
|
|
80484
|
-
|
|
80485
|
-
}
|
|
80513
|
+
if (entry.kind === "symlink")
|
|
80514
|
+
continue;
|
|
80486
80515
|
matched.push(entry);
|
|
80487
80516
|
}
|
|
80488
80517
|
return {
|
|
@@ -80490,12 +80519,6 @@ function candidateOutputFiles(output, manifest, context) {
|
|
|
80490
80519
|
files: matched
|
|
80491
80520
|
};
|
|
80492
80521
|
}
|
|
80493
|
-
for (const entry of manifest) {
|
|
80494
|
-
assertBudget(context);
|
|
80495
|
-
if (entry.kind === "symlink" && matcher.test(entry.path)) {
|
|
80496
|
-
throw new BenchmarkPhaseError("unsafe_path", `candidate output ${output.id} matches a symlink`);
|
|
80497
|
-
}
|
|
80498
|
-
}
|
|
80499
80522
|
const directories = [];
|
|
80500
80523
|
for (const directory of manifestDirectories(manifest, context)) {
|
|
80501
80524
|
assertBudget(context);
|
|
@@ -80510,9 +80533,8 @@ function candidateOutputFiles(output, manifest, context) {
|
|
|
80510
80533
|
assertBudget(context);
|
|
80511
80534
|
if (!entry.path.startsWith(prefix))
|
|
80512
80535
|
continue;
|
|
80513
|
-
if (entry.kind === "symlink")
|
|
80514
|
-
|
|
80515
|
-
}
|
|
80536
|
+
if (entry.kind === "symlink")
|
|
80537
|
+
continue;
|
|
80516
80538
|
selected.set(entry.path, entry);
|
|
80517
80539
|
}
|
|
80518
80540
|
}
|
|
@@ -80558,7 +80580,7 @@ async function copyCandidateOutput(output, manifest, spec, context) {
|
|
|
80558
80580
|
}
|
|
80559
80581
|
return copied;
|
|
80560
80582
|
}
|
|
80561
|
-
async function copyFrozenWorkspace(manifest, spec, context, sourceRoot = spec.workspace_root,
|
|
80583
|
+
async function copyFrozenWorkspace(manifest, spec, context, sourceRoot = spec.workspace_root, symlinkPolicy = "preserve") {
|
|
80562
80584
|
const destinationRoot = fs81.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-workspace-"));
|
|
80563
80585
|
fs81.chmodSync(destinationRoot, 448);
|
|
80564
80586
|
context.temporaryRoots.add(destinationRoot);
|
|
@@ -80572,12 +80594,9 @@ async function copyFrozenWorkspace(manifest, spec, context, sourceRoot = spec.wo
|
|
|
80572
80594
|
if (target === null || Buffer.byteLength(target) !== frozenFile.size || sha256(target) !== frozenFile.sha256) {
|
|
80573
80595
|
throw new BenchmarkPhaseError("evidence_tampered", `workspace changed after it was frozen: ${frozenFile.path}`);
|
|
80574
80596
|
}
|
|
80575
|
-
if (!preserveUnsafeSymlinks && path88.isAbsolute(target)) {
|
|
80576
|
-
throw new BenchmarkPhaseError("unsafe_workspace_symlink", `sandbox evaluator workspace cannot reproduce an absolute symlink: ${frozenFile.path}`);
|
|
80577
|
-
}
|
|
80578
80597
|
const resolvedTarget = path88.resolve(path88.dirname(source), target);
|
|
80579
|
-
if (
|
|
80580
|
-
|
|
80598
|
+
if (symlinkPolicy === "contained_relative_only" && (path88.isAbsolute(target) || !isWithin(sourceRoot, resolvedTarget))) {
|
|
80599
|
+
continue;
|
|
80581
80600
|
}
|
|
80582
80601
|
fs81.mkdirSync(path88.dirname(destination), { recursive: true, mode: 448 });
|
|
80583
80602
|
fs81.symlinkSync(target, destination);
|
|
@@ -80591,11 +80610,15 @@ async function copyFrozenWorkspace(manifest, spec, context, sourceRoot = spec.wo
|
|
|
80591
80610
|
}
|
|
80592
80611
|
return destinationRoot;
|
|
80593
80612
|
}
|
|
80594
|
-
async function copyEvaluatorTests(spec, context) {
|
|
80613
|
+
async function copyEvaluatorTests(spec, evaluator, context) {
|
|
80595
80614
|
const destinationRoot = fs81.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-tests-"));
|
|
80596
80615
|
fs81.chmodSync(destinationRoot, 448);
|
|
80597
80616
|
context.temporaryRoots.add(destinationRoot);
|
|
80598
|
-
const
|
|
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 }];
|
|
80599
80622
|
while (stack.length > 0) {
|
|
80600
80623
|
assertBudget(context);
|
|
80601
80624
|
const current = stack.pop();
|
|
@@ -80657,7 +80680,38 @@ var CriterionResultSchema = exports_external.object({
|
|
|
80657
80680
|
});
|
|
80658
80681
|
var CriteriaResultFileSchema = exports_external.object({
|
|
80659
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)
|
|
80660
80701
|
}).strict();
|
|
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
|
+
}
|
|
80661
80715
|
function readCriterionResults(resultPath, criterionKeys, allowedEvidenceIds, spec, additionalSecrets = []) {
|
|
80662
80716
|
let opened;
|
|
80663
80717
|
try {
|
|
@@ -80670,7 +80724,7 @@ function readCriterionResults(resultPath, criterionKeys, allowedEvidenceIds, spe
|
|
|
80670
80724
|
}
|
|
80671
80725
|
try {
|
|
80672
80726
|
if (opened.stat.size > MAX_CRITERIA_RESULT_BYTES) {
|
|
80673
|
-
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");
|
|
80674
80728
|
}
|
|
80675
80729
|
let raw;
|
|
80676
80730
|
try {
|
|
@@ -80766,9 +80820,12 @@ async function evaluateOne(evaluator, spec, manifest, frozenWorkspaceRoot, final
|
|
|
80766
80820
|
cwd: ".",
|
|
80767
80821
|
secret_env: []
|
|
80768
80822
|
}, spec.tests_root, spec, context, {
|
|
80769
|
-
|
|
80770
|
-
|
|
80771
|
-
|
|
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"
|
|
80772
80829
|
});
|
|
80773
80830
|
if (regexResult.exitCode === 2) {
|
|
80774
80831
|
throw new BenchmarkPhaseError("invalid_evaluator", `invalid regex in evaluator: ${evaluator.id}`);
|
|
@@ -80867,12 +80924,12 @@ async function evaluateOne(evaluator, spec, manifest, frozenWorkspaceRoot, final
|
|
|
80867
80924
|
let privateResultRoot;
|
|
80868
80925
|
const usesLiveWorkspace = evaluator.workspace_mode === "read_only";
|
|
80869
80926
|
try {
|
|
80870
|
-
const evaluatorWorkspace = usesLiveWorkspace ? spec.workspace_root : await copyFrozenWorkspace(manifest, spec, context, frozenWorkspaceRoot);
|
|
80927
|
+
const evaluatorWorkspace = usesLiveWorkspace ? spec.workspace_root : await copyFrozenWorkspace(manifest, spec, context, frozenWorkspaceRoot, "contained_relative_only");
|
|
80871
80928
|
if (!usesLiveWorkspace)
|
|
80872
80929
|
isolatedWorkspace = evaluatorWorkspace;
|
|
80873
80930
|
else
|
|
80874
80931
|
await verifyWorkspaceManifestUnchanged(manifest, spec, context);
|
|
80875
|
-
isolatedTestsRoot = await copyEvaluatorTests(spec, context);
|
|
80932
|
+
isolatedTestsRoot = await copyEvaluatorTests(spec, evaluator, context);
|
|
80876
80933
|
const testsPath = evaluatorTestsPath(evaluator, isolatedTestsRoot);
|
|
80877
80934
|
const commandRoot = evaluator.root === "workspace" ? evaluatorWorkspace : isolatedTestsRoot;
|
|
80878
80935
|
const command = { id: evaluator.id, ...evaluator.command };
|
|
@@ -80891,14 +80948,23 @@ async function evaluateOne(evaluator, spec, manifest, frozenWorkspaceRoot, final
|
|
|
80891
80948
|
criterionResultPath = path88.join(privateResultRoot, "result.json");
|
|
80892
80949
|
environment.BRAINBASE_BENCHMARK_CRITERIA_RESULT = criterionResultPath;
|
|
80893
80950
|
}
|
|
80894
|
-
const result2 = await runCommand(command, commandRoot, spec, context,
|
|
80951
|
+
const result2 = await runCommand(command, commandRoot, spec, context, {
|
|
80952
|
+
additions: environment,
|
|
80953
|
+
descendantCleanup: "always"
|
|
80954
|
+
});
|
|
80895
80955
|
const stdout = await writeLog(spec.logs_root, `${evaluator.id}.stdout.log`, result2.stdout, spec);
|
|
80896
80956
|
const stderr = await writeLog(spec.logs_root, `${evaluator.id}.stderr.log`, result2.stderr, spec);
|
|
80897
80957
|
let criterionResults;
|
|
80898
80958
|
try {
|
|
80899
80959
|
criterionResults = evaluator.criterion_keys && criterionResultPath ? readCriterionResults(criterionResultPath, evaluator.criterion_keys, evaluator.allowed_evidence_ids ?? [], spec, result2.redactions) : undefined;
|
|
80900
80960
|
} catch (error2) {
|
|
80901
|
-
|
|
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
|
+
}
|
|
80902
80968
|
return {
|
|
80903
80969
|
...base2,
|
|
80904
80970
|
status: "errored",
|
|
@@ -81034,7 +81100,7 @@ async function executeEvaluate(spec, context) {
|
|
|
81034
81100
|
assertEvaluateOutputBudget(spec, context);
|
|
81035
81101
|
}
|
|
81036
81102
|
await verifyRecordsUnchanged(manifest, spec);
|
|
81037
|
-
const frozenWorkspaceRoot = await copyFrozenWorkspace(manifest, spec, context, spec.workspace_root
|
|
81103
|
+
const frozenWorkspaceRoot = await copyFrozenWorkspace(manifest, spec, context, spec.workspace_root);
|
|
81038
81104
|
for (const reference of spec.references) {
|
|
81039
81105
|
const referenceOutputs = await copyMaterial(reference, spec.staging_root, spec.tests_root, "tests", false, context);
|
|
81040
81106
|
outputs.push(...referenceOutputs);
|
|
@@ -81134,6 +81200,67 @@ function stableError(error2) {
|
|
|
81134
81200
|
message: error2 instanceof Error ? error2.message : "unknown benchmark phase failure"
|
|
81135
81201
|
};
|
|
81136
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
|
+
}
|
|
81137
81264
|
function rawIdentity(value) {
|
|
81138
81265
|
if (!value || typeof value !== "object") {
|
|
81139
81266
|
return { phase: "unknown", attemptId: null, phaseId: null };
|
|
@@ -81206,13 +81333,9 @@ function prepareBenchmarkPhaseInvocation(specPathInput, resultPathInput, expecte
|
|
|
81206
81333
|
timeout_ms: spec.budget.timeout_ms
|
|
81207
81334
|
};
|
|
81208
81335
|
let cachedResult;
|
|
81209
|
-
|
|
81210
|
-
|
|
81211
|
-
|
|
81212
|
-
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") {
|
|
81213
|
-
cachedResult = cached2;
|
|
81214
|
-
}
|
|
81215
|
-
} 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;
|
|
81216
81339
|
}
|
|
81217
81340
|
if (cachedResult) {
|
|
81218
81341
|
if (spec.phase === "hydrate") {
|
|
@@ -81229,7 +81352,7 @@ function prepareBenchmarkPhaseInvocation(specPathInput, resultPathInput, expecte
|
|
|
81229
81352
|
} catch (error2) {
|
|
81230
81353
|
return {
|
|
81231
81354
|
ok: false,
|
|
81232
|
-
result: {
|
|
81355
|
+
result: compactOversizedResult({
|
|
81233
81356
|
schema_version: SCHEMA_VERSION,
|
|
81234
81357
|
cli_version: VERSION,
|
|
81235
81358
|
phase: identity2.phase === "unknown" ? expectedPhase : identity2.phase,
|
|
@@ -81244,7 +81367,7 @@ function prepareBenchmarkPhaseInvocation(specPathInput, resultPathInput, expecte
|
|
|
81244
81367
|
inputs: [],
|
|
81245
81368
|
outputs: [],
|
|
81246
81369
|
error: stableError(error2)
|
|
81247
|
-
}
|
|
81370
|
+
})
|
|
81248
81371
|
};
|
|
81249
81372
|
}
|
|
81250
81373
|
}
|
|
@@ -81324,13 +81447,9 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
81324
81447
|
}
|
|
81325
81448
|
resultPathValidated = true;
|
|
81326
81449
|
let cachedResult;
|
|
81327
|
-
|
|
81328
|
-
|
|
81329
|
-
|
|
81330
|
-
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") {
|
|
81331
|
-
cachedResult = cached2;
|
|
81332
|
-
}
|
|
81333
|
-
} 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;
|
|
81334
81453
|
}
|
|
81335
81454
|
if (cachedResult) {
|
|
81336
81455
|
if (spec.phase === "hydrate") {
|
|
@@ -81373,7 +81492,7 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
81373
81492
|
fs81.rmSync(temporary, { recursive: true, force: true });
|
|
81374
81493
|
}
|
|
81375
81494
|
}
|
|
81376
|
-
|
|
81495
|
+
let result2 = {
|
|
81377
81496
|
schema_version: SCHEMA_VERSION,
|
|
81378
81497
|
cli_version: VERSION,
|
|
81379
81498
|
phase: identity2.phase === "unknown" && expectedPhase ? expectedPhase : identity2.phase,
|
|
@@ -81391,6 +81510,7 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
81391
81510
|
...error2 ? { error: error2 } : {}
|
|
81392
81511
|
};
|
|
81393
81512
|
if (!resultPathValidated || !context?.logsOwned) {
|
|
81513
|
+
result2 = compactOversizedResult(result2);
|
|
81394
81514
|
return { exitCode: status === "succeeded" ? 0 : 1, result: result2 };
|
|
81395
81515
|
}
|
|
81396
81516
|
if (status === "succeeded" && validatedSpec?.phase === "evaluate") {
|
|
@@ -81404,6 +81524,8 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
81404
81524
|
result2.error = stableError(budgetError);
|
|
81405
81525
|
}
|
|
81406
81526
|
}
|
|
81527
|
+
result2 = compactOversizedResult(result2);
|
|
81528
|
+
status = result2.status;
|
|
81407
81529
|
try {
|
|
81408
81530
|
writeJsonAtomic(resultPath, result2);
|
|
81409
81531
|
} catch (writeError) {
|