@brainbase-labs/cli 0.25.0-eng1209.6 → 0.25.0-eng1209.8

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.
Files changed (2) hide show
  1. package/dist/index.js +199 -38
  2. package/package.json +1 -1
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.6",
36011
+ version: "0.25.0-eng1209.8",
36012
36012
  description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
36013
36013
  type: "module",
36014
36014
  bin: {
@@ -78492,6 +78492,9 @@ var MAX_CRITERION_EXPLANATION_LENGTH = 16384;
78492
78492
  var MAX_CRITERION_EVIDENCE_IDS = 100;
78493
78493
  var MAX_CRITERION_EVIDENCE_ID_LENGTH = 256;
78494
78494
  var MAX_ALLOWED_EVIDENCE_IDS = 1e4;
78495
+ var COMMAND_PROCESS_MARKER_ENV = "BRAINBASE_BENCHMARK_COMMAND_MARKER";
78496
+ var COMMAND_PROCESS_POLL_MS = 10;
78497
+ var COMMAND_PROCESS_CLEANUP_MS = 500;
78495
78498
  var RESERVED_WORKSPACE_PATHS = new Set([
78496
78499
  ".brainbase",
78497
78500
  ".git",
@@ -78712,6 +78715,20 @@ var BenchmarkSpecSchema = exports_external.discriminatedUnion("phase", [
78712
78715
  HydrateSpecSchema,
78713
78716
  EvaluateSpecSchema
78714
78717
  ]).superRefine((value, context) => {
78718
+ if (Object.hasOwn(value.environment, COMMAND_PROCESS_MARKER_ENV)) {
78719
+ context.addIssue({
78720
+ code: exports_external.ZodIssueCode.custom,
78721
+ path: ["environment", COMMAND_PROCESS_MARKER_ENV],
78722
+ message: `${COMMAND_PROCESS_MARKER_ENV} is reserved for command supervision`
78723
+ });
78724
+ }
78725
+ if (value.secret_env.includes(COMMAND_PROCESS_MARKER_ENV)) {
78726
+ context.addIssue({
78727
+ code: exports_external.ZodIssueCode.custom,
78728
+ path: ["secret_env"],
78729
+ message: `${COMMAND_PROCESS_MARKER_ENV} is reserved for command supervision`
78730
+ });
78731
+ }
78715
78732
  for (const name of Object.keys(value.environment)) {
78716
78733
  if (SENSITIVE_ENV_NAME_RE.test(name)) {
78717
78734
  context.addIssue({
@@ -79920,7 +79937,7 @@ function buildEnvironment(spec, secretNames, additions = {}) {
79920
79937
  Object.assign(env3, additions);
79921
79938
  return env3;
79922
79939
  }
79923
- function redactCommandOutput(data, spec) {
79940
+ function redactCommandOutput(data, spec, additionalSecrets = []) {
79924
79941
  let value = data.toString("utf8");
79925
79942
  const sensitiveNames = new Set([
79926
79943
  ...spec.secret_env,
@@ -79931,6 +79948,10 @@ function redactCommandOutput(data, spec) {
79931
79948
  if (secret)
79932
79949
  value = value.split(secret).join("[REDACTED]");
79933
79950
  }
79951
+ for (const secret of additionalSecrets) {
79952
+ if (secret)
79953
+ value = value.split(secret).join("[REDACTED]");
79954
+ }
79934
79955
  return Buffer.from(value);
79935
79956
  }
79936
79957
  function descendantPids(parentPid) {
@@ -79982,6 +80003,86 @@ function terminate(child) {
79982
80003
  child.kill("SIGKILL");
79983
80004
  }
79984
80005
  }
80006
+ function markedProcessPids(marker) {
80007
+ const assignment = `${COMMAND_PROCESS_MARKER_ENV}=${marker}`;
80008
+ if (process.platform === "linux") {
80009
+ const matches2 = [];
80010
+ let entries;
80011
+ try {
80012
+ entries = fs81.readdirSync("/proc");
80013
+ } catch {
80014
+ return matches2;
80015
+ }
80016
+ for (const entry of entries) {
80017
+ if (!/^\d+$/.test(entry))
80018
+ continue;
80019
+ const pid = Number(entry);
80020
+ if (pid === process.pid)
80021
+ continue;
80022
+ try {
80023
+ const environment = fs81.readFileSync(path88.join("/proc", entry, "environ"), "utf8");
80024
+ if (environment.split("\x00").includes(assignment))
80025
+ matches2.push(pid);
80026
+ } catch {}
80027
+ }
80028
+ return matches2;
80029
+ }
80030
+ if (process.platform === "darwin") {
80031
+ try {
80032
+ const output = execFileSync2("ps", ["eww", "-axo", "pid=,command="], {
80033
+ encoding: "utf8",
80034
+ maxBuffer: 16 * 1024 * 1024,
80035
+ stdio: ["ignore", "pipe", "ignore"]
80036
+ });
80037
+ const matches2 = [];
80038
+ for (const line of output.split(`
80039
+ `)) {
80040
+ const match = line.match(/^\s*(\d+)\s+(.*)$/);
80041
+ if (!match || !match[2].includes(assignment))
80042
+ continue;
80043
+ const pid = Number(match[1]);
80044
+ if (Number.isInteger(pid) && pid !== process.pid)
80045
+ matches2.push(pid);
80046
+ }
80047
+ return matches2;
80048
+ } catch {
80049
+ return [];
80050
+ }
80051
+ }
80052
+ return [];
80053
+ }
80054
+ function processExists(pid) {
80055
+ try {
80056
+ process.kill(pid, 0);
80057
+ return true;
80058
+ } catch (error2) {
80059
+ return error2.code !== "ESRCH";
80060
+ }
80061
+ }
80062
+ async function terminateCommandProcesses(child, marker, observedDescendants) {
80063
+ if (child.pid !== undefined) {
80064
+ for (const pid of descendantPids(child.pid))
80065
+ observedDescendants.add(pid);
80066
+ }
80067
+ terminate(child);
80068
+ const deadline = Date.now() + COMMAND_PROCESS_CLEANUP_MS;
80069
+ while (true) {
80070
+ for (const pid of markedProcessPids(marker))
80071
+ observedDescendants.add(pid);
80072
+ const active = [...observedDescendants].filter(processExists);
80073
+ for (const pid of active) {
80074
+ try {
80075
+ process.kill(pid, "SIGKILL");
80076
+ } catch {}
80077
+ }
80078
+ if (active.length === 0)
80079
+ return;
80080
+ if (Date.now() >= deadline) {
80081
+ throw new BenchmarkPhaseError("command_cleanup_failed", "command descendants remained after bounded cleanup");
80082
+ }
80083
+ await new Promise((resolve) => setTimeout(resolve, COMMAND_PROCESS_POLL_MS));
80084
+ }
80085
+ }
79985
80086
  async function runCommand(command, root, spec, context, additions = {}) {
79986
80087
  const cwdRel = normalizedRootRelative(command.cwd);
79987
80088
  assertNoSymlinkTraversal(root, cwdRel);
@@ -80001,27 +80102,56 @@ async function runCommand(command, root, spec, context, additions = {}) {
80001
80102
  const reachesPhaseDeadline = command.timeout_ms === undefined || command.timeout_ms >= remainingMs;
80002
80103
  const timeoutMs2 = reachesPhaseDeadline ? remainingMs : command.timeout_ms;
80003
80104
  const started = Date.now();
80105
+ const commandMarker = crypto6.randomBytes(32).toString("hex");
80004
80106
  return await new Promise((resolve, reject2) => {
80005
80107
  const child = spawn4(command.argv[0], command.argv.slice(1), {
80006
80108
  cwd: cwd2,
80007
- env: buildEnvironment(spec, command.secret_env, additions),
80109
+ env: buildEnvironment(spec, command.secret_env, {
80110
+ ...additions,
80111
+ [COMMAND_PROCESS_MARKER_ENV]: commandMarker
80112
+ }),
80008
80113
  stdio: ["ignore", "pipe", "pipe"],
80009
80114
  detached: process.platform !== "win32"
80010
80115
  });
80011
80116
  const stdout = [];
80012
80117
  const stderr = [];
80118
+ const observedDescendants = new Set;
80013
80119
  let captured = 0;
80014
80120
  let settled = false;
80015
80121
  let timer;
80122
+ let observer;
80123
+ let cleanup;
80124
+ const observeDescendants = () => {
80125
+ if (child.pid === undefined)
80126
+ return;
80127
+ for (const pid of descendantPids(child.pid))
80128
+ observedDescendants.add(pid);
80129
+ };
80130
+ const cleanupProcesses = () => {
80131
+ if (observer)
80132
+ clearInterval(observer);
80133
+ cleanup ??= terminateCommandProcesses(child, commandMarker, observedDescendants);
80134
+ return cleanup;
80135
+ };
80136
+ const sanitizedOutput = (chunks) => redactCommandOutput(Buffer.concat(chunks), spec, [commandMarker]);
80137
+ const sanitizedError = (error2) => {
80138
+ if (!(error2 instanceof BenchmarkCommandExecutionError))
80139
+ return error2;
80140
+ return new BenchmarkCommandExecutionError(error2.code, error2.message, sanitizedOutput(stdout), sanitizedOutput(stderr), error2.durationMs);
80141
+ };
80016
80142
  const fail = (error2) => {
80017
80143
  if (settled)
80018
80144
  return;
80019
80145
  settled = true;
80020
80146
  if (timer)
80021
80147
  clearTimeout(timer);
80022
- terminate(child);
80023
- reject2(error2);
80148
+ cleanupProcesses().then(() => reject2(sanitizedError(error2)), reject2);
80024
80149
  };
80150
+ if (process.platform !== "linux") {
80151
+ observeDescendants();
80152
+ observer = setInterval(observeDescendants, COMMAND_PROCESS_POLL_MS);
80153
+ observer.unref();
80154
+ }
80025
80155
  const capture = (target, chunk2) => {
80026
80156
  if (settled)
80027
80157
  return;
@@ -80035,6 +80165,11 @@ async function runCommand(command, root, spec, context, additions = {}) {
80035
80165
  };
80036
80166
  child.stdout?.on("data", (chunk2) => capture(stdout, chunk2));
80037
80167
  child.stderr?.on("data", (chunk2) => capture(stderr, chunk2));
80168
+ child.once("exit", () => {
80169
+ if (timer)
80170
+ clearTimeout(timer);
80171
+ cleanupProcesses();
80172
+ });
80038
80173
  child.on("error", (error2) => {
80039
80174
  fail(new BenchmarkCommandExecutionError("command_start_failed", `failed to start ${command.id}: ${error2.message}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
80040
80175
  });
@@ -80050,16 +80185,19 @@ async function runCommand(command, root, spec, context, additions = {}) {
80050
80185
  return;
80051
80186
  settled = true;
80052
80187
  clearTimeout(timer);
80053
- if (code === null) {
80054
- reject2(new BenchmarkCommandExecutionError("command_terminated", `command was terminated by ${signal ?? "an unknown signal"}: ${command.id}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
80055
- return;
80056
- }
80057
- resolve({
80058
- exitCode: code,
80059
- stdout: Buffer.concat(stdout),
80060
- stderr: Buffer.concat(stderr),
80061
- durationMs: Date.now() - started
80062
- });
80188
+ cleanupProcesses().then(() => {
80189
+ if (code === null) {
80190
+ reject2(new BenchmarkCommandExecutionError("command_terminated", `command was terminated by ${signal ?? "an unknown signal"}: ${command.id}`, sanitizedOutput(stdout), sanitizedOutput(stderr), Date.now() - started));
80191
+ return;
80192
+ }
80193
+ resolve({
80194
+ exitCode: code,
80195
+ stdout: sanitizedOutput(stdout),
80196
+ stderr: sanitizedOutput(stderr),
80197
+ durationMs: Date.now() - started,
80198
+ redactions: [commandMarker]
80199
+ });
80200
+ }, reject2);
80063
80201
  });
80064
80202
  });
80065
80203
  }
@@ -80252,6 +80390,13 @@ async function workspaceManifest(spec, context) {
80252
80390
  }
80253
80391
  return records.sort((a3, b4) => a3.path.localeCompare(b4.path));
80254
80392
  }
80393
+ async function verifyWorkspaceManifestUnchanged(expected, spec, context) {
80394
+ await verifyRecordsUnchanged(expected, spec);
80395
+ const actual = await workspaceManifest(spec, context);
80396
+ if (JSON.stringify(actual) !== JSON.stringify(expected)) {
80397
+ throw new BenchmarkPhaseError("evidence_tampered", "candidate workspace changed during read-only evaluation");
80398
+ }
80399
+ }
80255
80400
  function treeBytes(root, context) {
80256
80401
  let totalBytes = 0;
80257
80402
  const stack = [path88.resolve(root)];
@@ -80413,13 +80558,13 @@ async function copyCandidateOutput(output, manifest, spec, context) {
80413
80558
  }
80414
80559
  return copied;
80415
80560
  }
80416
- async function copyFrozenWorkspace(manifest, spec, context) {
80561
+ async function copyFrozenWorkspace(manifest, spec, context, sourceRoot = spec.workspace_root, preserveUnsafeSymlinks = false) {
80417
80562
  const destinationRoot = fs81.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-workspace-"));
80418
80563
  fs81.chmodSync(destinationRoot, 448);
80419
80564
  context.temporaryRoots.add(destinationRoot);
80420
80565
  for (const frozenFile of manifest) {
80421
80566
  assertBudget(context);
80422
- const source = path88.resolve(spec.workspace_root, safeRelPath(frozenFile.path));
80567
+ const source = path88.resolve(sourceRoot, safeRelPath(frozenFile.path));
80423
80568
  const destination = path88.resolve(destinationRoot, safeRelPath(frozenFile.path));
80424
80569
  if (frozenFile.kind === "symlink") {
80425
80570
  const stat = fs81.lstatSync(source);
@@ -80427,18 +80572,18 @@ async function copyFrozenWorkspace(manifest, spec, context) {
80427
80572
  if (target === null || Buffer.byteLength(target) !== frozenFile.size || sha256(target) !== frozenFile.sha256) {
80428
80573
  throw new BenchmarkPhaseError("evidence_tampered", `workspace changed after it was frozen: ${frozenFile.path}`);
80429
80574
  }
80430
- if (path88.isAbsolute(target)) {
80575
+ if (!preserveUnsafeSymlinks && path88.isAbsolute(target)) {
80431
80576
  throw new BenchmarkPhaseError("unsafe_workspace_symlink", `sandbox evaluator workspace cannot reproduce an absolute symlink: ${frozenFile.path}`);
80432
80577
  }
80433
80578
  const resolvedTarget = path88.resolve(path88.dirname(source), target);
80434
- if (!isWithin(spec.workspace_root, resolvedTarget)) {
80579
+ if (!preserveUnsafeSymlinks && !isWithin(sourceRoot, resolvedTarget)) {
80435
80580
  throw new BenchmarkPhaseError("unsafe_workspace_symlink", `sandbox evaluator workspace symlink escapes the workspace: ${frozenFile.path}`);
80436
80581
  }
80437
80582
  fs81.mkdirSync(path88.dirname(destination), { recursive: true, mode: 448 });
80438
80583
  fs81.symlinkSync(target, destination);
80439
80584
  continue;
80440
80585
  }
80441
- await atomicCopy(source, destination, frozenFile.mode, spec.workspace_root);
80586
+ await atomicCopy(source, destination, frozenFile.mode, sourceRoot);
80442
80587
  const copied = await recordFile(destinationRoot, destination, "workspace");
80443
80588
  if (copied.sha256 !== frozenFile.sha256 || copied.size !== frozenFile.size || copied.mode !== frozenFile.mode) {
80444
80589
  throw new BenchmarkPhaseError("evidence_tampered", `workspace changed after it was frozen: ${frozenFile.path}`);
@@ -80513,7 +80658,7 @@ var CriterionResultSchema = exports_external.object({
80513
80658
  var CriteriaResultFileSchema = exports_external.object({
80514
80659
  criteria: exports_external.array(CriterionResultSchema).max(MAX_CRITERIA_PER_EVALUATOR)
80515
80660
  }).strict();
80516
- function readCriterionResults(resultPath, criterionKeys, allowedEvidenceIds) {
80661
+ function readCriterionResults(resultPath, criterionKeys, allowedEvidenceIds, spec, additionalSecrets = []) {
80517
80662
  let opened;
80518
80663
  try {
80519
80664
  opened = openRegularFileNoFollow(resultPath, "criterion result", path88.dirname(resultPath));
@@ -80557,7 +80702,13 @@ function readCriterionResults(resultPath, criterionKeys, allowedEvidenceIds) {
80557
80702
  if (unknownEvidenceIds.length > 0) {
80558
80703
  throw new BenchmarkPhaseError("invalid_criterion_result", "criterion result cites evidence outside the evaluator plan", { unknown_evidence_ids: unknownEvidenceIds });
80559
80704
  }
80560
- return criterionKeys.map((key2) => byKey.get(key2));
80705
+ return criterionKeys.map((key2) => {
80706
+ const criterion = byKey.get(key2);
80707
+ return {
80708
+ ...criterion,
80709
+ explanation: redactCommandOutput(Buffer.from(criterion.explanation), spec, additionalSecrets).toString("utf8")
80710
+ };
80711
+ });
80561
80712
  } finally {
80562
80713
  fs81.closeSync(opened.fd);
80563
80714
  }
@@ -80580,7 +80731,7 @@ function eventType(event) {
80580
80731
  }
80581
80732
  return;
80582
80733
  }
80583
- async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, frozenEvidence, context) {
80734
+ async function evaluateOne(evaluator, spec, manifest, frozenWorkspaceRoot, finalOutput, trajectory, frozenEvidence, context) {
80584
80735
  const started = Date.now();
80585
80736
  const base2 = {
80586
80737
  id: evaluator.id,
@@ -80653,8 +80804,8 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80653
80804
  }
80654
80805
  if (evaluator.type === "workspace_assertion") {
80655
80806
  const relative = workspaceRel(evaluator.path);
80656
- assertNoSymlinkTraversal(spec.workspace_root, relative);
80657
- const candidate = path88.resolve(spec.workspace_root, relative);
80807
+ assertNoSymlinkTraversal(frozenWorkspaceRoot, relative);
80808
+ const candidate = path88.resolve(frozenWorkspaceRoot, relative);
80658
80809
  let stat = null;
80659
80810
  try {
80660
80811
  stat = fs81.lstatSync(candidate);
@@ -80675,7 +80826,7 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80675
80826
  verdict = !exists2;
80676
80827
  if (evaluator.assertion.operator === "sha256") {
80677
80828
  if (stat?.isFile()) {
80678
- const opened = openRegularFileNoFollow(candidate, `workspace assertion ${relative}`, spec.workspace_root);
80829
+ const opened = openRegularFileNoFollow(candidate, `workspace assertion ${relative}`, frozenWorkspaceRoot);
80679
80830
  try {
80680
80831
  actualSha256 = await sha256OfDescriptor(opened.fd);
80681
80832
  verdict = actualSha256 === evaluator.assertion.expected;
@@ -80686,7 +80837,7 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80686
80837
  }
80687
80838
  if (evaluator.assertion.operator === "contains") {
80688
80839
  if (stat?.isFile()) {
80689
- const opened = openRegularFileNoFollow(candidate, `workspace assertion ${relative}`, spec.workspace_root);
80840
+ const opened = openRegularFileNoFollow(candidate, `workspace assertion ${relative}`, frozenWorkspaceRoot);
80690
80841
  try {
80691
80842
  verdict = readDescriptor(opened.fd).toString("utf8").includes(evaluator.assertion.expected);
80692
80843
  } finally {
@@ -80714,9 +80865,13 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80714
80865
  let isolatedWorkspace;
80715
80866
  let isolatedTestsRoot;
80716
80867
  let privateResultRoot;
80868
+ const usesLiveWorkspace = evaluator.workspace_mode === "read_only";
80717
80869
  try {
80718
- const evaluatorWorkspace = await copyFrozenWorkspace(manifest, spec, context);
80719
- isolatedWorkspace = evaluatorWorkspace;
80870
+ const evaluatorWorkspace = usesLiveWorkspace ? spec.workspace_root : await copyFrozenWorkspace(manifest, spec, context, frozenWorkspaceRoot);
80871
+ if (!usesLiveWorkspace)
80872
+ isolatedWorkspace = evaluatorWorkspace;
80873
+ else
80874
+ await verifyWorkspaceManifestUnchanged(manifest, spec, context);
80720
80875
  isolatedTestsRoot = await copyEvaluatorTests(spec, context);
80721
80876
  const testsPath = evaluatorTestsPath(evaluator, isolatedTestsRoot);
80722
80877
  const commandRoot = evaluator.root === "workspace" ? evaluatorWorkspace : isolatedTestsRoot;
@@ -80741,7 +80896,7 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80741
80896
  const stderr = await writeLog(spec.logs_root, `${evaluator.id}.stderr.log`, result2.stderr, spec);
80742
80897
  let criterionResults;
80743
80898
  try {
80744
- criterionResults = evaluator.criterion_keys && criterionResultPath ? readCriterionResults(criterionResultPath, evaluator.criterion_keys, evaluator.allowed_evidence_ids ?? []) : undefined;
80899
+ criterionResults = evaluator.criterion_keys && criterionResultPath ? readCriterionResults(criterionResultPath, evaluator.criterion_keys, evaluator.allowed_evidence_ids ?? [], spec, result2.redactions) : undefined;
80745
80900
  } catch (error2) {
80746
80901
  const normalized = stableError(error2);
80747
80902
  return {
@@ -80770,11 +80925,17 @@ async function evaluateOne(evaluator, spec, manifest, finalOutput, trajectory, f
80770
80925
  ...criterionResults ? { criterion_results: criterionResults } : {}
80771
80926
  };
80772
80927
  } finally {
80773
- for (const temporary of [privateResultRoot, isolatedTestsRoot, isolatedWorkspace]) {
80774
- if (!temporary)
80775
- continue;
80776
- fs81.rmSync(temporary, { recursive: true, force: true });
80777
- context.temporaryRoots.delete(temporary);
80928
+ try {
80929
+ if (usesLiveWorkspace) {
80930
+ await verifyWorkspaceManifestUnchanged(manifest, spec, context);
80931
+ }
80932
+ } finally {
80933
+ for (const temporary of [privateResultRoot, isolatedTestsRoot, isolatedWorkspace]) {
80934
+ if (!temporary)
80935
+ continue;
80936
+ fs81.rmSync(temporary, { recursive: true, force: true });
80937
+ context.temporaryRoots.delete(temporary);
80938
+ }
80778
80939
  }
80779
80940
  }
80780
80941
  }
@@ -80873,12 +81034,12 @@ async function executeEvaluate(spec, context) {
80873
81034
  assertEvaluateOutputBudget(spec, context);
80874
81035
  }
80875
81036
  await verifyRecordsUnchanged(manifest, spec);
81037
+ const frozenWorkspaceRoot = await copyFrozenWorkspace(manifest, spec, context, spec.workspace_root, true);
80876
81038
  for (const reference of spec.references) {
80877
81039
  const referenceOutputs = await copyMaterial(reference, spec.staging_root, spec.tests_root, "tests", false, context);
80878
81040
  outputs.push(...referenceOutputs);
80879
81041
  context.outputs.push(...referenceOutputs);
80880
81042
  }
80881
- const frozenOutputCount = context.outputs.length;
80882
81043
  const evaluators = [];
80883
81044
  const evaluatorOrder = new Map(spec.evaluators.map((evaluator, index) => [evaluator.id, index]));
80884
81045
  const executionOrder = [...spec.evaluators].sort((left, right) => Number(left.type === "sandbox_command") - Number(right.type === "sandbox_command"));
@@ -80886,7 +81047,7 @@ async function executeEvaluate(spec, context) {
80886
81047
  assertBudget(context);
80887
81048
  const started = Date.now();
80888
81049
  try {
80889
- const evaluated = await evaluateOne(evaluator, spec, manifest, finalOutput.buffer.toString("utf8"), trajectory, { finalOutputPath, trajectoryPath }, context);
81050
+ const evaluated = await evaluateOne(evaluator, spec, manifest, frozenWorkspaceRoot, finalOutput.buffer.toString("utf8"), trajectory, { finalOutputPath, trajectoryPath }, context);
80890
81051
  assertBudget(context);
80891
81052
  evaluators.push(evaluated);
80892
81053
  context.evaluators.push(evaluated);
@@ -80941,7 +81102,7 @@ async function executeEvaluate(spec, context) {
80941
81102
  assertEvaluateOutputBudget(spec, context);
80942
81103
  }
80943
81104
  }
80944
- await verifyRecordsUnchanged([...context.inputs, ...context.outputs.slice(0, frozenOutputCount)], spec);
81105
+ await verifyRecordsUnchanged([...context.inputs, ...context.outputs], spec);
80945
81106
  if (!verifyOwnedDirectory(spec.tests_root, "tests", spec) || !verifyOwnedDirectory(spec.logs_root, "logs", spec)) {
80946
81107
  throw new BenchmarkPhaseError("evidence_tampered", "benchmark evaluator changed an owned phase directory marker");
80947
81108
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainbase-labs/cli",
3
- "version": "0.25.0-eng1209.6",
3
+ "version": "0.25.0-eng1209.8",
4
4
  "description": "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
5
5
  "type": "module",
6
6
  "bin": {