baro-ai 0.70.11 → 0.70.13
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/run-architect.mjs.map +1 -1
- package/dist/run-planner.mjs +310 -62
- package/dist/run-planner.mjs.map +1 -1
- package/dist/runner.mjs +1 -1
- package/dist/runner.mjs.map +1 -1
- package/package.json +1 -1
package/dist/run-planner.mjs
CHANGED
|
@@ -22283,38 +22283,29 @@ If the goal is NON-TRIVIAL: decompose normally per the rules below.
|
|
|
22283
22283
|
When in doubt, prefer FEWER stories over more. A single 2-file story is better
|
|
22284
22284
|
than two artificially-split 1-file stories.
|
|
22285
22285
|
|
|
22286
|
-
|
|
22287
|
-
baro
|
|
22288
|
-
|
|
22289
|
-
|
|
22290
|
-
|
|
22291
|
-
|
|
22292
|
-
|
|
22293
|
-
|
|
22294
|
-
|
|
22295
|
-
|
|
22296
|
-
|
|
22297
|
-
-
|
|
22298
|
-
|
|
22299
|
-
|
|
22300
|
-
|
|
22301
|
-
|
|
22302
|
-
-
|
|
22303
|
-
|
|
22304
|
-
|
|
22305
|
-
|
|
22306
|
-
|
|
22307
|
-
|
|
22308
|
-
|
|
22309
|
-
- "S1 = setup, then everything dependsOn S1" when S1 only adds an
|
|
22310
|
-
interface/abstraction the other stories don't actually consume.
|
|
22311
|
-
- One-story-per-level "staircase" plans for goals that obviously have
|
|
22312
|
-
independent pieces (e.g. five new providers, three new endpoints, four
|
|
22313
|
-
new components).
|
|
22314
|
-
|
|
22315
|
-
Target shape: most non-trivial plans should have AT LEAST one DAG level with
|
|
22316
|
-
2+ siblings. If your output is a single linear chain, re-examine \u2014 you almost
|
|
22317
|
-
certainly over-specified \`dependsOn\`.
|
|
22286
|
+
RUN SHAPE:
|
|
22287
|
+
baro can run focused, sequential, or parallel work. Parallelism is valuable only
|
|
22288
|
+
when stories have independent write surfaces. Do NOT create parallel stories
|
|
22289
|
+
that edit the same file, component, state machine, schema, or API contract.
|
|
22290
|
+
|
|
22291
|
+
Mode semantics:
|
|
22292
|
+
- focused: exactly one story. Use this for one bug, one UI/component issue,
|
|
22293
|
+
one failing build/runtime error, or anything likely centered on a shared
|
|
22294
|
+
file/surface. The story should carry enough context to finish the PR.
|
|
22295
|
+
- sequential: several small stories with real dependencies. Use this when the
|
|
22296
|
+
work is one feature that must be implemented in ordered steps.
|
|
22297
|
+
- parallel: a DAG with sibling stories only where you can prove independence
|
|
22298
|
+
(different files/modules/contracts). Parallelism requires a reason; do not
|
|
22299
|
+
fan out just to use agents.
|
|
22300
|
+
|
|
22301
|
+
Dependency rules:
|
|
22302
|
+
- Stories touching disjoint files/modules may run in parallel.
|
|
22303
|
+
- Stories touching the same file/component/state/API must be sequential or one
|
|
22304
|
+
focused story.
|
|
22305
|
+
- Only add dependsOn when story B literally cannot start until A is merged
|
|
22306
|
+
because B imports a symbol A defines, modifies a file A creates, or relies
|
|
22307
|
+
on a schema/API A introduces.
|
|
22308
|
+
- Decorative chains are bad, but unsafe parallel edits are worse.
|
|
22318
22309
|
|
|
22319
22310
|
Output ONLY valid JSON matching this exact schema (no markdown, no explanation, just JSON):
|
|
22320
22311
|
{
|
|
@@ -22375,6 +22366,139 @@ Rules:
|
|
|
22375
22366
|
- IDs: S1, S2, S3...
|
|
22376
22367
|
- Build on existing code, don't recreate what exists
|
|
22377
22368
|
- Output ONLY the JSON, nothing else`;
|
|
22369
|
+
function heuristicModeContract(args) {
|
|
22370
|
+
if (args.quick) {
|
|
22371
|
+
return {
|
|
22372
|
+
mode: "focused",
|
|
22373
|
+
confidence: 1,
|
|
22374
|
+
reason: "Quick mode was explicitly requested.",
|
|
22375
|
+
maxStories: 1,
|
|
22376
|
+
parallelism: 1
|
|
22377
|
+
};
|
|
22378
|
+
}
|
|
22379
|
+
const goal = args.goal.toLowerCase();
|
|
22380
|
+
const bugLike = /\b(fix|bug|error|crash|broken|console|build|doesn'?t|isn'?t|still|again|issue|problem|wrong|shifted|display|render)\b/.test(goal);
|
|
22381
|
+
const uiLike = /\b(ui|frontend|react|component|button|card|tab|modal|page|screen|css|style|layout|episode|season)\b/.test(goal);
|
|
22382
|
+
const bigLike = /\b(refactor|rewrite|migrate|redesign|implement|add support|multiple|several|backend|frontend|database|api|tests|docs)\b/.test(goal);
|
|
22383
|
+
if (bugLike || uiLike && !bigLike) {
|
|
22384
|
+
return {
|
|
22385
|
+
mode: "focused",
|
|
22386
|
+
confidence: 0.7,
|
|
22387
|
+
reason: "The goal looks like a localized bugfix/follow-up likely centered on shared UI or one code surface.",
|
|
22388
|
+
maxStories: 1,
|
|
22389
|
+
parallelism: 1
|
|
22390
|
+
};
|
|
22391
|
+
}
|
|
22392
|
+
if (bigLike) {
|
|
22393
|
+
return {
|
|
22394
|
+
mode: "sequential",
|
|
22395
|
+
confidence: 0.55,
|
|
22396
|
+
reason: "The goal may need multiple steps, but no LLM intake proved independent write surfaces.",
|
|
22397
|
+
maxStories: 5,
|
|
22398
|
+
parallelism: 1
|
|
22399
|
+
};
|
|
22400
|
+
}
|
|
22401
|
+
return {
|
|
22402
|
+
mode: "focused",
|
|
22403
|
+
confidence: 0.5,
|
|
22404
|
+
reason: "Uncertain goals default to focused mode to avoid unsafe parallel decomposition.",
|
|
22405
|
+
maxStories: 1,
|
|
22406
|
+
parallelism: 1
|
|
22407
|
+
};
|
|
22408
|
+
}
|
|
22409
|
+
function renderModeContract(decision) {
|
|
22410
|
+
const lines = [
|
|
22411
|
+
`mode: ${decision.mode}`,
|
|
22412
|
+
`confidence: ${decision.confidence}`,
|
|
22413
|
+
`reason: ${decision.reason}`
|
|
22414
|
+
];
|
|
22415
|
+
if (decision.maxStories) lines.push(`maxStories: ${decision.maxStories}`);
|
|
22416
|
+
if (decision.parallelism) lines.push(`parallelism: ${decision.parallelism}`);
|
|
22417
|
+
if (decision.mode === "focused") {
|
|
22418
|
+
lines.push(
|
|
22419
|
+
'Planner rules: output EXACTLY ONE story. Do not split. Set model to "opus" so this focused run uses the strong route.',
|
|
22420
|
+
"The story must include enough implementation context and acceptance criteria for one agent to finish the PR."
|
|
22421
|
+
);
|
|
22422
|
+
} else if (decision.mode === "sequential") {
|
|
22423
|
+
lines.push(
|
|
22424
|
+
"Planner rules: output a small ordered chain. Use dependsOn for real shared-surface dependencies.",
|
|
22425
|
+
"Do not create parallel siblings that edit the same file/component/state/API. Keep each story cheap-model-capable."
|
|
22426
|
+
);
|
|
22427
|
+
} else {
|
|
22428
|
+
lines.push(
|
|
22429
|
+
"Planner rules: output parallel siblings only where write surfaces are independent.",
|
|
22430
|
+
"For each sibling story, name its expected write surface in the description. Shared files/components must be sequential."
|
|
22431
|
+
);
|
|
22432
|
+
}
|
|
22433
|
+
return lines.join("\n");
|
|
22434
|
+
}
|
|
22435
|
+
function buildIntakePrompt(args) {
|
|
22436
|
+
if (args.quick) {
|
|
22437
|
+
return JSON.stringify({
|
|
22438
|
+
mode: "focused",
|
|
22439
|
+
confidence: 1,
|
|
22440
|
+
reason: "Quick mode was explicitly requested.",
|
|
22441
|
+
maxStories: 1,
|
|
22442
|
+
parallelism: 1
|
|
22443
|
+
});
|
|
22444
|
+
}
|
|
22445
|
+
return [
|
|
22446
|
+
"You are Baro Intake. Choose the execution shape BEFORE planning.",
|
|
22447
|
+
"",
|
|
22448
|
+
"Return ONLY valid JSON with this schema:",
|
|
22449
|
+
'{"mode":"focused|sequential|parallel","confidence":0.0,"reason":"short","maxStories":1,"parallelism":1}',
|
|
22450
|
+
"",
|
|
22451
|
+
"Definitions:",
|
|
22452
|
+
"- focused: one strong agent/story. Use for small bugfixes, UI tweaks, build/runtime errors, one component/file/surface, or unclear shared-write work.",
|
|
22453
|
+
"- sequential: multiple small ordered stories. Use when one feature naturally requires steps that touch shared code.",
|
|
22454
|
+
"- parallel: only when there are independent write surfaces (different modules/files/contracts) that can safely run at the same time.",
|
|
22455
|
+
"",
|
|
22456
|
+
"Bias rules:",
|
|
22457
|
+
"- If several agents would edit the same file/component/state/schema, choose focused or sequential, not parallel.",
|
|
22458
|
+
"- If the prompt is a follow-up bug report from screenshots/console output, choose focused unless it clearly spans independent surfaces.",
|
|
22459
|
+
"- If uncertain, choose focused. Unsafe parallelism is worse than leaving speed on the table.",
|
|
22460
|
+
"- maxStories is a cap for the planner, not a target.",
|
|
22461
|
+
"",
|
|
22462
|
+
args.projectContext?.trim() ? `Project context summary:
|
|
22463
|
+
${args.projectContext.trim().slice(0, 3e3)}
|
|
22464
|
+
` : "",
|
|
22465
|
+
args.decisionDocument?.trim() ? `Architect decision exists; prefer a compact plan that implements it.
|
|
22466
|
+
${args.decisionDocument.trim().slice(0, 3e3)}
|
|
22467
|
+
` : "",
|
|
22468
|
+
`User goal:
|
|
22469
|
+
${args.goal.trim()}`
|
|
22470
|
+
].filter(Boolean).join("\n");
|
|
22471
|
+
}
|
|
22472
|
+
function parseModeContract(text) {
|
|
22473
|
+
const json = JSON.parse(extractJsonObject(text));
|
|
22474
|
+
const mode = json.mode === "parallel" || json.mode === "sequential" || json.mode === "focused" ? json.mode : "focused";
|
|
22475
|
+
const confidence = Number.isFinite(Number(json.confidence)) ? Math.max(0, Math.min(1, Number(json.confidence))) : 0.5;
|
|
22476
|
+
return {
|
|
22477
|
+
mode,
|
|
22478
|
+
confidence,
|
|
22479
|
+
reason: typeof json.reason === "string" && json.reason.trim() ? json.reason.trim() : "No reason supplied by intake.",
|
|
22480
|
+
maxStories: Number.isFinite(Number(json.maxStories)) ? Math.max(1, Math.floor(Number(json.maxStories))) : void 0,
|
|
22481
|
+
parallelism: Number.isFinite(Number(json.parallelism)) ? Math.max(1, Math.floor(Number(json.parallelism))) : void 0
|
|
22482
|
+
};
|
|
22483
|
+
}
|
|
22484
|
+
function extractJsonObject(text) {
|
|
22485
|
+
const trimmed = text.trim();
|
|
22486
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) return trimmed;
|
|
22487
|
+
const fence = trimmed.match(/```(?:json)?\s*(\{[\s\S]*?\})\s*```/);
|
|
22488
|
+
if (fence) return fence[1];
|
|
22489
|
+
const start = trimmed.indexOf("{");
|
|
22490
|
+
if (start < 0) throw new Error(`no JSON object in response: ${trimmed.slice(0, 200)}`);
|
|
22491
|
+
let depth = 0;
|
|
22492
|
+
for (let i2 = start; i2 < trimmed.length; i2++) {
|
|
22493
|
+
const ch = trimmed[i2];
|
|
22494
|
+
if (ch === "{") depth++;
|
|
22495
|
+
else if (ch === "}") {
|
|
22496
|
+
depth--;
|
|
22497
|
+
if (depth === 0) return trimmed.slice(start, i2 + 1);
|
|
22498
|
+
}
|
|
22499
|
+
}
|
|
22500
|
+
throw new Error(`unbalanced JSON in response: ${trimmed.slice(0, 200)}`);
|
|
22501
|
+
}
|
|
22378
22502
|
function buildPlannerUserMessage(args) {
|
|
22379
22503
|
const sections = [];
|
|
22380
22504
|
if (args.projectContext && args.projectContext.trim().length > 0) {
|
|
@@ -22395,6 +22519,15 @@ function buildPlannerUserMessage(args) {
|
|
|
22395
22519
|
sections.push("---");
|
|
22396
22520
|
sections.push("");
|
|
22397
22521
|
}
|
|
22522
|
+
const modeContract = typeof args.modeContract === "string" ? args.modeContract : args.modeContract ? renderModeContract(args.modeContract) : void 0;
|
|
22523
|
+
if (modeContract && modeContract.trim().length > 0) {
|
|
22524
|
+
sections.push("EXECUTION MODE CONTRACT (chosen by Baro Intake \u2014 obey it):");
|
|
22525
|
+
sections.push("");
|
|
22526
|
+
sections.push(modeContract.trim());
|
|
22527
|
+
sections.push("");
|
|
22528
|
+
sections.push("---");
|
|
22529
|
+
sections.push("");
|
|
22530
|
+
}
|
|
22398
22531
|
if (args.quick) {
|
|
22399
22532
|
sections.push(
|
|
22400
22533
|
"QUICK MODE OVERRIDE \u2014 the user invoked `baro --quick`. They have told us this goal is trivial. You MUST output EXACTLY ONE story. Do not split. Do not decompose. Do not add a `verify` story. If you genuinely cannot do this in one story, emit the one story anyway with a description that explains what's missing; the user will rerun without --quick. One story, tight acceptance, minimum useful test command."
|
|
@@ -22424,11 +22557,19 @@ function effortTimeoutMs(effort) {
|
|
|
22424
22557
|
}
|
|
22425
22558
|
}
|
|
22426
22559
|
async function runPlannerClaude(opts) {
|
|
22560
|
+
const modeContract = await runClaudeIntake(opts).catch((e2) => {
|
|
22561
|
+
process.stderr.write(`[planner-claude] intake failed (${e2?.message ?? String(e2)}) \u2014 using heuristic mode contract
|
|
22562
|
+
`);
|
|
22563
|
+
return heuristicModeContract(opts);
|
|
22564
|
+
});
|
|
22565
|
+
process.stderr.write(`[planner-claude] intake mode=${modeContract.mode} confidence=${modeContract.confidence}
|
|
22566
|
+
`);
|
|
22427
22567
|
const userMessage = buildPlannerUserMessage({
|
|
22428
22568
|
goal: opts.goal,
|
|
22429
22569
|
decisionDocument: opts.decisionDocument,
|
|
22430
22570
|
quick: opts.quick,
|
|
22431
|
-
projectContext: opts.projectContext
|
|
22571
|
+
projectContext: opts.projectContext,
|
|
22572
|
+
modeContract
|
|
22432
22573
|
});
|
|
22433
22574
|
const { stdout } = await execFileAsync(
|
|
22434
22575
|
opts.claudeBin ?? "claude",
|
|
@@ -22456,9 +22597,37 @@ async function runPlannerClaude(opts) {
|
|
|
22456
22597
|
if (!planText) {
|
|
22457
22598
|
throw new Error("PlannerClaude: claude returned empty result");
|
|
22458
22599
|
}
|
|
22459
|
-
return
|
|
22600
|
+
return extractJsonObject2(planText);
|
|
22460
22601
|
}
|
|
22461
|
-
function
|
|
22602
|
+
async function runClaudeIntake(opts) {
|
|
22603
|
+
if (opts.quick) return heuristicModeContract(opts);
|
|
22604
|
+
const { stdout } = await execFileAsync(
|
|
22605
|
+
opts.claudeBin ?? "claude",
|
|
22606
|
+
[
|
|
22607
|
+
"--print",
|
|
22608
|
+
"--output-format",
|
|
22609
|
+
"json",
|
|
22610
|
+
...opts.model ? ["--model", opts.model] : [],
|
|
22611
|
+
...opts.effort ? ["--effort", opts.effort] : [],
|
|
22612
|
+
"--permission-mode",
|
|
22613
|
+
"bypassPermissions",
|
|
22614
|
+
"--system-prompt",
|
|
22615
|
+
"You classify software tasks for an autonomous PR workflow. Output JSON only.",
|
|
22616
|
+
"-p",
|
|
22617
|
+
buildIntakePrompt(opts)
|
|
22618
|
+
],
|
|
22619
|
+
{
|
|
22620
|
+
cwd: opts.cwd,
|
|
22621
|
+
timeout: Math.min(opts.timeoutMs ?? effortTimeoutMs(opts.effort), 18e4),
|
|
22622
|
+
maxBuffer: 2 * 1024 * 1024
|
|
22623
|
+
}
|
|
22624
|
+
);
|
|
22625
|
+
const wrapper = JSON.parse(stdout);
|
|
22626
|
+
const text = typeof wrapper.result === "string" ? wrapper.result.trim() : "";
|
|
22627
|
+
if (!text) throw new Error("empty intake result");
|
|
22628
|
+
return parseModeContract(text);
|
|
22629
|
+
}
|
|
22630
|
+
function extractJsonObject2(text) {
|
|
22462
22631
|
const trimmed = text.trim();
|
|
22463
22632
|
if (trimmed.startsWith("{") && trimmed.endsWith("}")) return trimmed;
|
|
22464
22633
|
const fence = trimmed.match(/```(?:json)?\s*(\{[\s\S]*?\})\s*```/);
|
|
@@ -22603,11 +22772,19 @@ ${item.text}` : item.text;
|
|
|
22603
22772
|
|
|
22604
22773
|
// ../baro-orchestrator/src/planning/planner-codex.ts
|
|
22605
22774
|
async function runPlannerCodex(opts) {
|
|
22775
|
+
const modeContract = await runCodexIntake(opts).catch((e2) => {
|
|
22776
|
+
process.stderr.write(`[planner-codex] intake failed (${e2?.message ?? String(e2)}) \u2014 using heuristic mode contract
|
|
22777
|
+
`);
|
|
22778
|
+
return heuristicModeContract(opts);
|
|
22779
|
+
});
|
|
22780
|
+
process.stderr.write(`[planner-codex] intake mode=${modeContract.mode} confidence=${modeContract.confidence}
|
|
22781
|
+
`);
|
|
22606
22782
|
const userMessage = buildPlannerUserMessage({
|
|
22607
22783
|
goal: opts.goal,
|
|
22608
22784
|
decisionDocument: opts.decisionDocument,
|
|
22609
22785
|
quick: opts.quick,
|
|
22610
|
-
projectContext: opts.projectContext
|
|
22786
|
+
projectContext: opts.projectContext,
|
|
22787
|
+
modeContract
|
|
22611
22788
|
});
|
|
22612
22789
|
const prompt = `${PLANNER_SYSTEM_PROMPT}
|
|
22613
22790
|
|
|
@@ -22624,9 +22801,23 @@ ${userMessage}`;
|
|
|
22624
22801
|
if (!planText) {
|
|
22625
22802
|
throw new Error("PlannerCodex: codex returned empty result");
|
|
22626
22803
|
}
|
|
22627
|
-
return
|
|
22804
|
+
return extractJsonObject3(planText);
|
|
22628
22805
|
}
|
|
22629
|
-
function
|
|
22806
|
+
async function runCodexIntake(opts) {
|
|
22807
|
+
if (opts.quick) return heuristicModeContract(opts);
|
|
22808
|
+
const text = await runCodexOneShot({
|
|
22809
|
+
prompt: `You classify software tasks for an autonomous PR workflow. Output JSON only.
|
|
22810
|
+
|
|
22811
|
+
${buildIntakePrompt(opts)}`,
|
|
22812
|
+
cwd: opts.cwd,
|
|
22813
|
+
model: opts.model,
|
|
22814
|
+
codexBin: opts.codexBin,
|
|
22815
|
+
timeoutMs: Math.min(opts.timeoutMs ?? 9e5, 18e4),
|
|
22816
|
+
label: "codex-intake"
|
|
22817
|
+
});
|
|
22818
|
+
return parseModeContract(text.trim());
|
|
22819
|
+
}
|
|
22820
|
+
function extractJsonObject3(text) {
|
|
22630
22821
|
const trimmed = text.trim();
|
|
22631
22822
|
if (trimmed.startsWith("{") && trimmed.endsWith("}")) return trimmed;
|
|
22632
22823
|
const fence = trimmed.match(/```(?:json)?\s*(\{[\s\S]*?\})\s*```/);
|
|
@@ -40110,6 +40301,19 @@ function pickModel(name) {
|
|
|
40110
40301
|
}
|
|
40111
40302
|
async function runPlannerOpenAI(opts) {
|
|
40112
40303
|
const model = pickModel(opts.model ?? "gpt-5.5");
|
|
40304
|
+
const intake = await decideExecutionMode(opts, model).catch((e2) => {
|
|
40305
|
+
process.stderr.write(`[planner-openai] intake failed (${e2?.message ?? String(e2)}) \u2014 defaulting to focused
|
|
40306
|
+
`);
|
|
40307
|
+
return {
|
|
40308
|
+
mode: "focused",
|
|
40309
|
+
confidence: 0,
|
|
40310
|
+
reason: "Intake failed, so Baro uses the conservative focused mode instead of unsafe parallel decomposition.",
|
|
40311
|
+
maxStories: 1,
|
|
40312
|
+
parallelism: 1
|
|
40313
|
+
};
|
|
40314
|
+
});
|
|
40315
|
+
process.stderr.write(`[planner-openai] intake mode=${intake.mode} confidence=${intake.confidence} reason=${oneLine(intake.reason).slice(0, 180)}
|
|
40316
|
+
`);
|
|
40113
40317
|
const tools = createCodebaseTools(opts.cwd);
|
|
40114
40318
|
setModelTools(model, tools);
|
|
40115
40319
|
let context = ModelContext.create("planner").addContextItem(SystemMessageItem.create(PLANNER_SYSTEM_PROMPT)).addContextItem(
|
|
@@ -40118,7 +40322,8 @@ async function runPlannerOpenAI(opts) {
|
|
|
40118
40322
|
goal: opts.goal,
|
|
40119
40323
|
decisionDocument: opts.decisionDocument,
|
|
40120
40324
|
quick: opts.quick,
|
|
40121
|
-
projectContext: opts.projectContext
|
|
40325
|
+
projectContext: opts.projectContext,
|
|
40326
|
+
modeContract: renderModeContract(intake)
|
|
40122
40327
|
})
|
|
40123
40328
|
)
|
|
40124
40329
|
);
|
|
@@ -40187,7 +40392,7 @@ async function runPlannerOpenAI(opts) {
|
|
|
40187
40392
|
process.stderr.write(`[planner-openai] ${usage.summary()}
|
|
40188
40393
|
`);
|
|
40189
40394
|
try {
|
|
40190
|
-
return
|
|
40395
|
+
return extractJsonObject(raw);
|
|
40191
40396
|
} catch (e2) {
|
|
40192
40397
|
process.stderr.write(`[planner-openai] invalid final JSON (${e2?.message ?? String(e2)}) \u2014 using fallback PRD
|
|
40193
40398
|
`);
|
|
@@ -40199,6 +40404,24 @@ async function runPlannerOpenAI(opts) {
|
|
|
40199
40404
|
`);
|
|
40200
40405
|
return fallbackPrdJson(opts.goal, `Planner exceeded maxRounds=${maxRounds} without producing a final plan.`);
|
|
40201
40406
|
}
|
|
40407
|
+
async function decideExecutionMode(opts, plannerModel) {
|
|
40408
|
+
if (opts.quick) {
|
|
40409
|
+
return {
|
|
40410
|
+
mode: "focused",
|
|
40411
|
+
confidence: 1,
|
|
40412
|
+
reason: "The user explicitly invoked quick mode.",
|
|
40413
|
+
maxStories: 1,
|
|
40414
|
+
parallelism: 1
|
|
40415
|
+
};
|
|
40416
|
+
}
|
|
40417
|
+
const intakeModel = pickModel(process.env.BARO_INTAKE_MODEL || plannerModel.specification.name);
|
|
40418
|
+
setModelTools(intakeModel, []);
|
|
40419
|
+
const prompt = buildIntakePrompt(opts);
|
|
40420
|
+
const context = ModelContext.create("intake").addContextItem(SystemMessageItem.create("You classify software tasks for an autonomous PR workflow. Output JSON only.")).addContextItem(UserMessageItem.create(prompt));
|
|
40421
|
+
const result = await runInferenceRound(context, intakeModel);
|
|
40422
|
+
const raw = result.items.filter((i2) => i2.type === "message").map((i2) => i2.toJSON().content?.[0]?.text ?? "").join("\n").trim();
|
|
40423
|
+
return parseModeContract(raw);
|
|
40424
|
+
}
|
|
40202
40425
|
function numberEnv(name, fallback) {
|
|
40203
40426
|
const n = Number(process.env[name]);
|
|
40204
40427
|
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
@@ -40264,26 +40487,6 @@ function setModelTools(model, tools) {
|
|
|
40264
40487
|
}
|
|
40265
40488
|
m2.setTools(tools);
|
|
40266
40489
|
}
|
|
40267
|
-
function extractJsonObject3(text) {
|
|
40268
|
-
const trimmed = text.trim();
|
|
40269
|
-
if (trimmed.startsWith("{") && trimmed.endsWith("}")) return trimmed;
|
|
40270
|
-
const fence = trimmed.match(/```(?:json)?\s*(\{[\s\S]*?\})\s*```/);
|
|
40271
|
-
if (fence) return fence[1];
|
|
40272
|
-
const start = trimmed.indexOf("{");
|
|
40273
|
-
if (start < 0) {
|
|
40274
|
-
throw new Error(`PlannerOpenAI: no JSON object in response: ${trimmed.slice(0, 200)}`);
|
|
40275
|
-
}
|
|
40276
|
-
let depth = 0;
|
|
40277
|
-
for (let i2 = start; i2 < trimmed.length; i2++) {
|
|
40278
|
-
const ch = trimmed[i2];
|
|
40279
|
-
if (ch === "{") depth++;
|
|
40280
|
-
else if (ch === "}") {
|
|
40281
|
-
depth--;
|
|
40282
|
-
if (depth === 0) return trimmed.slice(start, i2 + 1);
|
|
40283
|
-
}
|
|
40284
|
-
}
|
|
40285
|
-
throw new Error(`PlannerOpenAI: unbalanced JSON in response: ${trimmed.slice(0, 200)}`);
|
|
40286
|
-
}
|
|
40287
40490
|
|
|
40288
40491
|
// ../baro-orchestrator/src/opencode-one-shot.ts
|
|
40289
40492
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -40407,11 +40610,19 @@ ${part.text}` : part.text;
|
|
|
40407
40610
|
|
|
40408
40611
|
// ../baro-orchestrator/src/planning/planner-opencode.ts
|
|
40409
40612
|
async function runPlannerOpenCode(opts) {
|
|
40613
|
+
const modeContract = await runOpenCodeIntake(opts).catch((e2) => {
|
|
40614
|
+
process.stderr.write(`[planner-opencode] intake failed (${e2?.message ?? String(e2)}) \u2014 using heuristic mode contract
|
|
40615
|
+
`);
|
|
40616
|
+
return heuristicModeContract(opts);
|
|
40617
|
+
});
|
|
40618
|
+
process.stderr.write(`[planner-opencode] intake mode=${modeContract.mode} confidence=${modeContract.confidence}
|
|
40619
|
+
`);
|
|
40410
40620
|
const userMessage = buildPlannerUserMessage({
|
|
40411
40621
|
goal: opts.goal,
|
|
40412
40622
|
decisionDocument: opts.decisionDocument,
|
|
40413
40623
|
quick: opts.quick,
|
|
40414
|
-
projectContext: opts.projectContext
|
|
40624
|
+
projectContext: opts.projectContext,
|
|
40625
|
+
modeContract
|
|
40415
40626
|
});
|
|
40416
40627
|
const prompt = `${PLANNER_SYSTEM_PROMPT}
|
|
40417
40628
|
|
|
@@ -40430,6 +40641,20 @@ ${userMessage}`;
|
|
|
40430
40641
|
}
|
|
40431
40642
|
return extractJsonObject4(planText);
|
|
40432
40643
|
}
|
|
40644
|
+
async function runOpenCodeIntake(opts) {
|
|
40645
|
+
if (opts.quick) return heuristicModeContract(opts);
|
|
40646
|
+
const text = await runOpenCodeOneShot({
|
|
40647
|
+
prompt: `You classify software tasks for an autonomous PR workflow. Output JSON only.
|
|
40648
|
+
|
|
40649
|
+
${buildIntakePrompt(opts)}`,
|
|
40650
|
+
cwd: opts.cwd,
|
|
40651
|
+
model: opts.model,
|
|
40652
|
+
opencodeBin: opts.opencodeBin,
|
|
40653
|
+
timeoutMs: Math.min(opts.timeoutMs ?? 9e5, 18e4),
|
|
40654
|
+
label: "opencode-intake"
|
|
40655
|
+
});
|
|
40656
|
+
return parseModeContract(text.trim());
|
|
40657
|
+
}
|
|
40433
40658
|
function extractJsonObject4(text) {
|
|
40434
40659
|
const trimmed = text.trim();
|
|
40435
40660
|
if (trimmed.startsWith("{") && trimmed.endsWith("}")) return trimmed;
|
|
@@ -40623,11 +40848,19 @@ ${block.text}` : block.text;
|
|
|
40623
40848
|
|
|
40624
40849
|
// ../baro-orchestrator/src/planning/planner-pi.ts
|
|
40625
40850
|
async function runPlannerPi(opts) {
|
|
40851
|
+
const modeContract = await runPiIntake(opts).catch((e2) => {
|
|
40852
|
+
process.stderr.write(`[planner-pi] intake failed (${e2?.message ?? String(e2)}) \u2014 using heuristic mode contract
|
|
40853
|
+
`);
|
|
40854
|
+
return heuristicModeContract(opts);
|
|
40855
|
+
});
|
|
40856
|
+
process.stderr.write(`[planner-pi] intake mode=${modeContract.mode} confidence=${modeContract.confidence}
|
|
40857
|
+
`);
|
|
40626
40858
|
const userMessage = buildPlannerUserMessage({
|
|
40627
40859
|
goal: opts.goal,
|
|
40628
40860
|
decisionDocument: opts.decisionDocument,
|
|
40629
40861
|
quick: opts.quick,
|
|
40630
|
-
projectContext: opts.projectContext
|
|
40862
|
+
projectContext: opts.projectContext,
|
|
40863
|
+
modeContract
|
|
40631
40864
|
});
|
|
40632
40865
|
const prompt = `${PLANNER_SYSTEM_PROMPT}
|
|
40633
40866
|
|
|
@@ -40647,6 +40880,21 @@ ${userMessage}`;
|
|
|
40647
40880
|
}
|
|
40648
40881
|
return extractJsonObject5(planText);
|
|
40649
40882
|
}
|
|
40883
|
+
async function runPiIntake(opts) {
|
|
40884
|
+
if (opts.quick) return heuristicModeContract(opts);
|
|
40885
|
+
const text = await runPiOneShot({
|
|
40886
|
+
prompt: `You classify software tasks for an autonomous PR workflow. Output JSON only.
|
|
40887
|
+
|
|
40888
|
+
${buildIntakePrompt(opts)}`,
|
|
40889
|
+
cwd: opts.cwd,
|
|
40890
|
+
provider: opts.provider,
|
|
40891
|
+
model: opts.model,
|
|
40892
|
+
piBin: opts.piBin,
|
|
40893
|
+
timeoutMs: Math.min(opts.timeoutMs ?? 9e5, 18e4),
|
|
40894
|
+
label: "pi-intake"
|
|
40895
|
+
});
|
|
40896
|
+
return parseModeContract(text.trim());
|
|
40897
|
+
}
|
|
40650
40898
|
function extractJsonObject5(text) {
|
|
40651
40899
|
const trimmed = text.trim();
|
|
40652
40900
|
if (trimmed.startsWith("{") && trimmed.endsWith("}")) return trimmed;
|