baro-ai 0.70.7 → 0.70.9

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
@@ -42791,19 +42791,6 @@ var Finalizer = class extends BaseObserver {
42791
42791
  }
42792
42792
  async finalize(run) {
42793
42793
  if (!this.opts.createPr) return;
42794
- if (!run.success) {
42795
- this.log(
42796
- `[finalizer] run did not complete successfully (${run.abortReason ?? "no reason"}); skipping PR`
42797
- );
42798
- this.emit(
42799
- PrCreated.create({
42800
- url: null,
42801
- branch: this.branchName ?? "",
42802
- baseBranch: ""
42803
- })
42804
- );
42805
- return;
42806
- }
42807
42794
  if (!await this.hasGhBinary()) {
42808
42795
  this.log("[finalizer] `gh` not found on PATH; skipping PR creation");
42809
42796
  this.emit(
@@ -42835,6 +42822,21 @@ var Finalizer = class extends BaseObserver {
42835
42822
  return;
42836
42823
  }
42837
42824
  this.emit(FinalizeStarted.create({ branch }));
42825
+ const preAdrCommits = await this.collectCommitsSinceBase();
42826
+ const preAdrStats = await this.collectFileStats();
42827
+ const hasRunChanges = preAdrCommits.length > 0 || preAdrStats.created + preAdrStats.modified > 0;
42828
+ if (!run.success && !hasRunChanges) {
42829
+ this.log(
42830
+ `[finalizer] run failed with no branch changes (${run.abortReason ?? "no reason"}); skipping PR`
42831
+ );
42832
+ this.emit(PrCreated.create({ url: null, branch, baseBranch }));
42833
+ return;
42834
+ }
42835
+ if (!run.success) {
42836
+ this.log(
42837
+ `[finalizer] run failed after producing changes; opening checkpoint PR (${run.abortReason ?? "no reason"})`
42838
+ );
42839
+ }
42838
42840
  const prd = this.safeLoadPrd();
42839
42841
  if (prd) {
42840
42842
  for (const s2 of prd.userStories) {
@@ -42848,10 +42850,12 @@ var Finalizer = class extends BaseObserver {
42848
42850
  const { passed, failed } = this.partition(orderedStories);
42849
42851
  const filesStats = await this.collectFileStats();
42850
42852
  const totalSecs = run.totalDurationSecs;
42851
- const title = this.buildPrTitle(prd, passed.length, orderedStories.length);
42853
+ const checkpoint = !run.success;
42854
+ const title = this.buildPrTitle(prd, passed.length, orderedStories.length, checkpoint);
42852
42855
  const body = this.buildPrBody({
42853
42856
  prd,
42854
42857
  run,
42858
+ checkpoint,
42855
42859
  orderedStories,
42856
42860
  passed,
42857
42861
  failed,
@@ -42861,7 +42865,7 @@ var Finalizer = class extends BaseObserver {
42861
42865
  sequentialSecs: this.sequentialSeconds()
42862
42866
  });
42863
42867
  await this.pushBranch(branch);
42864
- this.log(`[finalizer] opening PR on ${baseBranch} \u2190 ${branch}`);
42868
+ this.log(`[finalizer] opening ${checkpoint ? "checkpoint " : ""}PR on ${baseBranch} \u2190 ${branch}`);
42865
42869
  const url = await this.openPr({ title, body, baseBranch, branch });
42866
42870
  if (url) {
42867
42871
  this.log(`[finalizer] PR opened: ${url}`);
@@ -43038,12 +43042,13 @@ ${BARO_COAUTHOR_TRAILER}`],
43038
43042
  return "main";
43039
43043
  }
43040
43044
  // ─── PR body composition ────────────────────────────────────────
43041
- buildPrTitle(prd, passed, total) {
43045
+ buildPrTitle(prd, passed, total, checkpoint = false) {
43042
43046
  const project = prd?.project ?? "baro run";
43047
+ const prefix = checkpoint ? "Checkpoint: " : "";
43043
43048
  if (passed === total) {
43044
- return `${project} (${total} ${total === 1 ? "story" : "stories"})`;
43049
+ return `${prefix}${project} (${total} ${total === 1 ? "story" : "stories"})`;
43045
43050
  }
43046
- return `${project} (${passed}/${total} stories)`;
43051
+ return `${prefix}${project} (${passed}/${total} stories)`;
43047
43052
  }
43048
43053
  buildPrBody(args) {
43049
43054
  const { prd, run, orderedStories, passed, failed, commits, filesStats } = args;
@@ -43052,6 +43057,10 @@ ${BARO_COAUTHOR_TRAILER}`],
43052
43057
  `> Opened by [baro](https://baro.rs) \u2014 Mozaik-orchestrated parallel coding agents.`
43053
43058
  );
43054
43059
  lines.push("");
43060
+ if (args.checkpoint) {
43061
+ lines.push("> **Checkpoint PR:** baro produced branch changes, but the run failed during verification/retry/replan. Review this as a candidate fix, not a fully verified completion.");
43062
+ lines.push("");
43063
+ }
43055
43064
  if (prd?.description) {
43056
43065
  lines.push("## Goal");
43057
43066
  lines.push("");
@@ -43368,8 +43377,20 @@ var FinalizationForwarder = class extends BaseObserver {
43368
43377
  // ../baro-orchestrator/src/participants/forwarders/progress.ts
43369
43378
  var ProgressForwarder = class extends BaseObserver {
43370
43379
  async onExternalEvent(_source, event) {
43371
- if (!ConductorState.is(event)) return;
43372
- const item = event.data;
43380
+ if (ConductorState.is(event)) {
43381
+ this.handleConductorState(event.data);
43382
+ return;
43383
+ }
43384
+ if (Replan.is(event)) {
43385
+ this.handleReplan(event.data);
43386
+ return;
43387
+ }
43388
+ if (Critique.is(event)) {
43389
+ this.handleCritique(event.data);
43390
+ return;
43391
+ }
43392
+ }
43393
+ handleConductorState(item) {
43373
43394
  if (item.phase === "running_level" && item.currentLevel != null && item.totalLevels != null) {
43374
43395
  emit({
43375
43396
  type: "progress",
@@ -43380,6 +43401,33 @@ var ProgressForwarder = class extends BaseObserver {
43380
43401
  )
43381
43402
  });
43382
43403
  }
43404
+ if (item.detail) {
43405
+ emit({
43406
+ type: "activity",
43407
+ id: "plan",
43408
+ kind: item.phase === "failed" ? "error" : "warn",
43409
+ text: item.detail
43410
+ });
43411
+ }
43412
+ }
43413
+ handleReplan(item) {
43414
+ const added = item.addedStories.length;
43415
+ const removed = item.removedStoryIds.length;
43416
+ emit({
43417
+ type: "activity",
43418
+ id: "plan",
43419
+ kind: "warn",
43420
+ text: `Replanned (${item.source}): +${added}/-${removed} \u2014 ${item.reason}`
43421
+ });
43422
+ }
43423
+ handleCritique(item) {
43424
+ emit({
43425
+ type: "activity",
43426
+ id: item.agentId,
43427
+ kind: "verdict",
43428
+ ok: item.verdict === "pass",
43429
+ text: item.verdict === "pass" ? `Critic accepted ${item.agentId}` : `Critic rejected ${item.agentId}: ${item.reasoning}`
43430
+ });
43383
43431
  }
43384
43432
  };
43385
43433
 
@@ -47767,7 +47815,7 @@ exactly this shape:
47767
47815
  {"action":"split"|"prereq"|"rewire"|"skip"|"abort",
47768
47816
  "reason":"\u2026",
47769
47817
  "added":[ { "id":"S?","priority":N,"title":"\u2026","description":"\u2026",
47770
- "dependsOn":["\u2026"], "acceptance":["\u2026"], "model":"sonnet" } ],
47818
+ "dependsOn":["\u2026"], "acceptance":["\u2026"] } ],
47771
47819
  "removed":["S?"],
47772
47820
  "modifiedDeps":[{"id":"S?","newDependsOn":["\u2026"]}]}
47773
47821
 
@@ -47777,18 +47825,22 @@ Rules:
47777
47825
  - "modifiedDeps" rewires a story's dependsOn \u2014 use to repoint dependents
47778
47826
  of a removed story to a replacement.
47779
47827
  - "abort" \u2192 empty added/removed/modifiedDeps arrays.
47780
- - TIER every added story with "model" ("haiku" | "sonnet" | "opus"),
47781
- the same way the planner does \u2014 by blast radius, not raw difficulty:
47782
- * "haiku" \u2192 mechanical, single-concern, nothing important breaks
47783
- * "sonnet" \u2192 one contained feature/module
47784
- * "opus" \u2192 cross-cutting / schema / wiring / a DAG hub
47785
- ESCALATION: the failing story already burned its retries at the tier
47786
- shown ("Tier that just failed"). Its replacement(s) must NOT repeat
47787
- that tier unless you are splitting it into genuinely smaller pieces.
47788
- When you keep the same scope (rewire / prereq replacement), bump the
47789
- tier UP one step (haiku\u2192sonnet\u2192opus). When you split (action "split"),
47790
- each child gets the tier its own (smaller) blast radius warrants \u2014
47791
- often lower, but a still-complex child stays "opus".
47828
+ - MODEL: LEAVE "model" UNSET on the stories you add \u2014 they run on the
47829
+ default (cheaper) model, which is exactly what split children want.
47830
+ Do NOT use planner tier names ("haiku"/"sonnet"/"opus") \u2014 the story
47831
+ model is not chosen by tier here; it is either the default or an
47832
+ explicit escalation route (below).
47833
+ - ESCALATION vs SPLIT \u2014 the failing story already burned its retries on
47834
+ the model shown ("Model that just failed"). Two ways to recover:
47835
+ * SPLIT (preferred): if it was TOO BROAD \u2014 too many files/concerns
47836
+ for one session \u2014 break it into smaller, focused stories and
47837
+ leave their "model" unset (they stay on the cheaper model). A
47838
+ smaller, sharper story is usually what a stuck run actually needs.
47839
+ * ESCALATE (sparingly): if the story was already RIGHT-SIZED but
47840
+ genuinely needs a more capable model, set that ONE story's "model"
47841
+ to the exact ESCALATION ROUTE printed in the failure context
47842
+ below. That runs it on the stronger model. Only escalate when the
47843
+ scope is already tight \u2014 never as a reflex.
47792
47844
  - Output ONLY the JSON object, nothing else.`;
47793
47845
  var Surgeon = class extends BaseObserver {
47794
47846
  opts;
@@ -47803,7 +47855,8 @@ var Surgeon = class extends BaseObserver {
47803
47855
  claudeBin: opts.claudeBin ?? "claude",
47804
47856
  timeoutMs: opts.timeoutMs ?? 9e4,
47805
47857
  snapshot: opts.snapshot,
47806
- resolveRoute: opts.resolveRoute
47858
+ resolveRoute: opts.resolveRoute,
47859
+ escalationRoute: opts.escalationRoute
47807
47860
  };
47808
47861
  }
47809
47862
  /** Resolves once every in-flight LLM evaluation has completed. */
@@ -47842,7 +47895,7 @@ var Surgeon = class extends BaseObserver {
47842
47895
  */
47843
47896
  async evaluateWithLlm(failure) {
47844
47897
  const snap = this.opts.snapshot();
47845
- const prompt = buildSurgeonPrompt(snap, failure, this.opts.resolveRoute);
47898
+ const prompt = buildSurgeonPrompt(snap, failure, this.opts.resolveRoute, this.opts.escalationRoute);
47846
47899
  try {
47847
47900
  const { stdout } = await execFileAsync3(
47848
47901
  this.opts.claudeBin,
@@ -47892,7 +47945,7 @@ var Surgeon = class extends BaseObserver {
47892
47945
  }
47893
47946
  }
47894
47947
  };
47895
- function buildSurgeonPrompt(snap, failure, resolveRoute) {
47948
+ function buildSurgeonPrompt(snap, failure, resolveRoute, escalationRoute) {
47896
47949
  const storyLines = snap.stories.map(
47897
47950
  (s2) => ` - ${s2.id} ${s2.passes ? "[passed]" : "[pending]"} ${s2.model ? `<tier:${s2.model}> ` : ""}"${s2.title}" deps=${JSON.stringify(s2.dependsOn)}`
47898
47951
  ).join("\n");
@@ -47915,6 +47968,12 @@ function buildSurgeonPrompt(snap, failure, resolveRoute) {
47915
47968
  ] : [],
47916
47969
  `Attempts: ${failure.attempts}`,
47917
47970
  `Error: ${failure.error ?? "(no reason captured)"}`,
47971
+ ...escalationRoute ? [
47972
+ "",
47973
+ `# Escalation route`,
47974
+ `To ESCALATE a right-sized story onto the stronger model, set that story's "model" to EXACTLY: ${escalationRoute}`,
47975
+ `Otherwise leave "model" unset \u2014 added stories run on the default (cheaper) model. Prefer splitting a too-broad story over escalating.`
47976
+ ] : [],
47918
47977
  "",
47919
47978
  `# Decide`,
47920
47979
  `Output the replan JSON per the rules in your system prompt.`
@@ -47964,7 +48023,8 @@ var SurgeonCodex = class extends BaseObserver {
47964
48023
  codexBin: opts.codexBin ?? "codex",
47965
48024
  timeoutMs: opts.timeoutMs ?? 3e5,
47966
48025
  snapshot: opts.snapshot,
47967
- resolveRoute: opts.resolveRoute
48026
+ resolveRoute: opts.resolveRoute,
48027
+ escalationRoute: opts.escalationRoute
47968
48028
  };
47969
48029
  }
47970
48030
  async idle() {
@@ -47988,7 +48048,7 @@ var SurgeonCodex = class extends BaseObserver {
47988
48048
  }
47989
48049
  async evaluateWithLlm(failure) {
47990
48050
  const snap = this.opts.snapshot();
47991
- const userPrompt = buildSurgeonPrompt(snap, failure, this.opts.resolveRoute);
48051
+ const userPrompt = buildSurgeonPrompt(snap, failure, this.opts.resolveRoute, this.opts.escalationRoute);
47992
48052
  const prompt = `${SURGEON_SYSTEM_PROMPT}
47993
48053
 
47994
48054
  ${userPrompt}`;
@@ -48061,7 +48121,8 @@ var SurgeonOpenAI = class extends BaseObserver {
48061
48121
  maxReplans: opts.maxReplans ?? Infinity,
48062
48122
  model: opts.model ?? "gpt-5.5",
48063
48123
  snapshot: opts.snapshot,
48064
- resolveRoute: opts.resolveRoute
48124
+ resolveRoute: opts.resolveRoute,
48125
+ escalationRoute: opts.escalationRoute
48065
48126
  };
48066
48127
  this.model = pickModel3(this.opts.model);
48067
48128
  }
@@ -48093,7 +48154,7 @@ var SurgeonOpenAI = class extends BaseObserver {
48093
48154
  */
48094
48155
  async evaluate(failure) {
48095
48156
  const snap = this.opts.snapshot();
48096
- const userPrompt = buildSurgeonPrompt(snap, failure, this.opts.resolveRoute);
48157
+ const userPrompt = buildSurgeonPrompt(snap, failure, this.opts.resolveRoute, this.opts.escalationRoute);
48097
48158
  const context = ModelContext.create("surgeon").addContextItem(SystemMessageItem.create(SURGEON_SYSTEM_PROMPT)).addContextItem(UserMessageItem.create(userPrompt));
48098
48159
  try {
48099
48160
  const round = await runInferenceRound(context, this.model);
@@ -48151,7 +48212,8 @@ var SurgeonOpenCode = class extends BaseObserver {
48151
48212
  opencodeBin: opts.opencodeBin ?? "opencode",
48152
48213
  timeoutMs: opts.timeoutMs ?? 3e5,
48153
48214
  snapshot: opts.snapshot,
48154
- resolveRoute: opts.resolveRoute
48215
+ resolveRoute: opts.resolveRoute,
48216
+ escalationRoute: opts.escalationRoute
48155
48217
  };
48156
48218
  }
48157
48219
  async idle() {
@@ -48175,7 +48237,7 @@ var SurgeonOpenCode = class extends BaseObserver {
48175
48237
  }
48176
48238
  async evaluateWithLlm(failure) {
48177
48239
  const snap = this.opts.snapshot();
48178
- const userPrompt = buildSurgeonPrompt(snap, failure, this.opts.resolveRoute);
48240
+ const userPrompt = buildSurgeonPrompt(snap, failure, this.opts.resolveRoute, this.opts.escalationRoute);
48179
48241
  const prompt = `${SURGEON_SYSTEM_PROMPT}
48180
48242
 
48181
48243
  ${userPrompt}`;
@@ -48231,7 +48293,8 @@ var SurgeonPi = class extends BaseObserver {
48231
48293
  piBin: opts.piBin ?? "pi",
48232
48294
  timeoutMs: opts.timeoutMs ?? 3e5,
48233
48295
  snapshot: opts.snapshot,
48234
- resolveRoute: opts.resolveRoute
48296
+ resolveRoute: opts.resolveRoute,
48297
+ escalationRoute: opts.escalationRoute
48235
48298
  };
48236
48299
  }
48237
48300
  async idle() {
@@ -48255,7 +48318,7 @@ var SurgeonPi = class extends BaseObserver {
48255
48318
  }
48256
48319
  async evaluateWithLlm(failure) {
48257
48320
  const snap = this.opts.snapshot();
48258
- const userPrompt = buildSurgeonPrompt(snap, failure, this.opts.resolveRoute);
48321
+ const userPrompt = buildSurgeonPrompt(snap, failure, this.opts.resolveRoute, this.opts.escalationRoute);
48259
48322
  const prompt = `${SURGEON_SYSTEM_PROMPT}
48260
48323
 
48261
48324
  ${userPrompt}`;
@@ -48552,16 +48615,20 @@ async function orchestrate(config) {
48552
48615
  return null;
48553
48616
  }
48554
48617
  } : void 0;
48618
+ const surgeonEscalationModel = config.surgeonModel ?? (surgeonLlm === "openai" ? "gpt-5.5" : surgeonLlm === "claude" ? "opus" : void 0);
48619
+ const escalationRoute = surgeonEscalationModel && !config.storyModel ? `${surgeonLlm}:${surgeonEscalationModel}` : void 0;
48555
48620
  if (surgeonLlm === "openai") {
48556
48621
  surgeon = new SurgeonOpenAI({
48557
48622
  snapshot,
48558
48623
  resolveRoute,
48624
+ escalationRoute,
48559
48625
  model: config.surgeonModel ?? "gpt-5.5"
48560
48626
  });
48561
48627
  } else if (surgeonLlm === "codex") {
48562
48628
  surgeon = new SurgeonCodex({
48563
48629
  snapshot,
48564
48630
  resolveRoute,
48631
+ escalationRoute,
48565
48632
  useLlm: config.surgeonUseLlm ?? true,
48566
48633
  model: config.surgeonModel
48567
48634
  });
@@ -48569,6 +48636,7 @@ async function orchestrate(config) {
48569
48636
  surgeon = new SurgeonOpenCode({
48570
48637
  snapshot,
48571
48638
  resolveRoute,
48639
+ escalationRoute,
48572
48640
  useLlm: config.surgeonUseLlm ?? true,
48573
48641
  model: config.surgeonModel
48574
48642
  });
@@ -48576,6 +48644,7 @@ async function orchestrate(config) {
48576
48644
  surgeon = new SurgeonPi({
48577
48645
  snapshot,
48578
48646
  resolveRoute,
48647
+ escalationRoute,
48579
48648
  useLlm: config.surgeonUseLlm ?? true,
48580
48649
  model: config.surgeonModel
48581
48650
  });
@@ -48583,6 +48652,7 @@ async function orchestrate(config) {
48583
48652
  surgeon = new Surgeon({
48584
48653
  snapshot,
48585
48654
  resolveRoute,
48655
+ escalationRoute,
48586
48656
  useLlm: config.surgeonUseLlm ?? false,
48587
48657
  model: config.surgeonModel ?? "opus"
48588
48658
  });
@@ -48732,6 +48802,12 @@ async function orchestrate(config) {
48732
48802
  const aborted = storyFactory.abort(storyId);
48733
48803
  if (aborted && emitTui) {
48734
48804
  emit({ type: "story_log", id: storyId, line: `\u26A0 ${reason} \u2014 aborting so it can be split/escalated` });
48805
+ emit({
48806
+ type: "activity",
48807
+ id: storyId,
48808
+ kind: "warn",
48809
+ text: `Supervisor paused ${storyId}: ${reason}. It will be retried or replanned.`
48810
+ });
48735
48811
  }
48736
48812
  }
48737
48813
  });