@pushpalsdev/cli 1.0.79 → 1.0.81
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/monitor-ui/+not-found.html +1 -1
- package/monitor-ui/_expo/static/js/web/{entry-c6862f701ea52ccf8692a6c9e749af5c.js → entry-e66b4de45f75e702ac16916082bcc9a5.js} +172 -171
- package/monitor-ui/_expo/static/js/web/{index-6013f9ebc87a963a55bb9137af1a5a06.js → index-ec13ec62e2b37ed3c5f6d324ef6784e1.js} +4 -4
- package/monitor-ui/_sitemap.html +1 -1
- package/monitor-ui/index.html +1 -1
- package/monitor-ui/modal.html +1 -1
- 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 +560 -23
- 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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// apps/remotebuddy/src/remotebuddy_main.ts
|
|
5
|
-
import { randomUUID as
|
|
5
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
6
6
|
import { Database as Database3 } from "bun:sqlite";
|
|
7
7
|
|
|
8
8
|
// apps/remotebuddy/src/llm.ts
|
|
@@ -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) => ({
|
|
@@ -7513,6 +7920,14 @@ Scope:
|
|
|
7513
7920
|
}
|
|
7514
7921
|
|
|
7515
7922
|
// apps/remotebuddy/src/worker_spawn.ts
|
|
7923
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
7924
|
+
function createWorkerPalId(options = {}) {
|
|
7925
|
+
const randomPart = String(options.randomId ?? randomUUID2()).replace(/[^a-z0-9]/gi, "");
|
|
7926
|
+
const timePart = Math.max(0, Math.floor(options.nowMs ?? Date.now())).toString(36);
|
|
7927
|
+
const pidPart = Math.max(0, Math.floor(options.processId ?? process.pid)).toString(36);
|
|
7928
|
+
const suffix = `${timePart}${pidPart}${randomPart}`.toLowerCase().slice(0, 12);
|
|
7929
|
+
return `workerpal-${suffix || "worker"}`;
|
|
7930
|
+
}
|
|
7516
7931
|
function resolveWorkerStartupTimeoutMs(options) {
|
|
7517
7932
|
const configuredMs = Math.max(1000, Math.floor(options.configuredMs || 0));
|
|
7518
7933
|
if (!options.docker) {
|
|
@@ -7806,7 +8221,7 @@ function ensureWriteGlobsCoverTargetPaths(targetPaths, writeGlobs) {
|
|
|
7806
8221
|
}
|
|
7807
8222
|
return { normalizedWriteGlobs, uncoveredTargets, addedGlobs };
|
|
7808
8223
|
}
|
|
7809
|
-
function buildExecutionGuidance(plan, targetPaths) {
|
|
8224
|
+
function buildExecutionGuidance(plan, targetPaths, requiredValidationSteps = []) {
|
|
7810
8225
|
const lines = [];
|
|
7811
8226
|
const targets = normalizePathHints(targetPaths.length > 0 ? targetPaths : plan.scope.write_globs ?? []);
|
|
7812
8227
|
if (targets.length > 0) {
|
|
@@ -7860,10 +8275,16 @@ function buildExecutionGuidance(plan, targetPaths) {
|
|
|
7860
8275
|
for (const step of plan.validation_steps)
|
|
7861
8276
|
lines.push(`- ${step}`);
|
|
7862
8277
|
}
|
|
8278
|
+
if (requiredValidationSteps.length > 0) {
|
|
8279
|
+
lines.push("Required vision.md testing criteria:");
|
|
8280
|
+
for (const step of requiredValidationSteps)
|
|
8281
|
+
lines.push(`- ${step}`);
|
|
8282
|
+
lines.push("- These repo-level checks are mandatory before reporting completion or publishing a PR.");
|
|
8283
|
+
}
|
|
7863
8284
|
return lines.join(`
|
|
7864
8285
|
`).trim();
|
|
7865
8286
|
}
|
|
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;
|
|
8287
|
+
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
8288
|
var VALIDATION_GENERIC_SAFE = /^(git\s+status\s+--porcelain|git\s+diff\b)/i;
|
|
7868
8289
|
var PATH_TOKEN_REGEX = /\b([A-Za-z0-9._/\-\\]+\.[A-Za-z0-9._-]+)\b/g;
|
|
7869
8290
|
function isCommandLikeValidationStep(step) {
|
|
@@ -7912,6 +8333,34 @@ function normalizeValidationSteps(steps, targetPaths) {
|
|
|
7912
8333
|
}
|
|
7913
8334
|
return out;
|
|
7914
8335
|
}
|
|
8336
|
+
function extractCommandFromValidationCriterion(value) {
|
|
8337
|
+
const raw = String(value ?? "").trim();
|
|
8338
|
+
if (!raw)
|
|
8339
|
+
return "";
|
|
8340
|
+
const fenced = raw.match(/`([^`]+)`/)?.[1]?.trim();
|
|
8341
|
+
const candidate = fenced || raw.replace(/^(run|execute|verify|validate|check)\s+/i, "").trim();
|
|
8342
|
+
return candidate.trim();
|
|
8343
|
+
}
|
|
8344
|
+
function extractRequiredValidationStepsFromVisionMarkdown(markdown) {
|
|
8345
|
+
const criteria = extractVisionKeyItems(markdown).testingCriteria;
|
|
8346
|
+
const out = [];
|
|
8347
|
+
const seen = new Set;
|
|
8348
|
+
for (const criterion of criteria) {
|
|
8349
|
+
const command = extractCommandFromValidationCriterion(criterion);
|
|
8350
|
+
if (!command)
|
|
8351
|
+
continue;
|
|
8352
|
+
if (!isCommandLikeValidationStep(command))
|
|
8353
|
+
continue;
|
|
8354
|
+
const key = command.toLowerCase();
|
|
8355
|
+
if (seen.has(key))
|
|
8356
|
+
continue;
|
|
8357
|
+
seen.add(key);
|
|
8358
|
+
out.push(command);
|
|
8359
|
+
if (out.length >= 12)
|
|
8360
|
+
break;
|
|
8361
|
+
}
|
|
8362
|
+
return out;
|
|
8363
|
+
}
|
|
7915
8364
|
function defaultValidationStepsForRequest(prompt, targetPaths) {
|
|
7916
8365
|
const text = prompt.toLowerCase();
|
|
7917
8366
|
const concreteTargets = targetPaths.filter((entry) => entry && entry !== ".").slice(0, 4);
|
|
@@ -7945,6 +8394,44 @@ function sanitizePlannerWorkerInstruction(workerInstruction, canonicalInstructio
|
|
|
7945
8394
|
}
|
|
7946
8395
|
return value;
|
|
7947
8396
|
}
|
|
8397
|
+
function summarizeToolRun(toolRun) {
|
|
8398
|
+
const command = toSingleLine(toolRun.commandLine, 160) || (Array.isArray(toolRun.argv) ? toSingleLine(toolRun.argv.join(" "), 160) : "");
|
|
8399
|
+
const parts = [
|
|
8400
|
+
`tool=${toSingleLine(toolRun.tool, 40) || "unknown"}`,
|
|
8401
|
+
toolRun.phase ? `phase=${toSingleLine(toolRun.phase, 60)}` : "",
|
|
8402
|
+
command ? `cmd=${command}` : "",
|
|
8403
|
+
toolRun.failureClass ? `class=${toSingleLine(toolRun.failureClass, 60)}` : "",
|
|
8404
|
+
typeof toolRun.retryable === "boolean" ? `retryable=${toolRun.retryable ? "yes" : "no"}` : "",
|
|
8405
|
+
typeof toolRun.exitCode === "number" ? `exit=${toolRun.exitCode}` : "",
|
|
8406
|
+
toolRun.remediation ? `fix=${toSingleLine(toolRun.remediation, 220)}` : ""
|
|
8407
|
+
].filter(Boolean);
|
|
8408
|
+
return parts.join(" | ");
|
|
8409
|
+
}
|
|
8410
|
+
function explainJobFailureFromToolRuns(toolRuns) {
|
|
8411
|
+
const failed = toolRuns.find((entry) => entry && entry.ok === false);
|
|
8412
|
+
if (!failed)
|
|
8413
|
+
return null;
|
|
8414
|
+
const tool = toSingleLine(failed.tool, 40) || "unknown tool";
|
|
8415
|
+
const failureClass = toSingleLine(failed.failureClass, 80) || "tool failure";
|
|
8416
|
+
const remediation = toSingleLine(failed.remediation, 260);
|
|
8417
|
+
const command = toSingleLine(failed.commandLine, 140) || (Array.isArray(failed.argv) ? toSingleLine(failed.argv.join(" "), 140) : "");
|
|
8418
|
+
return [
|
|
8419
|
+
`Latest tool failure: ${tool} reported ${failureClass}`,
|
|
8420
|
+
command ? `while running ${command}` : "",
|
|
8421
|
+
remediation ? `Recommended fix: ${remediation}` : ""
|
|
8422
|
+
].filter(Boolean).join(". ");
|
|
8423
|
+
}
|
|
8424
|
+
function formatToolRunDiagnostics(toolRuns) {
|
|
8425
|
+
const failed = toolRuns.filter((entry) => entry && entry.ok === false).slice(0, 4);
|
|
8426
|
+
if (failed.length === 0)
|
|
8427
|
+
return "";
|
|
8428
|
+
return `
|
|
8429
|
+
Tool diagnostics:
|
|
8430
|
+
\`\`\`
|
|
8431
|
+
${failed.map(summarizeToolRun).join(`
|
|
8432
|
+
`)}
|
|
8433
|
+
\`\`\``;
|
|
8434
|
+
}
|
|
7948
8435
|
function explainJobFailureFromLogs(logs, fallbackMessage, fallbackDetail) {
|
|
7949
8436
|
const lines = logs.map((row) => toSingleLine(row.message, 420)).filter(Boolean);
|
|
7950
8437
|
const joined = lines.join(`
|
|
@@ -8058,6 +8545,7 @@ class RemoteBuddyOrchestrator {
|
|
|
8058
8545
|
autonomyConfigPollTimer = null;
|
|
8059
8546
|
managedWorkers = new Map;
|
|
8060
8547
|
workerSpawnInFlight = null;
|
|
8548
|
+
workerStartupPrewarmInFlight = null;
|
|
8061
8549
|
workerSpawnCooldownUntil = 0;
|
|
8062
8550
|
workerSpawnBackoffMs;
|
|
8063
8551
|
workerAutoscalePollMs;
|
|
@@ -8281,6 +8769,22 @@ class RemoteBuddyOrchestrator {
|
|
|
8281
8769
|
return [];
|
|
8282
8770
|
}
|
|
8283
8771
|
}
|
|
8772
|
+
async fetchJobToolRuns(jobId, limit = 20) {
|
|
8773
|
+
try {
|
|
8774
|
+
const res = await fetch(`${this.server}/jobs/${jobId}/tool-runs?limit=${Math.max(1, Math.min(100, limit))}`, {
|
|
8775
|
+
method: "GET",
|
|
8776
|
+
headers: this.authHeaders()
|
|
8777
|
+
});
|
|
8778
|
+
if (!res.ok)
|
|
8779
|
+
return [];
|
|
8780
|
+
const data = await res.json();
|
|
8781
|
+
if (!data.ok || !Array.isArray(data.toolRuns))
|
|
8782
|
+
return [];
|
|
8783
|
+
return data.toolRuns.filter((row) => row && typeof row.tool === "string").slice(0, 20);
|
|
8784
|
+
} catch {
|
|
8785
|
+
return [];
|
|
8786
|
+
}
|
|
8787
|
+
}
|
|
8284
8788
|
markAutonomyFeedbackEventSeen(eventId) {
|
|
8285
8789
|
const id = String(eventId ?? "").trim();
|
|
8286
8790
|
if (!id)
|
|
@@ -8425,7 +8929,10 @@ class RemoteBuddyOrchestrator {
|
|
|
8425
8929
|
return;
|
|
8426
8930
|
}
|
|
8427
8931
|
console.warn(`[RemoteBuddy] Fetching failure logs for job ${jobId}...`);
|
|
8428
|
-
const logs = await
|
|
8932
|
+
const [logs, toolRuns] = await Promise.all([
|
|
8933
|
+
this.fetchJobLogs(jobId, 80),
|
|
8934
|
+
this.fetchJobToolRuns(jobId, 20)
|
|
8935
|
+
]);
|
|
8429
8936
|
const clarificationFromLogs = extractClarificationFromJobFailure(message, detail, logs);
|
|
8430
8937
|
if (clarificationFromLogs) {
|
|
8431
8938
|
const tail2 = logs.slice(-6).map((row) => toSingleLine(row.message, 220)).filter(Boolean);
|
|
@@ -8435,9 +8942,10 @@ Recent logs:
|
|
|
8435
8942
|
${tail2.join(`
|
|
8436
8943
|
`)}
|
|
8437
8944
|
\`\`\`` : "";
|
|
8945
|
+
const toolText2 = formatToolRunDiagnostics(toolRuns);
|
|
8438
8946
|
const clarificationMsg = `WorkerPal job ${shortJob} needs clarification before making changes: ${clarificationFromLogs}
|
|
8439
8947
|
|
|
8440
|
-
` + "Reply with the missing details and I will enqueue a focused follow-up request." + tailText2;
|
|
8948
|
+
` + "Reply with the missing details and I will enqueue a focused follow-up request." + toolText2 + tailText2;
|
|
8441
8949
|
await this.assistantMessage(sessionId, clarificationMsg, {
|
|
8442
8950
|
correlationId: envelope.correlationId,
|
|
8443
8951
|
turnId: envelope.turnId,
|
|
@@ -8445,7 +8953,7 @@ ${tail2.join(`
|
|
|
8445
8953
|
});
|
|
8446
8954
|
return;
|
|
8447
8955
|
}
|
|
8448
|
-
const explanation = explainJobFailureFromLogs(logs, message, detail);
|
|
8956
|
+
const explanation = explainJobFailureFromToolRuns(toolRuns) ?? explainJobFailureFromLogs(logs, message, detail);
|
|
8449
8957
|
const tail = logs.slice(-6).map((row) => toSingleLine(row.message, 220)).filter(Boolean);
|
|
8450
8958
|
const tailText = tail.length ? `
|
|
8451
8959
|
Recent logs:
|
|
@@ -8453,7 +8961,8 @@ Recent logs:
|
|
|
8453
8961
|
${tail.join(`
|
|
8454
8962
|
`)}
|
|
8455
8963
|
\`\`\`` : "";
|
|
8456
|
-
|
|
8964
|
+
const toolText = formatToolRunDiagnostics(toolRuns);
|
|
8965
|
+
await this.assistantMessage(sessionId, `Diagnosis for job ${shortJob}: ${explanation}${toolText}${tailText}`, {
|
|
8457
8966
|
correlationId: envelope.correlationId,
|
|
8458
8967
|
turnId: envelope.turnId,
|
|
8459
8968
|
parentId: envelope.id
|
|
@@ -8728,6 +9237,17 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
8728
9237
|
}
|
|
8729
9238
|
return out;
|
|
8730
9239
|
}
|
|
9240
|
+
loadVisionRequiredValidationSteps() {
|
|
9241
|
+
const visionPath = resolve5(this.repo, "vision.md");
|
|
9242
|
+
if (!existsSync5(visionPath))
|
|
9243
|
+
return [];
|
|
9244
|
+
try {
|
|
9245
|
+
return extractRequiredValidationStepsFromVisionMarkdown(readFileSync5(visionPath, "utf8"));
|
|
9246
|
+
} catch (err) {
|
|
9247
|
+
console.warn("[RemoteBuddy] Could not read vision.md testing criteria:", err);
|
|
9248
|
+
return [];
|
|
9249
|
+
}
|
|
9250
|
+
}
|
|
8731
9251
|
getRecentContextSnapshot(sessionId = this.sessionId) {
|
|
8732
9252
|
return this.sessionContext(sessionId).slice(-RemoteBuddyOrchestrator.MAX_CONTEXT);
|
|
8733
9253
|
}
|
|
@@ -9028,7 +9548,7 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
9028
9548
|
}
|
|
9029
9549
|
const spawnPromise = (async () => {
|
|
9030
9550
|
this.workerpalsUnavailableReason = null;
|
|
9031
|
-
const workerId =
|
|
9551
|
+
const workerId = createWorkerPalId();
|
|
9032
9552
|
const cmd = this.buildWorkerSpawnCommand(workerId);
|
|
9033
9553
|
console.log(`[RemoteBuddy] Spawning WorkerPal ${workerId} (${this.managedWorkers.size + 1}/${this.maxWorkers})`);
|
|
9034
9554
|
try {
|
|
@@ -9114,6 +9634,16 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
9114
9634
|
}
|
|
9115
9635
|
console.warn(`[RemoteBuddy] ${this.currentWorkerUnavailableReason()}`);
|
|
9116
9636
|
}
|
|
9637
|
+
startWorkerCapacityPrewarmOnStartup() {
|
|
9638
|
+
if (this.workerStartupPrewarmInFlight || this.disposed)
|
|
9639
|
+
return;
|
|
9640
|
+
this.workerStartupPrewarmInFlight = this.ensureWorkerCapacityOnStartup().catch((err) => {
|
|
9641
|
+
this.workerpalsUnavailableReason = `WorkerPal startup prewarm failed: ${String(err)}`;
|
|
9642
|
+
console.warn(`[RemoteBuddy] ${this.workerpalsUnavailableReason}`);
|
|
9643
|
+
}).finally(() => {
|
|
9644
|
+
this.workerStartupPrewarmInFlight = null;
|
|
9645
|
+
});
|
|
9646
|
+
}
|
|
9117
9647
|
async selectTargetWorkerForJob() {
|
|
9118
9648
|
const workers = await this.fetchWorkers();
|
|
9119
9649
|
const idleNow = this.pickIdleWorker(workers);
|
|
@@ -9162,7 +9692,7 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
9162
9692
|
}
|
|
9163
9693
|
const priority = normalizeRequestPriority(request.priority);
|
|
9164
9694
|
const queueWaitBudgetMs = Math.max(5000, Number.isFinite(Number(request.queueWaitBudgetMs)) ? Number(request.queueWaitBudgetMs) : priority === "interactive" ? 20000 : priority === "background" ? 240000 : 90000);
|
|
9165
|
-
const turnId =
|
|
9695
|
+
const turnId = randomUUID3();
|
|
9166
9696
|
const eventFrom = autonomyMetadata ? `agent:${this.agentId}/autonomy` : undefined;
|
|
9167
9697
|
const planningContext = this.buildPlanningContext(priority, requestSessionId);
|
|
9168
9698
|
this.rememberPersistentMemory("request", `priority=${priority} prompt=${toSingleLine(prompt, 520)}`, requestId, requestSessionId);
|
|
@@ -9190,7 +9720,12 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
9190
9720
|
const isAnalysisFromEngine = plan.intent === "analysis" && Boolean(autonomyMetadata);
|
|
9191
9721
|
const requiresWorker = forceWorker && !isAnalysisFromEngine ? true : this.shouldForceDirectReply(prompt, plan.intent) ? false : plan.requires_worker;
|
|
9192
9722
|
console.log("[RemoteBuddy] Planner output:", { plan, targetPath, requiresWorker });
|
|
9723
|
+
let requiredValidationSteps = [];
|
|
9193
9724
|
if (requiresWorker) {
|
|
9725
|
+
requiredValidationSteps = this.loadVisionRequiredValidationSteps();
|
|
9726
|
+
if (requiredValidationSteps.length > 0) {
|
|
9727
|
+
console.log(`[RemoteBuddy] Loaded ${requiredValidationSteps.length} required validation step(s) from vision.md testing criteria.`);
|
|
9728
|
+
}
|
|
9194
9729
|
const scopeCoverage = ensureWriteGlobsCoverTargetPaths(targetPaths, plan.scope.write_globs);
|
|
9195
9730
|
if (scopeCoverage.normalizedWriteGlobs.length > 0) {
|
|
9196
9731
|
plan.scope.write_globs = scopeCoverage.normalizedWriteGlobs;
|
|
@@ -9240,7 +9775,7 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
9240
9775
|
}
|
|
9241
9776
|
const canonicalInstruction = prompt.trim();
|
|
9242
9777
|
const rawPlannerInstruction = sanitizePlannerWorkerInstruction(String(plan.worker_instruction ?? ""), canonicalInstruction);
|
|
9243
|
-
const executionGuidance = buildExecutionGuidance(plan, targetPaths);
|
|
9778
|
+
const executionGuidance = buildExecutionGuidance(plan, targetPaths, requiredValidationSteps);
|
|
9244
9779
|
const plannerWorkerInstruction = [rawPlannerInstruction, executionGuidance].filter(Boolean).join(`
|
|
9245
9780
|
|
|
9246
9781
|
`).trim();
|
|
@@ -9286,7 +9821,7 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
9286
9821
|
this.rememberPersistentMemory("decision", `completed_without_worker intent=${plan.intent} lane=deterministic`, requestId, requestSessionId);
|
|
9287
9822
|
return;
|
|
9288
9823
|
}
|
|
9289
|
-
const taskId =
|
|
9824
|
+
const taskId = randomUUID3();
|
|
9290
9825
|
const targetWorkerId = await this.selectTargetWorkerForJob();
|
|
9291
9826
|
if (!targetWorkerId) {
|
|
9292
9827
|
const onlineWorkers = this.onlineWorkers(await this.fetchWorkers());
|
|
@@ -9346,6 +9881,7 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
9346
9881
|
} : {},
|
|
9347
9882
|
acceptanceCriteria: plan.acceptance_criteria,
|
|
9348
9883
|
validationSteps: plan.validation_steps,
|
|
9884
|
+
...requiredValidationSteps.length > 0 ? { requiredValidationSteps } : {},
|
|
9349
9885
|
queuePriority: priority,
|
|
9350
9886
|
queueWaitBudgetMs,
|
|
9351
9887
|
executionBudgetMs,
|
|
@@ -9662,7 +10198,7 @@ async function main() {
|
|
|
9662
10198
|
await orchestrator.emitStartupStatus();
|
|
9663
10199
|
orchestrator.startStatusHeartbeat();
|
|
9664
10200
|
orchestrator.startSessionEventMonitor();
|
|
9665
|
-
|
|
10201
|
+
orchestrator.startWorkerCapacityPrewarmOnStartup();
|
|
9666
10202
|
orchestrator.startAutonomy();
|
|
9667
10203
|
orchestrator.startAutonomyRuntimeConfigPolling();
|
|
9668
10204
|
const pollMs = CONFIG.remotebuddy.pollMs;
|
|
@@ -9675,6 +10211,7 @@ if (import.meta.main) {
|
|
|
9675
10211
|
});
|
|
9676
10212
|
}
|
|
9677
10213
|
export {
|
|
10214
|
+
extractRequiredValidationStepsFromVisionMarkdown,
|
|
9678
10215
|
buildTaskExecuteDedupeKey,
|
|
9679
10216
|
RemoteBuddyOrchestrator
|
|
9680
10217
|
};
|