@pushpalsdev/cli 1.0.79 → 1.0.80
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/pushpals-cli.js +66 -3
- package/package.json +1 -1
- package/runtime/prompts/remotebuddy/autonomy_ideation_system_prompt.md +5 -3
- package/runtime/prompts/workerpals/openai_codex_default_system_prompt.md +1 -0
- package/runtime/prompts/workerpals/openai_codex_runtime_policy_appendix.md +1 -0
- package/runtime/prompts/workerpals/openai_codex_task_execute_system_prompt.md +1 -0
- package/runtime/sandbox/.pushpals-remotebuddy-fallback.js +536 -18
- package/runtime/sandbox/apps/workerpals/src/execute_job.ts +282 -33
- package/runtime/sandbox/apps/workerpals/src/workerpals_main.ts +113 -2
- package/runtime/sandbox/packages/shared/src/client_preflight.ts +2 -0
- package/runtime/sandbox/packages/shared/src/config.ts +1 -6
- package/runtime/sandbox/packages/shared/src/index.ts +19 -0
- package/runtime/sandbox/packages/shared/src/tooling.ts +422 -0
- package/runtime/sandbox/packages/shared/src/vision.ts +12 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_default_system_prompt.md +1 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_runtime_policy_appendix.md +1 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_task_execute_system_prompt.md +1 -0
- package/runtime/vision.example.md +125 -122
|
@@ -1088,7 +1088,7 @@ function loadPushPalsConfig(options = {}) {
|
|
|
1088
1088
|
const canonical = coerceAutonomyComponentConfigKey(rawKey);
|
|
1089
1089
|
if (!canonical)
|
|
1090
1090
|
continue;
|
|
1091
|
-
const parsed =
|
|
1091
|
+
const parsed = rawValue;
|
|
1092
1092
|
remoteAutonomyDispatchByComponent[canonical] = Number.isFinite(parsed) ? Math.max(0, Math.floor(parsed)) : 0;
|
|
1093
1093
|
}
|
|
1094
1094
|
const workerNode = getObject(merged, "workerpals");
|
|
@@ -1603,6 +1603,9 @@ function classifyHeadingBucket(heading) {
|
|
|
1603
1603
|
if (text.includes("non-goal") || text.includes("out of scope") || text.includes("not ")) {
|
|
1604
1604
|
return "nonGoals";
|
|
1605
1605
|
}
|
|
1606
|
+
if (text.includes("testing criteria") || text.includes("test criteria") || text.includes("required tests") || text.includes("required validation") || text.includes("validation criteria")) {
|
|
1607
|
+
return "testingCriteria";
|
|
1608
|
+
}
|
|
1606
1609
|
if (text.includes("measure") || text.includes("metric") || text.includes("good looks like")) {
|
|
1607
1610
|
return "metrics";
|
|
1608
1611
|
}
|
|
@@ -1694,6 +1697,7 @@ function extractVisionKeyItems(markdown) {
|
|
|
1694
1697
|
constraints: [],
|
|
1695
1698
|
nonGoals: [],
|
|
1696
1699
|
metrics: [],
|
|
1700
|
+
testingCriteria: [],
|
|
1697
1701
|
riskPolicy: [],
|
|
1698
1702
|
operatingModel: [],
|
|
1699
1703
|
governance: []
|
|
@@ -1720,11 +1724,24 @@ function extractVisionKeyItems(markdown) {
|
|
|
1720
1724
|
constraints: dedupeAndClamp(buckets.constraints),
|
|
1721
1725
|
nonGoals: dedupeAndClamp(buckets.nonGoals),
|
|
1722
1726
|
metrics: dedupeAndClamp(buckets.metrics),
|
|
1727
|
+
testingCriteria: dedupeAndClamp(buckets.testingCriteria),
|
|
1723
1728
|
riskPolicy: dedupeAndClamp(buckets.riskPolicy),
|
|
1724
1729
|
operatingModel: dedupeAndClamp(buckets.operatingModel),
|
|
1725
1730
|
governance: dedupeAndClamp(buckets.governance)
|
|
1726
1731
|
};
|
|
1727
1732
|
}
|
|
1733
|
+
// packages/shared/src/tooling.ts
|
|
1734
|
+
var KNOWN_TOOL_NAMES = new Set([
|
|
1735
|
+
"bun",
|
|
1736
|
+
"codex",
|
|
1737
|
+
"docker",
|
|
1738
|
+
"gh",
|
|
1739
|
+
"git",
|
|
1740
|
+
"node",
|
|
1741
|
+
"npm",
|
|
1742
|
+
"python",
|
|
1743
|
+
"shell"
|
|
1744
|
+
]);
|
|
1728
1745
|
// packages/shared/src/session_event_visibility.ts
|
|
1729
1746
|
var ALWAYS_VISIBLE_EVENT_TYPES = new Set(["question_asked"]);
|
|
1730
1747
|
// packages/shared/src/localbuddy_runtime.ts
|
|
@@ -4059,7 +4076,7 @@ class PersistentSessionMemory {
|
|
|
4059
4076
|
}
|
|
4060
4077
|
|
|
4061
4078
|
// apps/remotebuddy/src/remotebuddy_main.ts
|
|
4062
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
4079
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync5 } from "fs";
|
|
4063
4080
|
import { resolve as resolve5 } from "path";
|
|
4064
4081
|
|
|
4065
4082
|
// apps/remotebuddy/src/autonomous_engine.ts
|
|
@@ -4516,6 +4533,144 @@ function uniqueLowercaseTokens(values, max = 24) {
|
|
|
4516
4533
|
}
|
|
4517
4534
|
return out;
|
|
4518
4535
|
}
|
|
4536
|
+
var CATEGORY_KEYWORD_RULES = [
|
|
4537
|
+
{
|
|
4538
|
+
category: "product_core",
|
|
4539
|
+
pattern: /\b(core|primary|match|game|gameplay|battle|battlefield|player|decision|territor|combat|strategy|mission|workflow|editor|dashboard|api)\b/i
|
|
4540
|
+
},
|
|
4541
|
+
{
|
|
4542
|
+
category: "user_experience",
|
|
4543
|
+
pattern: /\b(user experience|ux|ui|readab|legib|clarity|clear|shell|screen|navigation|control|input|touch|mobile|visual|presentation|feedback|discoverable|usable)\b/i
|
|
4544
|
+
},
|
|
4545
|
+
{
|
|
4546
|
+
category: "onboarding",
|
|
4547
|
+
pattern: /\b(onboard|new user|first[- ]?time|tutorial|learn|help|guide|activation|setup)\b/i
|
|
4548
|
+
},
|
|
4549
|
+
{
|
|
4550
|
+
category: "reliability",
|
|
4551
|
+
pattern: /\b(reliab|stable|stability|startup|trust|regression|failure|resilien|recover|fallback|safe|crash|broken|blocker)\b/i
|
|
4552
|
+
},
|
|
4553
|
+
{
|
|
4554
|
+
category: "validation",
|
|
4555
|
+
pattern: /\b(validation|validate|test|smoke|coverage|browser|e2e|end[- ]?to[- ]?end|ci|check|quality)\b/i
|
|
4556
|
+
},
|
|
4557
|
+
{
|
|
4558
|
+
category: "performance",
|
|
4559
|
+
pattern: /\b(performance|latency|smooth|jitter|lag|throughput|fps|render|memory|speed|fast|responsive)\b/i
|
|
4560
|
+
},
|
|
4561
|
+
{
|
|
4562
|
+
category: "maintainability",
|
|
4563
|
+
pattern: /\b(maintain|refactor|cleanup|architecture|structure|modular|debt|simplify|consistency|coherent)\b/i
|
|
4564
|
+
},
|
|
4565
|
+
{
|
|
4566
|
+
category: "delivery_loop",
|
|
4567
|
+
pattern: /\b(autonom|agent|worker|delivery loop|reliable autonomous delivery|merge|review|pr|pull request|dispatch|orchestrat|planner|compiler|ideation)\b/i
|
|
4568
|
+
},
|
|
4569
|
+
{
|
|
4570
|
+
category: "governance",
|
|
4571
|
+
pattern: /\b(policy|permission|scope|guardrail|risk|constraint|governance|approval|audit|security|non[- ]?goal)\b/i
|
|
4572
|
+
},
|
|
4573
|
+
{
|
|
4574
|
+
category: "growth",
|
|
4575
|
+
pattern: /\b(growth|retention|conversion|activation|adoption|audience|returning|replay|engagement)\b/i
|
|
4576
|
+
},
|
|
4577
|
+
{
|
|
4578
|
+
category: "content",
|
|
4579
|
+
pattern: /\b(content|variety|skin|cosmetic|faction|map|ship|projectile|enemy|level|mode|character)\b/i
|
|
4580
|
+
}
|
|
4581
|
+
];
|
|
4582
|
+
var META_OBJECTIVE_CATEGORIES = new Set([
|
|
4583
|
+
"delivery_loop",
|
|
4584
|
+
"governance",
|
|
4585
|
+
"maintainability"
|
|
4586
|
+
]);
|
|
4587
|
+
function slugifyObjectiveId(value, fallback) {
|
|
4588
|
+
const slug = asString2(value).toLowerCase().replace(/`([^`]+)`/g, "$1").replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 80);
|
|
4589
|
+
return slug || fallback;
|
|
4590
|
+
}
|
|
4591
|
+
function categorizeVisionText(text) {
|
|
4592
|
+
const matched = [];
|
|
4593
|
+
for (const rule of CATEGORY_KEYWORD_RULES) {
|
|
4594
|
+
if (rule.pattern.test(text))
|
|
4595
|
+
matched.push(rule.category);
|
|
4596
|
+
}
|
|
4597
|
+
if (matched.length === 0) {
|
|
4598
|
+
return { primary: "unknown", secondary: [] };
|
|
4599
|
+
}
|
|
4600
|
+
const [primary, ...secondary] = matched;
|
|
4601
|
+
return {
|
|
4602
|
+
primary,
|
|
4603
|
+
secondary: [...new Set(secondary)].slice(0, 4)
|
|
4604
|
+
};
|
|
4605
|
+
}
|
|
4606
|
+
function sourceBucketSectionRef(sourceBucket, sectionNumbers) {
|
|
4607
|
+
const preferredByBucket = {
|
|
4608
|
+
priorities: ["6", "5", "4"],
|
|
4609
|
+
objectives: ["7", "6", "5"],
|
|
4610
|
+
metrics: ["4", "6"],
|
|
4611
|
+
testing_criteria: ["12", "9", "4"],
|
|
4612
|
+
guardrails: ["9", "10", "3"],
|
|
4613
|
+
constraints: ["9", "5"],
|
|
4614
|
+
risk_policy: ["9", "10"],
|
|
4615
|
+
operating_model: ["11", "7"],
|
|
4616
|
+
governance: ["10", "9"],
|
|
4617
|
+
target_users: ["1"],
|
|
4618
|
+
non_goals: ["5", "9"],
|
|
4619
|
+
section: ["6", "7", "4", "3"]
|
|
4620
|
+
};
|
|
4621
|
+
const available = new Set(sectionNumbers);
|
|
4622
|
+
for (const ref of preferredByBucket[sourceBucket] ?? []) {
|
|
4623
|
+
if (available.has(ref))
|
|
4624
|
+
return ref;
|
|
4625
|
+
}
|
|
4626
|
+
return sectionNumbers[0] ?? "";
|
|
4627
|
+
}
|
|
4628
|
+
function categoryObjectiveType(category) {
|
|
4629
|
+
switch (category) {
|
|
4630
|
+
case "product_core":
|
|
4631
|
+
case "user_experience":
|
|
4632
|
+
case "onboarding":
|
|
4633
|
+
case "content":
|
|
4634
|
+
case "growth":
|
|
4635
|
+
return "feature_small";
|
|
4636
|
+
case "performance":
|
|
4637
|
+
case "reliability":
|
|
4638
|
+
case "maintainability":
|
|
4639
|
+
case "delivery_loop":
|
|
4640
|
+
case "governance":
|
|
4641
|
+
return "small_refactor";
|
|
4642
|
+
case "validation":
|
|
4643
|
+
return "flaky_test";
|
|
4644
|
+
default:
|
|
4645
|
+
return "small_refactor";
|
|
4646
|
+
}
|
|
4647
|
+
}
|
|
4648
|
+
function categoryTriggerType(category, topSignals) {
|
|
4649
|
+
const allowed = [
|
|
4650
|
+
"test_failure",
|
|
4651
|
+
"lint_failure",
|
|
4652
|
+
"typecheck_failure",
|
|
4653
|
+
"queue_health",
|
|
4654
|
+
"regret_signal"
|
|
4655
|
+
];
|
|
4656
|
+
const strongestSignal = topSignals.map((signal) => ({
|
|
4657
|
+
type: asString2(signal.type),
|
|
4658
|
+
value: clamp012(asNumber(signal.value, 0))
|
|
4659
|
+
})).filter((signal) => allowed.includes(signal.type)).sort((a, b) => b.value - a.value)[0];
|
|
4660
|
+
if (category === "validation") {
|
|
4661
|
+
return strongestSignal?.type === "test_failure" ? "test_failure" : "queue_health";
|
|
4662
|
+
}
|
|
4663
|
+
if (category === "performance" || category === "reliability") {
|
|
4664
|
+
return strongestSignal?.type ?? "queue_health";
|
|
4665
|
+
}
|
|
4666
|
+
if (category === "delivery_loop" || category === "governance" || category === "maintainability") {
|
|
4667
|
+
return strongestSignal?.type === "regret_signal" ? "regret_signal" : "queue_health";
|
|
4668
|
+
}
|
|
4669
|
+
return "queue_health";
|
|
4670
|
+
}
|
|
4671
|
+
function isMetaRepoObjective(objective) {
|
|
4672
|
+
return META_OBJECTIVE_CATEGORIES.has(objective.category);
|
|
4673
|
+
}
|
|
4519
4674
|
var OBJECTIVE_TYPES = new Set([
|
|
4520
4675
|
"flaky_test",
|
|
4521
4676
|
"lint_fix",
|
|
@@ -4736,6 +4891,67 @@ function chooseRepoTargetProfile(profiles, hints, triggerType) {
|
|
|
4736
4891
|
}
|
|
4737
4892
|
return best?.profile ?? profiles[0] ?? null;
|
|
4738
4893
|
}
|
|
4894
|
+
function chooseRepoObjectiveTargetProfile(profiles, objective) {
|
|
4895
|
+
if (profiles.length === 0)
|
|
4896
|
+
return null;
|
|
4897
|
+
const hintTokens = [...new Set([...objective.keywords, ...tokenizePath(objective.title)])];
|
|
4898
|
+
const categories = new Set([
|
|
4899
|
+
objective.category,
|
|
4900
|
+
...objective.secondary_categories
|
|
4901
|
+
]);
|
|
4902
|
+
let best = null;
|
|
4903
|
+
for (const profile of profiles) {
|
|
4904
|
+
const label = profile.label.toLowerCase();
|
|
4905
|
+
const profileTokens = new Set(profile.keywords);
|
|
4906
|
+
let score = 0;
|
|
4907
|
+
for (const token of hintTokens) {
|
|
4908
|
+
if (profileTokens.has(token))
|
|
4909
|
+
score += 3;
|
|
4910
|
+
if (label.includes(token))
|
|
4911
|
+
score += 1;
|
|
4912
|
+
}
|
|
4913
|
+
const productSurface = /(^|\/)(app|src|components|component|screens|pages|routes|styles|assets)\b/i.test(label) || /\b(client|frontend|web|ui|ux|game|screen|view|layout)\b/i.test(label);
|
|
4914
|
+
const validationSurface = /(^|\/)(__tests__|tests?|e2e|smoke|specs?)\b/i.test(label) || /\b(test|smoke|spec)\b/i.test(label);
|
|
4915
|
+
const docsSurface = /\b(readme|vision|docs?)\b/i.test(label);
|
|
4916
|
+
const scriptSurface = /(^|\/)(scripts?|tools?)\b/i.test(label);
|
|
4917
|
+
const packageSurface = /\b(package\.json|tsconfig|eslint|prettier|config)\b/i.test(label);
|
|
4918
|
+
if (categories.has("product_core") || categories.has("user_experience") || categories.has("onboarding") || categories.has("content") || categories.has("growth")) {
|
|
4919
|
+
if (productSurface)
|
|
4920
|
+
score += 5;
|
|
4921
|
+
if (/\b(game|play|screen|route|layout|index|style|component)\b/i.test(label))
|
|
4922
|
+
score += 3;
|
|
4923
|
+
if (validationSurface)
|
|
4924
|
+
score -= 7;
|
|
4925
|
+
if (docsSurface || packageSurface || scriptSurface)
|
|
4926
|
+
score -= 4;
|
|
4927
|
+
}
|
|
4928
|
+
if (categories.has("validation")) {
|
|
4929
|
+
if (validationSurface || scriptSurface)
|
|
4930
|
+
score += 5;
|
|
4931
|
+
if (productSurface)
|
|
4932
|
+
score += 1;
|
|
4933
|
+
}
|
|
4934
|
+
if (categories.has("performance")) {
|
|
4935
|
+
if (productSurface || /\b(perf|render|animation|worker|server)\b/i.test(label))
|
|
4936
|
+
score += 4;
|
|
4937
|
+
if (docsSurface)
|
|
4938
|
+
score -= 3;
|
|
4939
|
+
}
|
|
4940
|
+
if (categories.has("reliability")) {
|
|
4941
|
+
if (productSurface || scriptSurface || packageSurface || /\b(config|startup|server)\b/i.test(label)) {
|
|
4942
|
+
score += 3;
|
|
4943
|
+
}
|
|
4944
|
+
}
|
|
4945
|
+
if (categories.has("delivery_loop") || categories.has("governance") || categories.has("maintainability")) {
|
|
4946
|
+
if (scriptSurface || packageSurface || /\b(src|utils?|lib|server|shared|policy)\b/i.test(label)) {
|
|
4947
|
+
score += 3;
|
|
4948
|
+
}
|
|
4949
|
+
}
|
|
4950
|
+
if (!best || score > best.score)
|
|
4951
|
+
best = { profile, score };
|
|
4952
|
+
}
|
|
4953
|
+
return best?.profile ?? chooseRepoTargetProfile(profiles, [objective.title], "queue_health");
|
|
4954
|
+
}
|
|
4739
4955
|
function adaptCandidateShapeToRepo(params) {
|
|
4740
4956
|
const shape = params.shape;
|
|
4741
4957
|
const scopeValidation = validateScopeInvariants(shape.component_area, shape.target_paths, shape.write_globs, {
|
|
@@ -5178,12 +5394,117 @@ function maxSignalScore(snapshot, types) {
|
|
|
5178
5394
|
function maxTraitScore(snapshot, pattern) {
|
|
5179
5395
|
return clamp012(Math.max(0, ...snapshot.state_traits.filter((trait) => pattern.test(String(trait.focus ?? "")) || pattern.test(String(trait.evidence ?? "")) || pattern.test(String(trait.trait_id ?? ""))).map((trait) => asNumber(trait.score, 0))));
|
|
5180
5396
|
}
|
|
5397
|
+
function repoObjectiveWeight(params) {
|
|
5398
|
+
const rank = params.priorityRank ?? 12;
|
|
5399
|
+
const sourceBase = params.sourceBucket === "priorities" ? 0.86 : params.sourceBucket === "objectives" ? 0.78 : params.sourceBucket === "metrics" ? 0.58 : params.sourceBucket === "section" ? 0.5 : 0.42;
|
|
5400
|
+
const rankPenalty = Math.min(0.28, Math.max(0, rank - 1) * 0.045);
|
|
5401
|
+
const metaPenalty = META_OBJECTIVE_CATEGORIES.has(params.category) ? 0.08 : 0;
|
|
5402
|
+
const explicitValidationBoost = params.category === "validation" || /\b(smoke|browser|validation|test)\b/i.test(params.text) ? 0.04 : 0;
|
|
5403
|
+
return clamp012(sourceBase - rankPenalty - metaPenalty + explicitValidationBoost);
|
|
5404
|
+
}
|
|
5405
|
+
function compileRepoVisionObjectives(params) {
|
|
5406
|
+
const sectionNumbers = params.vision.section_numbers ?? [];
|
|
5407
|
+
const keyItems = params.vision.key_items;
|
|
5408
|
+
const constraints = bucketLines(keyItems, [
|
|
5409
|
+
"guardrails",
|
|
5410
|
+
"constraints",
|
|
5411
|
+
"risk_policy",
|
|
5412
|
+
"non_goals"
|
|
5413
|
+
]).slice(0, 12);
|
|
5414
|
+
const validationExpectations = [
|
|
5415
|
+
...bucketLines(keyItems, ["testing_criteria"]),
|
|
5416
|
+
...bucketLines(keyItems, ["metrics", "constraints", "risk_policy"]).filter((line) => /\b(validation|validate|test|smoke|browser|ci|check)\b/i.test(line))
|
|
5417
|
+
].slice(0, 8);
|
|
5418
|
+
const successCriteria = bucketLines(keyItems, ["metrics", "objectives", "priorities"]).slice(0, 8);
|
|
5419
|
+
const entries = [];
|
|
5420
|
+
const seen = new Set;
|
|
5421
|
+
const addEntry = (rawTitle, sourceBucket, priorityRank, explicitSectionRef) => {
|
|
5422
|
+
const title = asString2(rawTitle);
|
|
5423
|
+
if (!title)
|
|
5424
|
+
return;
|
|
5425
|
+
const key = title.toLowerCase();
|
|
5426
|
+
if (seen.has(key))
|
|
5427
|
+
return;
|
|
5428
|
+
seen.add(key);
|
|
5429
|
+
const titleCategory = categorizeVisionText(title);
|
|
5430
|
+
const contextCategory = categorizeVisionText([constraints.join(" "), validationExpectations.join(" ")].join(`
|
|
5431
|
+
`));
|
|
5432
|
+
const secondaryCategories = [
|
|
5433
|
+
...titleCategory.secondary,
|
|
5434
|
+
contextCategory.primary,
|
|
5435
|
+
...contextCategory.secondary
|
|
5436
|
+
].filter((category) => category !== "unknown" && category !== titleCategory.primary);
|
|
5437
|
+
const primaryCategory = titleCategory.primary === "unknown" && (sourceBucket === "priorities" || sourceBucket === "objectives") ? "product_core" : titleCategory.primary;
|
|
5438
|
+
const categorized = {
|
|
5439
|
+
primary: primaryCategory,
|
|
5440
|
+
secondary: [
|
|
5441
|
+
...new Set(secondaryCategories.filter((category) => category !== primaryCategory))
|
|
5442
|
+
].slice(0, 4)
|
|
5443
|
+
};
|
|
5444
|
+
const id = slugifyObjectiveId(title, `vision_objective_${entries.length + 1}`);
|
|
5445
|
+
const sectionRef = explicitSectionRef || sourceBucketSectionRef(sourceBucket, sectionNumbers) || "";
|
|
5446
|
+
const keywords = uniqueLowercaseTokens([
|
|
5447
|
+
...tokenizePath(title),
|
|
5448
|
+
categorized.primary,
|
|
5449
|
+
...categorized.secondary
|
|
5450
|
+
]);
|
|
5451
|
+
const weight = repoObjectiveWeight({
|
|
5452
|
+
sourceBucket,
|
|
5453
|
+
priorityRank,
|
|
5454
|
+
category: categorized.primary,
|
|
5455
|
+
text: title
|
|
5456
|
+
});
|
|
5457
|
+
entries.push({
|
|
5458
|
+
id,
|
|
5459
|
+
title,
|
|
5460
|
+
category: categorized.primary,
|
|
5461
|
+
secondary_categories: categorized.secondary,
|
|
5462
|
+
priority_rank: priorityRank,
|
|
5463
|
+
source_bucket: sourceBucket,
|
|
5464
|
+
section_ref: sectionRef,
|
|
5465
|
+
weight,
|
|
5466
|
+
keywords,
|
|
5467
|
+
success_criteria: successCriteria,
|
|
5468
|
+
constraints,
|
|
5469
|
+
validation_expectations: validationExpectations,
|
|
5470
|
+
evidence: [
|
|
5471
|
+
`source_bucket=${sourceBucket}`,
|
|
5472
|
+
priorityRank != null ? `priority_rank=${priorityRank}` : "priority_rank=none",
|
|
5473
|
+
`category=${categorized.primary}`,
|
|
5474
|
+
`section_ref=${sectionRef || "none"}`
|
|
5475
|
+
]
|
|
5476
|
+
});
|
|
5477
|
+
};
|
|
5478
|
+
keyItems.priorities.forEach((title, index) => addEntry(title, "priorities", index + 1));
|
|
5479
|
+
keyItems.objectives.forEach((title, index) => addEntry(title, "objectives", index + 1));
|
|
5480
|
+
keyItems.metrics.filter((title) => /\b(validation|smoke|browser|performance|reliab|startup)\b/i.test(title)).forEach((title, index) => addEntry(title, "metrics", index + 1));
|
|
5481
|
+
for (const section of params.vision.sections ?? []) {
|
|
5482
|
+
const sectionTitle = asString2(section.title);
|
|
5483
|
+
if (!sectionTitle)
|
|
5484
|
+
continue;
|
|
5485
|
+
if (/^(who this is for|the problem|scope|long-term|how decisions|get made)$/i.test(sectionTitle)) {
|
|
5486
|
+
continue;
|
|
5487
|
+
}
|
|
5488
|
+
const sectionNumber = asString2(section.number);
|
|
5489
|
+
const priorityRank = Number.isFinite(Number(sectionNumber)) ? Number(sectionNumber) : null;
|
|
5490
|
+
addEntry(sectionTitle, "section", priorityRank, sectionNumber);
|
|
5491
|
+
}
|
|
5492
|
+
return entries.sort((a, b) => {
|
|
5493
|
+
if (b.weight !== a.weight)
|
|
5494
|
+
return b.weight - a.weight;
|
|
5495
|
+
const aRank = a.priority_rank ?? Number.MAX_SAFE_INTEGER;
|
|
5496
|
+
const bRank = b.priority_rank ?? Number.MAX_SAFE_INTEGER;
|
|
5497
|
+
if (aRank !== bRank)
|
|
5498
|
+
return aRank - bRank;
|
|
5499
|
+
return a.id.localeCompare(b.id);
|
|
5500
|
+
});
|
|
5501
|
+
}
|
|
5181
5502
|
function normalizeValidationIdeas(ideas) {
|
|
5182
5503
|
const out = [];
|
|
5183
5504
|
for (const idea of ideas) {
|
|
5184
|
-
const
|
|
5185
|
-
if (
|
|
5186
|
-
out.push(
|
|
5505
|
+
const command = extractValidationCommandFromIdea(idea);
|
|
5506
|
+
if (isRepoNativeValidationCommand(command)) {
|
|
5507
|
+
out.push(command);
|
|
5187
5508
|
continue;
|
|
5188
5509
|
}
|
|
5189
5510
|
const lower = idea.toLowerCase();
|
|
@@ -5198,6 +5519,16 @@ function normalizeValidationIdeas(ideas) {
|
|
|
5198
5519
|
out.push("bun run test:root");
|
|
5199
5520
|
return [...new Set(out)].slice(0, 5);
|
|
5200
5521
|
}
|
|
5522
|
+
function extractValidationCommandFromIdea(value) {
|
|
5523
|
+
const raw = asString2(value);
|
|
5524
|
+
if (!raw)
|
|
5525
|
+
return "";
|
|
5526
|
+
const fenced = raw.match(/`([^`]+)`/)?.[1]?.trim();
|
|
5527
|
+
return (fenced || raw.replace(/^(run|execute|verify|validate|check)\s+/i, "")).trim();
|
|
5528
|
+
}
|
|
5529
|
+
function isRepoNativeValidationCommand(value) {
|
|
5530
|
+
return /^(bun|bunx|npm|npx|pnpm|yarn|node|python|python3|uv|pytest|vitest|jest|tsc|eslint|ruff|mypy|go|cargo|make|docker|pwsh|powershell|sh|bash)\b/i.test(value);
|
|
5531
|
+
}
|
|
5201
5532
|
function inferComponentAreaFromText(text, repoTargets, triggerType) {
|
|
5202
5533
|
const repoTargetMatch = chooseRepoTargetProfile(repoTargets ?? [], [text], triggerType);
|
|
5203
5534
|
if (repoTargetMatch)
|
|
@@ -5554,6 +5885,7 @@ function buildCommitHistoryBlocks(params) {
|
|
|
5554
5885
|
function buildEngineInspirationContext(params) {
|
|
5555
5886
|
const oneSentence = asString2(params.vision.one_sentence);
|
|
5556
5887
|
const keyItems = params.vision.key_items;
|
|
5888
|
+
const compiledRepoObjectives = compileRepoVisionObjectives({ vision: params.vision });
|
|
5557
5889
|
const compiledObjectives = ENGINE_OBJECTIVE_BLUEPRINTS.map((blueprint) => {
|
|
5558
5890
|
const lines = bucketLines(keyItems, blueprint.buckets);
|
|
5559
5891
|
const evidence = keywordEvidence(lines, blueprint.keywordPattern);
|
|
@@ -5720,6 +6052,7 @@ function buildEngineInspirationContext(params) {
|
|
|
5720
6052
|
}
|
|
5721
6053
|
const buildingBlocks = [...buildingBlockMap.values()].sort((a, b) => b.score - a.score);
|
|
5722
6054
|
return {
|
|
6055
|
+
compiled_repo_objectives: compiledRepoObjectives,
|
|
5723
6056
|
compiled_objectives: compiledObjectives,
|
|
5724
6057
|
opportunity_gaps: opportunityGaps,
|
|
5725
6058
|
building_blocks: buildingBlocks,
|
|
@@ -5806,6 +6139,63 @@ function inferEngineTrialFromCandidate(candidate, engineInspiration) {
|
|
|
5806
6139
|
hypothesis: fallback.hypothesis
|
|
5807
6140
|
};
|
|
5808
6141
|
}
|
|
6142
|
+
function buildRepoVisionFallbackCandidates(params) {
|
|
6143
|
+
const maxCandidates = Number.isFinite(params.maxCandidates) ? Math.max(1, Math.min(6, Math.floor(params.maxCandidates))) : 3;
|
|
6144
|
+
const sectionRefs = selectVisionSectionRefs(params.visionSectionRefs);
|
|
6145
|
+
const objectives = params.engineInspiration.compiled_repo_objectives.filter((objective) => objective.weight >= 0.42).sort((a, b) => {
|
|
6146
|
+
if (b.weight !== a.weight)
|
|
6147
|
+
return b.weight - a.weight;
|
|
6148
|
+
const aRank = a.priority_rank ?? Number.MAX_SAFE_INTEGER;
|
|
6149
|
+
const bRank = b.priority_rank ?? Number.MAX_SAFE_INTEGER;
|
|
6150
|
+
if (aRank !== bRank)
|
|
6151
|
+
return aRank - bRank;
|
|
6152
|
+
const aMeta = isMetaRepoObjective(a) ? 1 : 0;
|
|
6153
|
+
const bMeta = isMetaRepoObjective(b) ? 1 : 0;
|
|
6154
|
+
if (aMeta !== bMeta)
|
|
6155
|
+
return aMeta - bMeta;
|
|
6156
|
+
return a.id.localeCompare(b.id);
|
|
6157
|
+
}).slice(0, maxCandidates);
|
|
6158
|
+
return objectives.map((objective, idx) => {
|
|
6159
|
+
const target = chooseRepoObjectiveTargetProfile(params.repoTargets ?? [], objective);
|
|
6160
|
+
const targetPaths = target?.target_paths ?? [objective.section_ref ? `vision.md` : "README.md"];
|
|
6161
|
+
const writeGlobs = target?.write_globs ?? targetPaths;
|
|
6162
|
+
const componentArea = target?.component_area ?? (normalizeAutonomyComponentArea(pathDirname(targetPaths[0]) || targetPaths[0]) ?? "docs");
|
|
6163
|
+
const triggerType = categoryTriggerType(objective.category, params.snapshotTopSignals);
|
|
6164
|
+
const signalIds = pickSignalIdsForTrigger(params.snapshotTopSignals, triggerType);
|
|
6165
|
+
const sectionRef = objective.section_ref || sectionRefs[0] || "";
|
|
6166
|
+
const categorySummary = [
|
|
6167
|
+
objective.category,
|
|
6168
|
+
...objective.secondary_categories.slice(0, 2)
|
|
6169
|
+
].join(", ");
|
|
6170
|
+
return {
|
|
6171
|
+
id: `cand_repo_${objective.id}_${randomUUID().slice(0, 8)}`,
|
|
6172
|
+
title: `Vision objective: ${objective.title}`,
|
|
6173
|
+
objective_type: categoryObjectiveType(objective.category),
|
|
6174
|
+
problem_statement: `Advance the repo vision objective "${objective.title}" (${categorySummary}). ` + "Deliver one small, observable improvement using the repo's own product/domain language.",
|
|
6175
|
+
trigger_type: triggerType,
|
|
6176
|
+
component_area: componentArea,
|
|
6177
|
+
target_paths: targetPaths,
|
|
6178
|
+
scope: {
|
|
6179
|
+
read_anywhere: false,
|
|
6180
|
+
write_globs: writeGlobs
|
|
6181
|
+
},
|
|
6182
|
+
risk_level: "low",
|
|
6183
|
+
expected_validation: normalizeValidationIdeas(objective.validation_expectations.length > 0 ? objective.validation_expectations : ["bun run test:root"]),
|
|
6184
|
+
estimated_effort: idx === 0 ? "small" : "medium",
|
|
6185
|
+
why_now_signal_ids: signalIds,
|
|
6186
|
+
confidence: clamp012(0.5 + objective.weight * 0.45),
|
|
6187
|
+
vision_alignment_reason: `Highest repo vision category ${objective.category}; source=${objective.source_bucket}; ` + `priority=${objective.priority_rank ?? "n/a"}; section=${sectionRef || "n/a"}.`,
|
|
6188
|
+
vision_section_refs: sectionRef ? [sectionRef] : sectionRefs,
|
|
6189
|
+
feature_hypotheses: [
|
|
6190
|
+
objective.success_criteria[0] ? `Success signal: ${objective.success_criteria[0]}` : `Improve ${objective.title} without widening scope.`,
|
|
6191
|
+
objective.constraints[0] ? `Guardrail: ${objective.constraints[0]}` : "",
|
|
6192
|
+
objective.validation_expectations[0] ? `Validation expectation: ${objective.validation_expectations[0]}` : "Validate through the smallest repo-supported check."
|
|
6193
|
+
].filter(Boolean),
|
|
6194
|
+
requires_user_input: false,
|
|
6195
|
+
question_if_blocked: ""
|
|
6196
|
+
};
|
|
6197
|
+
});
|
|
6198
|
+
}
|
|
5809
6199
|
function buildEngineFallbackCandidates(params) {
|
|
5810
6200
|
const maxCandidates = Number.isFinite(params.maxCandidates) ? Math.max(1, Math.min(6, Math.floor(params.maxCandidates))) : 3;
|
|
5811
6201
|
const objectiveTitleById = new Map(params.engineInspiration.compiled_objectives.map((objective) => [
|
|
@@ -6130,6 +6520,7 @@ class RemoteBuddyAutonomousEngine {
|
|
|
6130
6520
|
constraints: keyItems.constraints,
|
|
6131
6521
|
non_goals: keyItems.nonGoals,
|
|
6132
6522
|
metrics: keyItems.metrics,
|
|
6523
|
+
testing_criteria: keyItems.testingCriteria,
|
|
6133
6524
|
risk_policy: keyItems.riskPolicy,
|
|
6134
6525
|
operating_model: keyItems.operatingModel,
|
|
6135
6526
|
governance: keyItems.governance
|
|
@@ -6860,8 +7251,16 @@ ${JSON.stringify(input.messages ?? [])}`),
|
|
|
6860
7251
|
return;
|
|
6861
7252
|
}
|
|
6862
7253
|
let rawCandidates = Array.isArray(ideationJson.candidates) ? ideationJson.candidates : [];
|
|
7254
|
+
let rawCandidatesSource = "llm";
|
|
6863
7255
|
if (rawCandidates.length === 0) {
|
|
6864
|
-
const
|
|
7256
|
+
const repoSynthesized = buildRepoVisionFallbackCandidates({
|
|
7257
|
+
engineInspiration,
|
|
7258
|
+
snapshotTopSignals: snapshot.top_signals,
|
|
7259
|
+
visionSectionRefs: visionContext.section_numbers,
|
|
7260
|
+
maxCandidates: Math.max(1, Math.min(3, this.cfg.topK)),
|
|
7261
|
+
repoTargets
|
|
7262
|
+
});
|
|
7263
|
+
const synthesized = repoSynthesized.length > 0 ? repoSynthesized : buildEngineFallbackCandidates({
|
|
6865
7264
|
engineInspiration,
|
|
6866
7265
|
snapshotTopSignals: snapshot.top_signals,
|
|
6867
7266
|
visionSectionRefs: visionContext.section_numbers,
|
|
@@ -6870,8 +7269,9 @@ ${JSON.stringify(input.messages ?? [])}`),
|
|
|
6870
7269
|
repoTargets
|
|
6871
7270
|
});
|
|
6872
7271
|
if (synthesized.length > 0) {
|
|
6873
|
-
console.log(`[RemoteBuddyAutonomousEngine] tick ${runId}: ideation returned no candidates; using ${synthesized.length} deterministic engine-inspiration fallback candidates.`);
|
|
7272
|
+
console.log(`[RemoteBuddyAutonomousEngine] tick ${runId}: ideation returned no candidates; using ${synthesized.length} deterministic ${repoSynthesized.length > 0 ? "repo-vision" : "engine-inspiration"} fallback candidates.`);
|
|
6874
7273
|
rawCandidates = synthesized;
|
|
7274
|
+
rawCandidatesSource = repoSynthesized.length > 0 ? "repo_vision_fallback" : "engine_fallback";
|
|
6875
7275
|
}
|
|
6876
7276
|
}
|
|
6877
7277
|
const normalizedCandidates = [];
|
|
@@ -6960,7 +7360,7 @@ ${JSON.stringify(input.messages ?? [])}`),
|
|
|
6960
7360
|
console.warn(`[RemoteBuddyAutonomousEngine] dropping candidate ${candidate.id}: target_paths missing in repo ${missingTargetPaths.join(", ")}`);
|
|
6961
7361
|
continue;
|
|
6962
7362
|
}
|
|
6963
|
-
if (!candidate.engine_trial) {
|
|
7363
|
+
if (!candidate.engine_trial && source !== "repo_vision_fallback") {
|
|
6964
7364
|
const inferred = inferEngineTrialFromCandidate(candidate, engineInspiration);
|
|
6965
7365
|
if (inferred) {
|
|
6966
7366
|
candidate.engine_trial = {
|
|
@@ -6972,9 +7372,16 @@ ${JSON.stringify(input.messages ?? [])}`),
|
|
|
6972
7372
|
normalizedCandidates.push(candidate);
|
|
6973
7373
|
}
|
|
6974
7374
|
};
|
|
6975
|
-
ingestRawCandidates(rawCandidates,
|
|
7375
|
+
ingestRawCandidates(rawCandidates, rawCandidatesSource);
|
|
6976
7376
|
if (normalizedCandidates.length === 0) {
|
|
6977
|
-
const
|
|
7377
|
+
const repoSynthesizedFallback = buildRepoVisionFallbackCandidates({
|
|
7378
|
+
engineInspiration,
|
|
7379
|
+
snapshotTopSignals: snapshot.top_signals,
|
|
7380
|
+
visionSectionRefs: visionContext.section_numbers,
|
|
7381
|
+
maxCandidates: Math.max(1, Math.min(3, this.cfg.topK)),
|
|
7382
|
+
repoTargets
|
|
7383
|
+
});
|
|
7384
|
+
const synthesizedFallback = repoSynthesizedFallback.length > 0 ? repoSynthesizedFallback : buildEngineFallbackCandidates({
|
|
6978
7385
|
engineInspiration,
|
|
6979
7386
|
snapshotTopSignals: snapshot.top_signals,
|
|
6980
7387
|
visionSectionRefs: visionContext.section_numbers,
|
|
@@ -6983,7 +7390,7 @@ ${JSON.stringify(input.messages ?? [])}`),
|
|
|
6983
7390
|
repoTargets
|
|
6984
7391
|
});
|
|
6985
7392
|
if (synthesizedFallback.length > 0) {
|
|
6986
|
-
ingestRawCandidates(synthesizedFallback, "engine_fallback");
|
|
7393
|
+
ingestRawCandidates(synthesizedFallback, repoSynthesizedFallback.length > 0 ? "repo_vision_fallback" : "engine_fallback");
|
|
6987
7394
|
}
|
|
6988
7395
|
}
|
|
6989
7396
|
candidatesPayload = normalizedCandidates.map((candidate) => ({
|
|
@@ -7806,7 +8213,7 @@ function ensureWriteGlobsCoverTargetPaths(targetPaths, writeGlobs) {
|
|
|
7806
8213
|
}
|
|
7807
8214
|
return { normalizedWriteGlobs, uncoveredTargets, addedGlobs };
|
|
7808
8215
|
}
|
|
7809
|
-
function buildExecutionGuidance(plan, targetPaths) {
|
|
8216
|
+
function buildExecutionGuidance(plan, targetPaths, requiredValidationSteps = []) {
|
|
7810
8217
|
const lines = [];
|
|
7811
8218
|
const targets = normalizePathHints(targetPaths.length > 0 ? targetPaths : plan.scope.write_globs ?? []);
|
|
7812
8219
|
if (targets.length > 0) {
|
|
@@ -7860,10 +8267,16 @@ function buildExecutionGuidance(plan, targetPaths) {
|
|
|
7860
8267
|
for (const step of plan.validation_steps)
|
|
7861
8268
|
lines.push(`- ${step}`);
|
|
7862
8269
|
}
|
|
8270
|
+
if (requiredValidationSteps.length > 0) {
|
|
8271
|
+
lines.push("Required vision.md testing criteria:");
|
|
8272
|
+
for (const step of requiredValidationSteps)
|
|
8273
|
+
lines.push(`- ${step}`);
|
|
8274
|
+
lines.push("- These repo-level checks are mandatory before reporting completion or publishing a PR.");
|
|
8275
|
+
}
|
|
7863
8276
|
return lines.join(`
|
|
7864
8277
|
`).trim();
|
|
7865
8278
|
}
|
|
7866
|
-
var VALIDATION_COMMAND_PREFIX = /^(git|bun|bunx|node|python|python3|uv|pytest|vitest|jest|tsc|eslint|ruff|mypy|go|cargo|make|docker|pwsh|powershell|sh|bash)\b/i;
|
|
8279
|
+
var VALIDATION_COMMAND_PREFIX = /^(git|bun|bunx|npm|npx|pnpm|yarn|node|python|python3|uv|pytest|vitest|jest|tsc|eslint|ruff|mypy|go|cargo|make|docker|pwsh|powershell|sh|bash)\b/i;
|
|
7867
8280
|
var VALIDATION_GENERIC_SAFE = /^(git\s+status\s+--porcelain|git\s+diff\b)/i;
|
|
7868
8281
|
var PATH_TOKEN_REGEX = /\b([A-Za-z0-9._/\-\\]+\.[A-Za-z0-9._-]+)\b/g;
|
|
7869
8282
|
function isCommandLikeValidationStep(step) {
|
|
@@ -7912,6 +8325,34 @@ function normalizeValidationSteps(steps, targetPaths) {
|
|
|
7912
8325
|
}
|
|
7913
8326
|
return out;
|
|
7914
8327
|
}
|
|
8328
|
+
function extractCommandFromValidationCriterion(value) {
|
|
8329
|
+
const raw = String(value ?? "").trim();
|
|
8330
|
+
if (!raw)
|
|
8331
|
+
return "";
|
|
8332
|
+
const fenced = raw.match(/`([^`]+)`/)?.[1]?.trim();
|
|
8333
|
+
const candidate = fenced || raw.replace(/^(run|execute|verify|validate|check)\s+/i, "").trim();
|
|
8334
|
+
return candidate.trim();
|
|
8335
|
+
}
|
|
8336
|
+
function extractRequiredValidationStepsFromVisionMarkdown(markdown) {
|
|
8337
|
+
const criteria = extractVisionKeyItems(markdown).testingCriteria;
|
|
8338
|
+
const out = [];
|
|
8339
|
+
const seen = new Set;
|
|
8340
|
+
for (const criterion of criteria) {
|
|
8341
|
+
const command = extractCommandFromValidationCriterion(criterion);
|
|
8342
|
+
if (!command)
|
|
8343
|
+
continue;
|
|
8344
|
+
if (!isCommandLikeValidationStep(command))
|
|
8345
|
+
continue;
|
|
8346
|
+
const key = command.toLowerCase();
|
|
8347
|
+
if (seen.has(key))
|
|
8348
|
+
continue;
|
|
8349
|
+
seen.add(key);
|
|
8350
|
+
out.push(command);
|
|
8351
|
+
if (out.length >= 12)
|
|
8352
|
+
break;
|
|
8353
|
+
}
|
|
8354
|
+
return out;
|
|
8355
|
+
}
|
|
7915
8356
|
function defaultValidationStepsForRequest(prompt, targetPaths) {
|
|
7916
8357
|
const text = prompt.toLowerCase();
|
|
7917
8358
|
const concreteTargets = targetPaths.filter((entry) => entry && entry !== ".").slice(0, 4);
|
|
@@ -7945,6 +8386,44 @@ function sanitizePlannerWorkerInstruction(workerInstruction, canonicalInstructio
|
|
|
7945
8386
|
}
|
|
7946
8387
|
return value;
|
|
7947
8388
|
}
|
|
8389
|
+
function summarizeToolRun(toolRun) {
|
|
8390
|
+
const command = toSingleLine(toolRun.commandLine, 160) || (Array.isArray(toolRun.argv) ? toSingleLine(toolRun.argv.join(" "), 160) : "");
|
|
8391
|
+
const parts = [
|
|
8392
|
+
`tool=${toSingleLine(toolRun.tool, 40) || "unknown"}`,
|
|
8393
|
+
toolRun.phase ? `phase=${toSingleLine(toolRun.phase, 60)}` : "",
|
|
8394
|
+
command ? `cmd=${command}` : "",
|
|
8395
|
+
toolRun.failureClass ? `class=${toSingleLine(toolRun.failureClass, 60)}` : "",
|
|
8396
|
+
typeof toolRun.retryable === "boolean" ? `retryable=${toolRun.retryable ? "yes" : "no"}` : "",
|
|
8397
|
+
typeof toolRun.exitCode === "number" ? `exit=${toolRun.exitCode}` : "",
|
|
8398
|
+
toolRun.remediation ? `fix=${toSingleLine(toolRun.remediation, 220)}` : ""
|
|
8399
|
+
].filter(Boolean);
|
|
8400
|
+
return parts.join(" | ");
|
|
8401
|
+
}
|
|
8402
|
+
function explainJobFailureFromToolRuns(toolRuns) {
|
|
8403
|
+
const failed = toolRuns.find((entry) => entry && entry.ok === false);
|
|
8404
|
+
if (!failed)
|
|
8405
|
+
return null;
|
|
8406
|
+
const tool = toSingleLine(failed.tool, 40) || "unknown tool";
|
|
8407
|
+
const failureClass = toSingleLine(failed.failureClass, 80) || "tool failure";
|
|
8408
|
+
const remediation = toSingleLine(failed.remediation, 260);
|
|
8409
|
+
const command = toSingleLine(failed.commandLine, 140) || (Array.isArray(failed.argv) ? toSingleLine(failed.argv.join(" "), 140) : "");
|
|
8410
|
+
return [
|
|
8411
|
+
`Latest tool failure: ${tool} reported ${failureClass}`,
|
|
8412
|
+
command ? `while running ${command}` : "",
|
|
8413
|
+
remediation ? `Recommended fix: ${remediation}` : ""
|
|
8414
|
+
].filter(Boolean).join(". ");
|
|
8415
|
+
}
|
|
8416
|
+
function formatToolRunDiagnostics(toolRuns) {
|
|
8417
|
+
const failed = toolRuns.filter((entry) => entry && entry.ok === false).slice(0, 4);
|
|
8418
|
+
if (failed.length === 0)
|
|
8419
|
+
return "";
|
|
8420
|
+
return `
|
|
8421
|
+
Tool diagnostics:
|
|
8422
|
+
\`\`\`
|
|
8423
|
+
${failed.map(summarizeToolRun).join(`
|
|
8424
|
+
`)}
|
|
8425
|
+
\`\`\``;
|
|
8426
|
+
}
|
|
7948
8427
|
function explainJobFailureFromLogs(logs, fallbackMessage, fallbackDetail) {
|
|
7949
8428
|
const lines = logs.map((row) => toSingleLine(row.message, 420)).filter(Boolean);
|
|
7950
8429
|
const joined = lines.join(`
|
|
@@ -8281,6 +8760,22 @@ class RemoteBuddyOrchestrator {
|
|
|
8281
8760
|
return [];
|
|
8282
8761
|
}
|
|
8283
8762
|
}
|
|
8763
|
+
async fetchJobToolRuns(jobId, limit = 20) {
|
|
8764
|
+
try {
|
|
8765
|
+
const res = await fetch(`${this.server}/jobs/${jobId}/tool-runs?limit=${Math.max(1, Math.min(100, limit))}`, {
|
|
8766
|
+
method: "GET",
|
|
8767
|
+
headers: this.authHeaders()
|
|
8768
|
+
});
|
|
8769
|
+
if (!res.ok)
|
|
8770
|
+
return [];
|
|
8771
|
+
const data = await res.json();
|
|
8772
|
+
if (!data.ok || !Array.isArray(data.toolRuns))
|
|
8773
|
+
return [];
|
|
8774
|
+
return data.toolRuns.filter((row) => row && typeof row.tool === "string").slice(0, 20);
|
|
8775
|
+
} catch {
|
|
8776
|
+
return [];
|
|
8777
|
+
}
|
|
8778
|
+
}
|
|
8284
8779
|
markAutonomyFeedbackEventSeen(eventId) {
|
|
8285
8780
|
const id = String(eventId ?? "").trim();
|
|
8286
8781
|
if (!id)
|
|
@@ -8425,7 +8920,10 @@ class RemoteBuddyOrchestrator {
|
|
|
8425
8920
|
return;
|
|
8426
8921
|
}
|
|
8427
8922
|
console.warn(`[RemoteBuddy] Fetching failure logs for job ${jobId}...`);
|
|
8428
|
-
const logs = await
|
|
8923
|
+
const [logs, toolRuns] = await Promise.all([
|
|
8924
|
+
this.fetchJobLogs(jobId, 80),
|
|
8925
|
+
this.fetchJobToolRuns(jobId, 20)
|
|
8926
|
+
]);
|
|
8429
8927
|
const clarificationFromLogs = extractClarificationFromJobFailure(message, detail, logs);
|
|
8430
8928
|
if (clarificationFromLogs) {
|
|
8431
8929
|
const tail2 = logs.slice(-6).map((row) => toSingleLine(row.message, 220)).filter(Boolean);
|
|
@@ -8435,9 +8933,10 @@ Recent logs:
|
|
|
8435
8933
|
${tail2.join(`
|
|
8436
8934
|
`)}
|
|
8437
8935
|
\`\`\`` : "";
|
|
8936
|
+
const toolText2 = formatToolRunDiagnostics(toolRuns);
|
|
8438
8937
|
const clarificationMsg = `WorkerPal job ${shortJob} needs clarification before making changes: ${clarificationFromLogs}
|
|
8439
8938
|
|
|
8440
|
-
` + "Reply with the missing details and I will enqueue a focused follow-up request." + tailText2;
|
|
8939
|
+
` + "Reply with the missing details and I will enqueue a focused follow-up request." + toolText2 + tailText2;
|
|
8441
8940
|
await this.assistantMessage(sessionId, clarificationMsg, {
|
|
8442
8941
|
correlationId: envelope.correlationId,
|
|
8443
8942
|
turnId: envelope.turnId,
|
|
@@ -8445,7 +8944,7 @@ ${tail2.join(`
|
|
|
8445
8944
|
});
|
|
8446
8945
|
return;
|
|
8447
8946
|
}
|
|
8448
|
-
const explanation = explainJobFailureFromLogs(logs, message, detail);
|
|
8947
|
+
const explanation = explainJobFailureFromToolRuns(toolRuns) ?? explainJobFailureFromLogs(logs, message, detail);
|
|
8449
8948
|
const tail = logs.slice(-6).map((row) => toSingleLine(row.message, 220)).filter(Boolean);
|
|
8450
8949
|
const tailText = tail.length ? `
|
|
8451
8950
|
Recent logs:
|
|
@@ -8453,7 +8952,8 @@ Recent logs:
|
|
|
8453
8952
|
${tail.join(`
|
|
8454
8953
|
`)}
|
|
8455
8954
|
\`\`\`` : "";
|
|
8456
|
-
|
|
8955
|
+
const toolText = formatToolRunDiagnostics(toolRuns);
|
|
8956
|
+
await this.assistantMessage(sessionId, `Diagnosis for job ${shortJob}: ${explanation}${toolText}${tailText}`, {
|
|
8457
8957
|
correlationId: envelope.correlationId,
|
|
8458
8958
|
turnId: envelope.turnId,
|
|
8459
8959
|
parentId: envelope.id
|
|
@@ -8728,6 +9228,17 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
8728
9228
|
}
|
|
8729
9229
|
return out;
|
|
8730
9230
|
}
|
|
9231
|
+
loadVisionRequiredValidationSteps() {
|
|
9232
|
+
const visionPath = resolve5(this.repo, "vision.md");
|
|
9233
|
+
if (!existsSync5(visionPath))
|
|
9234
|
+
return [];
|
|
9235
|
+
try {
|
|
9236
|
+
return extractRequiredValidationStepsFromVisionMarkdown(readFileSync5(visionPath, "utf8"));
|
|
9237
|
+
} catch (err) {
|
|
9238
|
+
console.warn("[RemoteBuddy] Could not read vision.md testing criteria:", err);
|
|
9239
|
+
return [];
|
|
9240
|
+
}
|
|
9241
|
+
}
|
|
8731
9242
|
getRecentContextSnapshot(sessionId = this.sessionId) {
|
|
8732
9243
|
return this.sessionContext(sessionId).slice(-RemoteBuddyOrchestrator.MAX_CONTEXT);
|
|
8733
9244
|
}
|
|
@@ -9190,7 +9701,12 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
9190
9701
|
const isAnalysisFromEngine = plan.intent === "analysis" && Boolean(autonomyMetadata);
|
|
9191
9702
|
const requiresWorker = forceWorker && !isAnalysisFromEngine ? true : this.shouldForceDirectReply(prompt, plan.intent) ? false : plan.requires_worker;
|
|
9192
9703
|
console.log("[RemoteBuddy] Planner output:", { plan, targetPath, requiresWorker });
|
|
9704
|
+
let requiredValidationSteps = [];
|
|
9193
9705
|
if (requiresWorker) {
|
|
9706
|
+
requiredValidationSteps = this.loadVisionRequiredValidationSteps();
|
|
9707
|
+
if (requiredValidationSteps.length > 0) {
|
|
9708
|
+
console.log(`[RemoteBuddy] Loaded ${requiredValidationSteps.length} required validation step(s) from vision.md testing criteria.`);
|
|
9709
|
+
}
|
|
9194
9710
|
const scopeCoverage = ensureWriteGlobsCoverTargetPaths(targetPaths, plan.scope.write_globs);
|
|
9195
9711
|
if (scopeCoverage.normalizedWriteGlobs.length > 0) {
|
|
9196
9712
|
plan.scope.write_globs = scopeCoverage.normalizedWriteGlobs;
|
|
@@ -9240,7 +9756,7 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
9240
9756
|
}
|
|
9241
9757
|
const canonicalInstruction = prompt.trim();
|
|
9242
9758
|
const rawPlannerInstruction = sanitizePlannerWorkerInstruction(String(plan.worker_instruction ?? ""), canonicalInstruction);
|
|
9243
|
-
const executionGuidance = buildExecutionGuidance(plan, targetPaths);
|
|
9759
|
+
const executionGuidance = buildExecutionGuidance(plan, targetPaths, requiredValidationSteps);
|
|
9244
9760
|
const plannerWorkerInstruction = [rawPlannerInstruction, executionGuidance].filter(Boolean).join(`
|
|
9245
9761
|
|
|
9246
9762
|
`).trim();
|
|
@@ -9346,6 +9862,7 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
9346
9862
|
} : {},
|
|
9347
9863
|
acceptanceCriteria: plan.acceptance_criteria,
|
|
9348
9864
|
validationSteps: plan.validation_steps,
|
|
9865
|
+
...requiredValidationSteps.length > 0 ? { requiredValidationSteps } : {},
|
|
9349
9866
|
queuePriority: priority,
|
|
9350
9867
|
queueWaitBudgetMs,
|
|
9351
9868
|
executionBudgetMs,
|
|
@@ -9675,6 +10192,7 @@ if (import.meta.main) {
|
|
|
9675
10192
|
});
|
|
9676
10193
|
}
|
|
9677
10194
|
export {
|
|
10195
|
+
extractRequiredValidationStepsFromVisionMarkdown,
|
|
9678
10196
|
buildTaskExecuteDedupeKey,
|
|
9679
10197
|
RemoteBuddyOrchestrator
|
|
9680
10198
|
};
|