baro-ai 0.125.0 → 0.126.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/dist/cli.mjs CHANGED
@@ -70955,8 +70955,8 @@ async function getCurrentBranch(cwd) {
70955
70955
  }
70956
70956
  async function ensureGreenfieldRepo(cwd, onLog) {
70957
70957
  if (await isInsideGitRepo(cwd)) return false;
70958
- const { readdir } = await import("fs/promises");
70959
- const entries = (await readdir(cwd)).filter(
70958
+ const { readdir: readdir2 } = await import("fs/promises");
70959
+ const entries = (await readdir2(cwd)).filter(
70960
70960
  (name) => name !== ".DS_Store" && name !== "Thumbs.db" && // baro's own pre-branch artifacts don't make a directory a project.
70961
70961
  name !== "prd.json" && name !== "baro.lock" && name !== ".baro"
70962
70962
  );
@@ -90624,10 +90624,12 @@ function extractVerdictJson(text3) {
90624
90624
  }
90625
90625
 
90626
90626
  // ../baro-orchestrator/src/acceptance/critic-evidence.ts
90627
+ import { execFile as execFile4 } from "child_process";
90627
90628
  import { createHash as createHash18 } from "crypto";
90628
90629
  import { constants as constants2 } from "fs";
90629
- import { lstat as lstat2, open as open2, readlink, realpath } from "fs/promises";
90630
+ import { lstat as lstat2, open as open2, readdir, readlink, realpath } from "fs/promises";
90630
90631
  import { isAbsolute as isAbsolute6, relative as relative7, resolve as resolve11, sep as sep4 } from "path";
90632
+ import { promisify as promisify2 } from "util";
90631
90633
 
90632
90634
  // ../baro-orchestrator/src/runtime/participant-identity.ts
90633
90635
  function participantAgentId(source) {
@@ -90794,6 +90796,110 @@ async function readLineCount(cwd, path7) {
90794
90796
  return countLines(await readFile(target, "utf8"));
90795
90797
  }
90796
90798
 
90799
+ // ../baro-orchestrator/src/acceptance/content-preserving-command.ts
90800
+ var NOT_PRESERVING = { preserving: false, commits: false };
90801
+ var READ_ONLY = /* @__PURE__ */ new Set([
90802
+ "cd",
90803
+ "pwd",
90804
+ "ls",
90805
+ "cat",
90806
+ "head",
90807
+ "tail",
90808
+ "wc",
90809
+ "grep",
90810
+ "egrep",
90811
+ "fgrep",
90812
+ "rg",
90813
+ "echo",
90814
+ "printf",
90815
+ "true",
90816
+ "test",
90817
+ "[",
90818
+ "stat",
90819
+ "which",
90820
+ "type",
90821
+ "diff",
90822
+ "cmp",
90823
+ "cut",
90824
+ "tr",
90825
+ "basename",
90826
+ "dirname",
90827
+ "realpath",
90828
+ "printenv",
90829
+ "sleep",
90830
+ "jq"
90831
+ ]);
90832
+ var GIT_READ_ONLY = /* @__PURE__ */ new Set([
90833
+ "status",
90834
+ "diff",
90835
+ "log",
90836
+ "show",
90837
+ "rev-parse",
90838
+ "ls-files",
90839
+ "ls-tree",
90840
+ "cat-file",
90841
+ "blame",
90842
+ "grep",
90843
+ "describe",
90844
+ "shortlog",
90845
+ "merge-base",
90846
+ "rev-list",
90847
+ "name-rev"
90848
+ ]);
90849
+ var GIT_INDEX_ONLY = /* @__PURE__ */ new Set(["add"]);
90850
+ var SHELL_WRAPPER = /^(?:\/(?:usr\/)?bin\/)?(?:ba|z)?sh\s+-l?c\s+(['"])([\s\S]*)\1\s*$/u;
90851
+ var HARMLESS_REDIRECT = /\s*(?:\d?>\s*\/dev\/null|\d>&\d)/gu;
90852
+ var HEREDOC_MESSAGE = /"\$\(cat <<-?\s*(['"])(\w+)\1\n[\s\S]*?\n\s*\2\s*\)"/gu;
90853
+ var SINGLE_QUOTED = /'[^']*'/gu;
90854
+ var DOUBLE_QUOTED = /"(?:[^"\\$`]|\\.)*"/gu;
90855
+ var UNSAFE = /[<>`$"']|\b(?:xargs|tee)\b/u;
90856
+ var UNSAFE_OPTION = /^(?:--output|--pre\b|-O$|--open-files-in-pager|--ext-diff)/u;
90857
+ function contentPreservingCommand(command) {
90858
+ let script = command.trim();
90859
+ const wrapped = SHELL_WRAPPER.exec(script);
90860
+ if (wrapped) script = wrapped[2].trim();
90861
+ script = script.replace(HEREDOC_MESSAGE, "MSG").replace(SINGLE_QUOTED, "Q").replace(DOUBLE_QUOTED, "Q").replace(HARMLESS_REDIRECT, "");
90862
+ if (script === "" || UNSAFE.test(script)) return NOT_PRESERVING;
90863
+ let commits = false;
90864
+ for (const segment of script.split(/&&|\|\||[;|\n]/u)) {
90865
+ const words = segment.trim().split(/\s+/u).filter(Boolean);
90866
+ if (words.length === 0) continue;
90867
+ const [program, ...rest] = words;
90868
+ if (rest.some((w) => UNSAFE_OPTION.test(w))) return NOT_PRESERVING;
90869
+ if (program === "git") {
90870
+ const sub = gitSubcommand(rest);
90871
+ if (sub === null) return NOT_PRESERVING;
90872
+ if (sub === "commit") {
90873
+ commits = true;
90874
+ continue;
90875
+ }
90876
+ if (GIT_READ_ONLY.has(sub) || GIT_INDEX_ONLY.has(sub)) continue;
90877
+ return NOT_PRESERVING;
90878
+ }
90879
+ if (program === "sed") {
90880
+ if (rest.some((w) => /^-[a-zA-Z]*i/u.test(w) || w.startsWith("--in-place"))) return NOT_PRESERVING;
90881
+ if (!rest.includes("-n")) return NOT_PRESERVING;
90882
+ continue;
90883
+ }
90884
+ if (program === "find") {
90885
+ if (rest.some((w) => /^-(?:delete|exec|execdir|ok|okdir|fprint|fprintf|fls)$/u.test(w))) return NOT_PRESERVING;
90886
+ continue;
90887
+ }
90888
+ if (!READ_ONLY.has(program)) return NOT_PRESERVING;
90889
+ }
90890
+ return { preserving: true, commits };
90891
+ }
90892
+ function gitSubcommand(words) {
90893
+ let i2 = 0;
90894
+ while (i2 < words.length && words[i2].startsWith("-")) {
90895
+ const option = words[i2];
90896
+ if (option === "-C") i2 += 2;
90897
+ else if (option === "--no-pager") i2 += 1;
90898
+ else return null;
90899
+ }
90900
+ return words[i2] ?? null;
90901
+ }
90902
+
90797
90903
  // ../baro-orchestrator/src/acceptance/critic-evidence.ts
90798
90904
  var DEFAULT_GIT_TIMEOUT_MS = 1e4;
90799
90905
  var DEFAULT_MAX_DIFF_CHARS = 32e3;
@@ -90871,6 +90977,9 @@ var CriticCommandEvidenceCollector = class extends BusMember {
90871
90977
  pending = /* @__PURE__ */ new Map();
90872
90978
  fingerprintTasks = /* @__PURE__ */ new Map();
90873
90979
  fallbackRevisions = /* @__PURE__ */ new Map();
90980
+ /** What advanced each revision, so a fingerprint raced only by commands
90981
+ * that cannot change content (git add/commit, git status, reads) is kept. */
90982
+ activity = /* @__PURE__ */ new Map();
90874
90983
  onExternalEvent(source, event) {
90875
90984
  if (AgentState.is(event) && this.acceptsSource(source, event.payload.agentId) && event.payload.phase === "running" && (event.payload.detail?.startsWith("attempt ") === true || event.payload.detail === "first turn")) {
90876
90985
  this.resetAttempt(event.payload.agentId);
@@ -90881,7 +90990,7 @@ var CriticCommandEvidenceCollector = class extends BusMember {
90881
90990
  if (!agentId) return;
90882
90991
  if (!this.acceptsSource(source, agentId)) return;
90883
90992
  if (isWorkspaceWriteTool(item.name)) {
90884
- this.advanceFallbackRevision(agentId);
90993
+ this.advanceFallbackRevision(agentId, { preserving: false, commits: false });
90885
90994
  return;
90886
90995
  }
90887
90996
  if (!isShellTool(item.name)) return;
@@ -90891,7 +91000,7 @@ var CriticCommandEvidenceCollector = class extends BusMember {
90891
91000
  existing.command = redactSensitiveText(extractCommand(item.args));
90892
91001
  return;
90893
91002
  }
90894
- this.advanceFallbackRevision(agentId);
91003
+ this.advanceFallbackRevision(agentId, contentPreservingCommand(extractCommand(item.args)));
90895
91004
  const entry = {
90896
91005
  callId: item.callId,
90897
91006
  tool: item.name,
@@ -90985,6 +91094,7 @@ var CriticCommandEvidenceCollector = class extends BusMember {
90985
91094
  resetAttempt(agentId) {
90986
91095
  this.commands.delete(agentId);
90987
91096
  this.fallbackRevisions.delete(agentId);
91097
+ this.activity.delete(agentId);
90988
91098
  const prefix = `${agentId}\0`;
90989
91099
  for (const key of this.pending.keys()) {
90990
91100
  if (key.startsWith(prefix)) this.pending.delete(key);
@@ -91021,12 +91131,13 @@ var CriticCommandEvidenceCollector = class extends BusMember {
91021
91131
  return;
91022
91132
  }
91023
91133
  const task = (async () => {
91134
+ const resolved = await target;
91024
91135
  const fingerprint = await this.fingerprintResolvedTarget(
91025
- await target,
91136
+ resolved,
91026
91137
  fallbackRevision
91027
91138
  );
91028
91139
  const completedRevision = this.fallbackRevisions.get(agentId) ?? 0;
91029
- entry.fingerprint = completedRevision === fallbackRevision ? fingerprint : {
91140
+ entry.fingerprint = completedRevision === fallbackRevision || await this.onlyContentPreservingSince(agentId, fallbackRevision, completedRevision, resolved) ? fingerprint : {
91030
91141
  state: "unavailable",
91031
91142
  reason: "shell/write activity advanced while the command fingerprint was being captured"
91032
91143
  };
@@ -91075,11 +91186,32 @@ var CriticCommandEvidenceCollector = class extends BusMember {
91075
91186
  };
91076
91187
  }
91077
91188
  }
91078
- advanceFallbackRevision(agentId) {
91079
- this.fallbackRevisions.set(
91080
- agentId,
91081
- (this.fallbackRevisions.get(agentId) ?? 0) + 1
91082
- );
91189
+ advanceFallbackRevision(agentId, kind) {
91190
+ const revision = (this.fallbackRevisions.get(agentId) ?? 0) + 1;
91191
+ this.fallbackRevisions.set(agentId, revision);
91192
+ let log2 = this.activity.get(agentId);
91193
+ if (!log2) {
91194
+ log2 = /* @__PURE__ */ new Map();
91195
+ this.activity.set(agentId, log2);
91196
+ }
91197
+ log2.set(revision, kind);
91198
+ for (const old of log2.keys()) {
91199
+ if (old > revision - MAX_ACTIVITY_LOG) break;
91200
+ log2.delete(old);
91201
+ }
91202
+ }
91203
+ /** True when every activity in (from, to] provably left the working tree
91204
+ * as it was, so a fingerprint taken meanwhile still describes the command. */
91205
+ async onlyContentPreservingSince(agentId, from, to, target) {
91206
+ if (!target) return false;
91207
+ const log2 = this.activity.get(agentId);
91208
+ let commits = false;
91209
+ for (let revision = from + 1; revision <= to; revision++) {
91210
+ const kind = log2?.get(revision);
91211
+ if (!kind?.preserving) return false;
91212
+ commits ||= kind.commits;
91213
+ }
91214
+ return !commits || !await hasCommitHooks(target.cwd);
91083
91215
  }
91084
91216
  async drainFingerprintTasks(agentId) {
91085
91217
  while ((this.fingerprintTasks.get(agentId)?.size ?? 0) > 0) {
@@ -91945,6 +92077,24 @@ async function collectRepositoryEvidence(target, opts) {
91945
92077
  renderGitOutput(commits, MAX_LOG_CHARS)
91946
92078
  ].join("\n");
91947
92079
  }
92080
+ var MAX_ACTIVITY_LOG = 256;
92081
+ var execFileAsync = promisify2(execFile4);
92082
+ var COMMIT_HOOKS = /* @__PURE__ */ new Set(["pre-commit", "prepare-commit-msg", "commit-msg", "post-commit"]);
92083
+ async function hasCommitHooks(cwd) {
92084
+ try {
92085
+ const { stdout } = await execFileAsync("git", ["rev-parse", "--git-path", "hooks"], {
92086
+ cwd: resolve11(cwd),
92087
+ timeout: DEFAULT_GIT_TIMEOUT_MS
92088
+ });
92089
+ const names = await readdir(resolve11(cwd, stdout.trim())).catch((error) => {
92090
+ if (error.code === "ENOENT") return [];
92091
+ throw error;
92092
+ });
92093
+ return names.some((name) => COMMIT_HOOKS.has(name));
92094
+ } catch {
92095
+ return true;
92096
+ }
92097
+ }
91948
92098
  async function runGit(cwd, args, timeoutMs) {
91949
92099
  const nullDevice = process.platform === "win32" ? "NUL" : "/dev/null";
91950
92100
  const hardenedArgs = [