baro-ai 0.73.0 → 0.73.1

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
@@ -40948,6 +40948,15 @@ var Conductor = class extends BaseObserver {
40948
40948
  recoveryLevelsStarted = 0;
40949
40949
  totalAttempts = 0;
40950
40950
  appliedReplans = 0;
40951
+ /**
40952
+ * Healing actions (replan applications + recovery-level starts) since
40953
+ * the last successful story. The Surgeon may propose forever; this is
40954
+ * the Conductor's brake — when it reaches the budget the run ends
40955
+ * gracefully so the Finalizer can ship completed work (checkpoint PR).
40956
+ */
40957
+ replansSinceProgress = 0;
40958
+ replanProgressBudget;
40959
+ softDeadlineSecs;
40951
40960
  currentLevel = null;
40952
40961
  /** Replan payloads emitted during a level. Applied at level boundary. */
40953
40962
  pendingReplans = [];
@@ -40984,6 +40993,8 @@ var Conductor = class extends BaseObserver {
40984
40993
  if (this.opts.intraLevelDelaySecs == null) {
40985
40994
  this.opts.intraLevelDelaySecs = 10;
40986
40995
  }
40996
+ this.replanProgressBudget = opts.replanProgressBudget ?? envNonNegativeInt("BARO_REPLAN_PROGRESS_BUDGET", 3);
40997
+ this.softDeadlineSecs = opts.softDeadlineSecs ?? envNonNegativeInt("BARO_RUN_SOFT_DEADLINE_SECS", 0);
40987
40998
  this.done = new Promise((resolve4) => {
40988
40999
  this.resolveDone = resolve4;
40989
41000
  });
@@ -41076,6 +41087,11 @@ var Conductor = class extends BaseObserver {
41076
41087
  if (levels.length === 0) {
41077
41088
  const allPassed = this.prd.userStories.every((s2) => s2.passes) && this.globalDropped.length === 0;
41078
41089
  if (!allPassed && this.globalFailed.length > 0) {
41090
+ const halt = this.healingHaltReason();
41091
+ if (halt) {
41092
+ this.terminateRun(false, halt);
41093
+ return;
41094
+ }
41079
41095
  const recovered = await this.tryStartRecoveryLevel(
41080
41096
  this.globalFailed,
41081
41097
  blockedStoryIds.size > 0 ? `blocked dependencies: ${[...blockedStoryIds].join(", ")}` : "terminal failed stories"
@@ -41086,6 +41102,11 @@ var Conductor = class extends BaseObserver {
41086
41102
  this.terminateRun(allPassed, abortReason);
41087
41103
  return;
41088
41104
  }
41105
+ const softDeadline = this.softDeadlineReason();
41106
+ if (softDeadline) {
41107
+ this.terminateRun(false, softDeadline);
41108
+ return;
41109
+ }
41089
41110
  const level = levels[0];
41090
41111
  const ordinal = (this.currentLevel?.ordinal ?? 0) + 1;
41091
41112
  const totalLevelsHint = ordinal + levels.length - 1;
@@ -41134,6 +41155,7 @@ var Conductor = class extends BaseObserver {
41134
41155
  this.currentLevel.passed.push(item.storyId);
41135
41156
  this.globalCompleted.push(item.storyId);
41136
41157
  this.removeGlobalFailed(item.storyId);
41158
+ this.replansSinceProgress = 0;
41137
41159
  if (this.prd) {
41138
41160
  this.prd = markStoryPassed(this.prd, item.storyId, item.durationSecs);
41139
41161
  savePrd(this.opts.prdPath, this.prd);
@@ -41249,6 +41271,7 @@ ${prompt}`;
41249
41271
  })
41250
41272
  );
41251
41273
  let replannedThisLevel = false;
41274
+ let healingHalt = null;
41252
41275
  if (this.pendingReplans.length > 0 && this.prd) {
41253
41276
  const drained = this.pendingReplans.splice(0);
41254
41277
  for (const replan of drained) {
@@ -41262,8 +41285,11 @@ ${prompt}`;
41262
41285
  );
41263
41286
  continue;
41264
41287
  }
41288
+ healingHalt = this.healingHaltReason();
41289
+ if (healingHalt) break;
41265
41290
  this.prd = applyReplan(this.prd, replan);
41266
41291
  this.appliedReplans += 1;
41292
+ this.noteHealingAction(lvl.ordinal);
41267
41293
  replannedThisLevel = true;
41268
41294
  if (replan.removedStoryIds.length > 0) {
41269
41295
  const removeSet = new Set(replan.removedStoryIds);
@@ -41291,9 +41317,18 @@ ${prompt}`;
41291
41317
  }
41292
41318
  savePrd(this.opts.prdPath, this.prd);
41293
41319
  }
41320
+ if (healingHalt) {
41321
+ this.terminateRun(false, healingHalt);
41322
+ return;
41323
+ }
41294
41324
  const anySuccess = lvl.passed.length > 0;
41295
41325
  const totalThisLevel = lvl.passed.length + lvl.failed.length;
41296
41326
  if (!anySuccess && totalThisLevel > 0 && !replannedThisLevel) {
41327
+ const halt = this.healingHaltReason();
41328
+ if (halt) {
41329
+ this.terminateRun(false, halt);
41330
+ return;
41331
+ }
41297
41332
  const recovered = await this.tryStartRecoveryLevel(
41298
41333
  lvl.failed,
41299
41334
  "all stories in level failed"
@@ -41372,6 +41407,36 @@ ${prompt}`;
41372
41407
  }
41373
41408
  return blocked;
41374
41409
  }
41410
+ /**
41411
+ * Non-null when healing must stop: the progress budget is spent or
41412
+ * the soft deadline has passed. Callers terminate the run with the
41413
+ * returned reason instead of applying a replan / starting recovery.
41414
+ */
41415
+ healingHaltReason() {
41416
+ if (this.replanProgressBudget > 0 && this.replansSinceProgress >= this.replanProgressBudget) {
41417
+ const n = this.replansSinceProgress;
41418
+ return `no progress after ${n} replan${n === 1 ? "" : "s"} \u2014 stopping so completed work can ship`;
41419
+ }
41420
+ return this.softDeadlineReason();
41421
+ }
41422
+ softDeadlineReason() {
41423
+ if (this.softDeadlineSecs <= 0) return null;
41424
+ const elapsedSecs = (Date.now() - this.startedAt) / 1e3;
41425
+ if (elapsedSecs < this.softDeadlineSecs) return null;
41426
+ return `soft deadline reached (${this.softDeadlineSecs}s) \u2014 stopping so completed work can ship`;
41427
+ }
41428
+ noteHealingAction(currentLevel) {
41429
+ this.replansSinceProgress += 1;
41430
+ if (this.replanProgressBudget > 0) {
41431
+ this.emit(
41432
+ ConductorState.create({
41433
+ phase: "running_level",
41434
+ detail: `replan ${this.replansSinceProgress}/${this.replanProgressBudget} without progress`,
41435
+ ...currentLevel != null ? { currentLevel } : {}
41436
+ })
41437
+ );
41438
+ }
41439
+ }
41375
41440
  addGlobalFailed(storyId) {
41376
41441
  if (!this.globalFailed.includes(storyId)) {
41377
41442
  this.globalFailed.push(storyId);
@@ -41398,6 +41463,7 @@ ${prompt}`;
41398
41463
  stories.push(story);
41399
41464
  }
41400
41465
  if (stories.length === 0) return false;
41466
+ this.noteHealingAction(this.currentLevel?.ordinal);
41401
41467
  for (const story of stories) {
41402
41468
  this.recoveryAttempts.set(
41403
41469
  story.id,
@@ -41513,6 +41579,12 @@ function applyReplan(prd, replan) {
41513
41579
  }
41514
41580
  return { ...prd, userStories: stories };
41515
41581
  }
41582
+ function envNonNegativeInt(name, fallback) {
41583
+ const raw = process.env[name];
41584
+ if (raw == null || raw.trim() === "") return fallback;
41585
+ const n = Number(raw);
41586
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : fallback;
41587
+ }
41516
41588
  function readFileSyncSafe(path6) {
41517
41589
  try {
41518
41590
  return readFileSync2(path6, "utf8");