@pushpalsdev/cli 1.1.18 → 1.1.20
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 +16 -2
- package/package.json +1 -1
- package/runtime/configs/default.toml +1 -0
- package/runtime/sandbox/.pushpals-remotebuddy-fallback.js +199 -13
- package/runtime/sandbox/apps/workerpals/src/backends/openai_codex/openai_codex_executor.py +250 -6
- package/runtime/sandbox/apps/workerpals/src/backends/openai_codex/test_openai_codex_runtime_config.py +223 -0
- package/runtime/sandbox/apps/workerpals/src/backends/shared/executor_base.py +9 -0
- package/runtime/sandbox/apps/workerpals/src/docker_executor.ts +42 -1
- package/runtime/sandbox/apps/workerpals/src/execute_job.ts +450 -5
- package/runtime/sandbox/apps/workerpals/src/workerpals_main.ts +13 -26
- package/runtime/sandbox/apps/workerpals/src/worktree_base_ref.ts +141 -0
- package/runtime/sandbox/configs/default.toml +1 -0
- package/runtime/sandbox/packages/shared/src/config.ts +9 -0
package/dist/pushpals-cli.js
CHANGED
|
@@ -1040,6 +1040,7 @@ function loadPushPalsConfig(options = {}) {
|
|
|
1040
1040
|
enabled: parseBoolEnv("REMOTEBUDDY_AUTONOMY_ENABLED") ?? asBoolean(remoteAutonomyNode.enabled, true),
|
|
1041
1041
|
killSwitchEnabled: parseBoolEnv("REMOTEBUDDY_AUTONOMY_KILL_SWITCH_ENABLED") ?? asBoolean(remoteAutonomyNode.kill_switch_enabled, false),
|
|
1042
1042
|
tickIntervalMs: Math.max(5000, asInt(parseIntEnv("REMOTEBUDDY_AUTONOMY_TICK_INTERVAL_MS") ?? remoteAutonomyNode.tick_interval_ms, 120000)),
|
|
1043
|
+
startupGraceMs: Math.max(0, asInt(parseIntEnv("REMOTEBUDDY_AUTONOMY_STARTUP_GRACE_MS") ?? remoteAutonomyNode.startup_grace_ms, 120000)),
|
|
1043
1044
|
heartbeatLogMs: Math.max(1000, asInt(parseIntEnv("REMOTEBUDDY_AUTONOMY_HEARTBEAT_LOG_MS") ?? remoteAutonomyNode.heartbeat_log_ms, 30000)),
|
|
1044
1045
|
visionContextMaxChars: Math.max(1000, Math.min(1e6, asInt(parseIntEnv("REMOTEBUDDY_AUTONOMY_VISION_CONTEXT_MAX_CHARS") ?? remoteAutonomyNode.vision_context_max_chars, 65536))),
|
|
1045
1046
|
ideationBudgetMs: Math.max(1000, asInt(parseIntEnv("REMOTEBUDDY_AUTONOMY_IDEATION_BUDGET_MS") ?? remoteAutonomyNode.ideation_budget_ms, 20000)),
|
|
@@ -1648,7 +1649,8 @@ var DEFAULT_STARTUP_GIT_PROBE_TIMEOUT_MS = 5000;
|
|
|
1648
1649
|
var DEFAULT_STARTUP_GIT_REMOTE_TIMEOUT_MS = 1e4;
|
|
1649
1650
|
var DEFAULT_EMBEDDED_SERVICE_LAUNCH_WARN_MS = 5000;
|
|
1650
1651
|
var EMBEDDED_SERVICE_RESTART_MAX_ATTEMPTS = 4;
|
|
1651
|
-
var
|
|
1652
|
+
var DEFAULT_WORKERPAL_STARTUP_READINESS_PROBE_MAX_MS = 5000;
|
|
1653
|
+
var WORKERPAL_STARTUP_READINESS_PROBE_MAX_MS_ENV = "PUSHPALS_WORKERPAL_STARTUP_READINESS_PROBE_MAX_MS";
|
|
1652
1654
|
var BLOCKING_WORKERPAL_IMAGE_BUILD_ENV = "PUSHPALS_BLOCKING_WORKERPAL_IMAGE_BUILD";
|
|
1653
1655
|
var CLI_SESSION_JOB_LOG_MAX_CHARS = 700;
|
|
1654
1656
|
var CLI_SESSION_SHOW_JOB_EVENTS_ENV = "PUSHPALS_CLI_SHOW_JOB_EVENTS";
|
|
@@ -1681,6 +1683,13 @@ function formatTimestampedCliLine(line, at = new Date) {
|
|
|
1681
1683
|
function isTruthyCliEnvValue(value) {
|
|
1682
1684
|
return /^(1|true|yes|on)$/i.test(String(value ?? "").trim());
|
|
1683
1685
|
}
|
|
1686
|
+
function parseCliIntEnv(name, env = process.env) {
|
|
1687
|
+
const raw = env[name];
|
|
1688
|
+
if (raw == null || String(raw).trim() === "")
|
|
1689
|
+
return null;
|
|
1690
|
+
const parsed = Number.parseInt(String(raw), 10);
|
|
1691
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
1692
|
+
}
|
|
1684
1693
|
function shouldShowCliSessionOperationalEvents(env = process.env) {
|
|
1685
1694
|
return isTruthyCliEnvValue(env[CLI_SESSION_SHOW_JOB_EVENTS_ENV]);
|
|
1686
1695
|
}
|
|
@@ -3899,8 +3908,12 @@ async function precheckWorkerpalDockerAvailability(opts) {
|
|
|
3899
3908
|
function resolveWorkerpalCapacityTimeoutMs(config) {
|
|
3900
3909
|
return Math.max(config.remotebuddy.waitForWorkerpalMs, config.remotebuddy.workerpalStartupTimeoutMs, config.remotebuddy.workerpalDocker ? config.workerpals.dockerAgentStartupTimeoutMs + 15000 : 0, 1e4);
|
|
3901
3910
|
}
|
|
3911
|
+
function resolveWorkerpalStartupReadinessProbeMaxMs(env = process.env) {
|
|
3912
|
+
const configured = parseCliIntEnv(WORKERPAL_STARTUP_READINESS_PROBE_MAX_MS_ENV, env);
|
|
3913
|
+
return Math.max(1000, configured ?? DEFAULT_WORKERPAL_STARTUP_READINESS_PROBE_MAX_MS);
|
|
3914
|
+
}
|
|
3902
3915
|
function resolveWorkerpalStartupReadinessProbeTimeoutMs(config) {
|
|
3903
|
-
return Math.max(
|
|
3916
|
+
return Math.max(1000, Math.min(resolveWorkerpalCapacityTimeoutMs(config), resolveWorkerpalStartupReadinessProbeMaxMs()));
|
|
3904
3917
|
}
|
|
3905
3918
|
function shouldPrepareEmbeddedWorkerpalDockerImageBlocking(opts = {}) {
|
|
3906
3919
|
const env = opts.env ?? process.env;
|
|
@@ -6082,6 +6095,7 @@ export {
|
|
|
6082
6095
|
shouldPrepareEmbeddedWorkerpalDockerImageBlocking,
|
|
6083
6096
|
shouldDeferRemoteBuddySessionConsumerReadiness,
|
|
6084
6097
|
runCommandWithEnv,
|
|
6098
|
+
resolveWorkerpalStartupReadinessProbeMaxMs,
|
|
6085
6099
|
resolveWorkerpalDockerProbe,
|
|
6086
6100
|
resolveWorkerExecutionReadiness,
|
|
6087
6101
|
resolveWindowsWhereExecutableCandidatesForEnv,
|
package/package.json
CHANGED
|
@@ -1323,6 +1323,7 @@ function loadPushPalsConfig(options = {}) {
|
|
|
1323
1323
|
enabled: parseBoolEnv("REMOTEBUDDY_AUTONOMY_ENABLED") ?? asBoolean(remoteAutonomyNode.enabled, true),
|
|
1324
1324
|
killSwitchEnabled: parseBoolEnv("REMOTEBUDDY_AUTONOMY_KILL_SWITCH_ENABLED") ?? asBoolean(remoteAutonomyNode.kill_switch_enabled, false),
|
|
1325
1325
|
tickIntervalMs: Math.max(5000, asInt(parseIntEnv("REMOTEBUDDY_AUTONOMY_TICK_INTERVAL_MS") ?? remoteAutonomyNode.tick_interval_ms, 120000)),
|
|
1326
|
+
startupGraceMs: Math.max(0, asInt(parseIntEnv("REMOTEBUDDY_AUTONOMY_STARTUP_GRACE_MS") ?? remoteAutonomyNode.startup_grace_ms, 120000)),
|
|
1326
1327
|
heartbeatLogMs: Math.max(1000, asInt(parseIntEnv("REMOTEBUDDY_AUTONOMY_HEARTBEAT_LOG_MS") ?? remoteAutonomyNode.heartbeat_log_ms, 30000)),
|
|
1327
1328
|
visionContextMaxChars: Math.max(1000, Math.min(1e6, asInt(parseIntEnv("REMOTEBUDDY_AUTONOMY_VISION_CONTEXT_MAX_CHARS") ?? remoteAutonomyNode.vision_context_max_chars, 65536))),
|
|
1328
1329
|
ideationBudgetMs: Math.max(1000, asInt(parseIntEnv("REMOTEBUDDY_AUTONOMY_IDEATION_BUDGET_MS") ?? remoteAutonomyNode.ideation_budget_ms, 20000)),
|
|
@@ -4812,6 +4813,46 @@ var IGNORED_REPO_TARGET_DIRS = new Set([
|
|
|
4812
4813
|
"__pycache__",
|
|
4813
4814
|
"target"
|
|
4814
4815
|
]);
|
|
4816
|
+
function isPushPalsRepository(repoRoot) {
|
|
4817
|
+
return existsSync4(resolve4(repoRoot, "apps", "remotebuddy", "src", "autonomous_engine.ts")) && existsSync4(resolve4(repoRoot, "apps", "workerpals", "src", "workerpals_main.ts")) && existsSync4(resolve4(repoRoot, "packages", "shared", "src", "autonomy_policy.ts"));
|
|
4818
|
+
}
|
|
4819
|
+
function isPushPalsInternalUserRepoPath(path) {
|
|
4820
|
+
const normalized = asString2(path).replace(/\\/g, "/").toLowerCase();
|
|
4821
|
+
if (!normalized)
|
|
4822
|
+
return false;
|
|
4823
|
+
return /(^|\/)_layout\.autonomy\.test\.[cm]?[jt]sx?$/.test(normalized);
|
|
4824
|
+
}
|
|
4825
|
+
function containsPushPalsInternalUserRepoText(text) {
|
|
4826
|
+
return /\b(queue_health|workerpal|remotebuddy|sourcecontrolmanager|source_control_manager|reviewagent|pushpals)\b/i.test(text);
|
|
4827
|
+
}
|
|
4828
|
+
function candidateLeaksPushPalsInternals(candidate) {
|
|
4829
|
+
if ([candidate.component_area, ...candidate.target_paths].some((path) => isPushPalsInternalUserRepoPath(path))) {
|
|
4830
|
+
return true;
|
|
4831
|
+
}
|
|
4832
|
+
const publicText = [
|
|
4833
|
+
candidate.title,
|
|
4834
|
+
candidate.problem_statement,
|
|
4835
|
+
candidate.vision_alignment_reason,
|
|
4836
|
+
...candidate.feature_hypotheses,
|
|
4837
|
+
...candidate.target_paths
|
|
4838
|
+
].join(`
|
|
4839
|
+
`);
|
|
4840
|
+
return containsPushPalsInternalUserRepoText(publicText);
|
|
4841
|
+
}
|
|
4842
|
+
function buildRepoNativeFallbackInstruction(candidate) {
|
|
4843
|
+
return [
|
|
4844
|
+
candidate.title,
|
|
4845
|
+
"",
|
|
4846
|
+
candidate.problem_statement,
|
|
4847
|
+
"",
|
|
4848
|
+
"Keep the change scoped to the repo's own product/runtime behavior. Do not add PushPals, WorkerPal, RemoteBuddy, queue-health, or autonomy-internal concepts to user-facing code or tests.",
|
|
4849
|
+
"",
|
|
4850
|
+
"Scope:",
|
|
4851
|
+
`- target_paths: ${candidate.target_paths.join(", ")}`,
|
|
4852
|
+
`- write_globs: ${candidate.scope.write_globs.join(", ")}`
|
|
4853
|
+
].join(`
|
|
4854
|
+
`);
|
|
4855
|
+
}
|
|
4815
4856
|
function pathBasename(path) {
|
|
4816
4857
|
const normalized = path.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
4817
4858
|
const idx = normalized.lastIndexOf("/");
|
|
@@ -4890,10 +4931,13 @@ function collectRepoTargetFiles(repoRoot, startRelativePath, maxResults, maxDept
|
|
|
4890
4931
|
function discoverRepoTargetProfiles(repoRoot, maxProfiles = 16) {
|
|
4891
4932
|
const profiles = [];
|
|
4892
4933
|
const seen = new Set;
|
|
4934
|
+
const allowPushPalsInternalTargets = isPushPalsRepository(repoRoot);
|
|
4893
4935
|
const add = (targetPath) => {
|
|
4894
4936
|
const finalPath = normalizeAutonomyComponentArea(targetPath);
|
|
4895
4937
|
if (!finalPath)
|
|
4896
4938
|
return;
|
|
4939
|
+
if (!allowPushPalsInternalTargets && isPushPalsInternalUserRepoPath(finalPath))
|
|
4940
|
+
return;
|
|
4897
4941
|
if (seen.has(finalPath))
|
|
4898
4942
|
return;
|
|
4899
4943
|
seen.add(finalPath);
|
|
@@ -4959,6 +5003,8 @@ function chooseRepoObjectiveTargetProfile(profiles, objective) {
|
|
|
4959
5003
|
let best = null;
|
|
4960
5004
|
for (const profile of profiles) {
|
|
4961
5005
|
const label = profile.label.toLowerCase();
|
|
5006
|
+
if (isPushPalsInternalUserRepoPath(label))
|
|
5007
|
+
continue;
|
|
4962
5008
|
const profileTokens = new Set(profile.keywords);
|
|
4963
5009
|
let score = 0;
|
|
4964
5010
|
for (const token of hintTokens) {
|
|
@@ -4988,6 +5034,17 @@ function chooseRepoObjectiveTargetProfile(profiles, objective) {
|
|
|
4988
5034
|
if (productSurface)
|
|
4989
5035
|
score += 1;
|
|
4990
5036
|
}
|
|
5037
|
+
if (/\b(web|browser|smoke|e2e|review path|review|navigation|delivery|trust)\b/i.test(objective.title)) {
|
|
5038
|
+
if (/(^|\/)(scripts?|tools?)\/.*(web|browser|smoke|e2e|playwright)/i.test(label)) {
|
|
5039
|
+
score += 8;
|
|
5040
|
+
}
|
|
5041
|
+
if (/\b(app\/(_layout|index)|route|navigation|shell|home|screen)\b/i.test(label)) {
|
|
5042
|
+
score += 4;
|
|
5043
|
+
}
|
|
5044
|
+
if (validationSurface && !/(web|browser|smoke|e2e|playwright)/i.test(label)) {
|
|
5045
|
+
score -= 3;
|
|
5046
|
+
}
|
|
5047
|
+
}
|
|
4991
5048
|
if (categories.has("performance")) {
|
|
4992
5049
|
if (productSurface || /\b(perf|render|animation|worker|server)\b/i.test(label))
|
|
4993
5050
|
score += 4;
|
|
@@ -6513,6 +6570,7 @@ class RemoteBuddyAutonomousEngine {
|
|
|
6513
6570
|
cfg;
|
|
6514
6571
|
runtimeEnabled = true;
|
|
6515
6572
|
timer = null;
|
|
6573
|
+
startupGraceTimer = null;
|
|
6516
6574
|
startupFastTickTimer = null;
|
|
6517
6575
|
heartbeatTimer = null;
|
|
6518
6576
|
inFlight = false;
|
|
@@ -6585,7 +6643,8 @@ class RemoteBuddyAutonomousEngine {
|
|
|
6585
6643
|
console.log(`[RemoteBuddyAutonomousEngine] heartbeat: status=running run=${this.currentRunId} phase=${this.currentPhase} run_elapsed_ms=${runElapsedMs} phase_elapsed_ms=${phaseElapsedMs}`);
|
|
6586
6644
|
return;
|
|
6587
6645
|
}
|
|
6588
|
-
const
|
|
6646
|
+
const hasScheduledTick = Boolean(this.timer || this.startupGraceTimer || this.startupFastTickTimer);
|
|
6647
|
+
const nextTickInMs = hasScheduledTick && this.nextTickAtMs > 0 ? Math.max(0, this.nextTickAtMs - now) : 0;
|
|
6589
6648
|
const lastAgeMs = this.lastCompletedAtMs > 0 ? Math.max(0, now - this.lastCompletedAtMs) : -1;
|
|
6590
6649
|
console.log(`[RemoteBuddyAutonomousEngine] heartbeat: status=idle last_outcome=${this.lastOutcome} detail=${this.lastDetail} last_tick_age_ms=${lastAgeMs} next_tick_in_ms=${nextTickInMs}`);
|
|
6591
6650
|
}
|
|
@@ -6611,6 +6670,15 @@ class RemoteBuddyAutonomousEngine {
|
|
|
6611
6670
|
startupFastTickDelayMs() {
|
|
6612
6671
|
return Math.max(1000, Math.min(STARTUP_FAST_TICK_MAX_DELAY_MS, Math.floor(this.cfg.tickIntervalMs / 10)));
|
|
6613
6672
|
}
|
|
6673
|
+
startupGraceMs() {
|
|
6674
|
+
return Math.max(0, this.cfg.startupGraceMs ?? 0);
|
|
6675
|
+
}
|
|
6676
|
+
clearStartupGraceTimer() {
|
|
6677
|
+
if (this.startupGraceTimer) {
|
|
6678
|
+
clearTimeout(this.startupGraceTimer);
|
|
6679
|
+
this.startupGraceTimer = null;
|
|
6680
|
+
}
|
|
6681
|
+
}
|
|
6614
6682
|
clearStartupFastTickTimer() {
|
|
6615
6683
|
if (this.startupFastTickTimer) {
|
|
6616
6684
|
clearTimeout(this.startupFastTickTimer);
|
|
@@ -7479,6 +7547,7 @@ ${JSON.stringify(input.messages ?? [])}`),
|
|
|
7479
7547
|
}
|
|
7480
7548
|
const normalizedCandidates = [];
|
|
7481
7549
|
const dropReasonCounts = new Map;
|
|
7550
|
+
const allowPushPalsInternalCandidates = isPushPalsRepository(this.autonomyRepo);
|
|
7482
7551
|
const recordDropReason = (reason) => {
|
|
7483
7552
|
dropReasonCounts.set(reason, (dropReasonCounts.get(reason) ?? 0) + 1);
|
|
7484
7553
|
};
|
|
@@ -7553,6 +7622,11 @@ ${JSON.stringify(input.messages ?? [])}`),
|
|
|
7553
7622
|
candidate.component_area = scopeValidation.componentArea ?? candidate.component_area;
|
|
7554
7623
|
candidate.target_paths = scopeValidation.normalizedTargetPaths;
|
|
7555
7624
|
candidate.scope.write_globs = scopeValidation.normalizedWriteGlobs;
|
|
7625
|
+
if (!allowPushPalsInternalCandidates && candidateLeaksPushPalsInternals(candidate)) {
|
|
7626
|
+
recordDropReason(`${source}_pushpals_internal_leak`);
|
|
7627
|
+
console.warn(`[RemoteBuddyAutonomousEngine] dropping candidate ${candidate.id}: PushPals-internal concepts do not belong in user-repo autonomy work.`);
|
|
7628
|
+
continue;
|
|
7629
|
+
}
|
|
7556
7630
|
const missingTargetPaths = findMissingRepoTargetPaths(this.autonomyRepo, candidate.target_paths);
|
|
7557
7631
|
if (missingTargetPaths.length > 0) {
|
|
7558
7632
|
recordDropReason(`${source}_target_paths_missing_in_repo`);
|
|
@@ -7966,13 +8040,17 @@ ${JSON.stringify(input.messages ?? [])}`),
|
|
|
7966
8040
|
outcomeDetail = "lock_renew_failed_before_enqueue";
|
|
7967
8041
|
return;
|
|
7968
8042
|
}
|
|
7969
|
-
|
|
8043
|
+
let instruction = canonicalizeInstructionTextForBun(asString2(planningJson.instruction) || `${selected.candidate.title}
|
|
7970
8044
|
|
|
7971
8045
|
${selected.candidate.problem_statement}
|
|
7972
8046
|
|
|
7973
8047
|
Scope:
|
|
7974
8048
|
- target_paths: ${selected.candidate.target_paths.join(", ")}
|
|
7975
8049
|
- write_globs: ${selected.candidate.scope.write_globs.join(", ")}`);
|
|
8050
|
+
if (!isPushPalsRepository(this.autonomyRepo) && containsPushPalsInternalUserRepoText(instruction)) {
|
|
8051
|
+
console.warn(`[RemoteBuddyAutonomousEngine] replacing autonomy instruction for ${selected.candidate.id}: planner output contained PushPals-internal wording.`);
|
|
8052
|
+
instruction = canonicalizeInstructionTextForBun(buildRepoNativeFallbackInstruction(selected.candidate));
|
|
8053
|
+
}
|
|
7976
8054
|
this.setPhase("enqueue_request");
|
|
7977
8055
|
const requestId = await this.enqueueSyntheticRequest(instruction, {
|
|
7978
8056
|
objectiveId,
|
|
@@ -8094,22 +8172,42 @@ Scope:
|
|
|
8094
8172
|
});
|
|
8095
8173
|
}
|
|
8096
8174
|
start() {
|
|
8097
|
-
if (!this.runtimeEnabled || this.timer)
|
|
8175
|
+
if (!this.runtimeEnabled || this.timer || this.startupGraceTimer)
|
|
8098
8176
|
return;
|
|
8099
8177
|
console.log(`[RemoteBuddyAutonomousEngine] Using dedicated autonomy worktree ${this.autonomyRepo} (remote=${this.gitRemote} integration=${this.integrationBranch} base=${this.baseBranch}).`);
|
|
8100
8178
|
this.startupFastTickAttemptsRemaining = STARTUP_FAST_TICK_MAX_ATTEMPTS;
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
this.
|
|
8105
|
-
|
|
8179
|
+
const startInterval = () => {
|
|
8180
|
+
if (this.timer)
|
|
8181
|
+
return;
|
|
8182
|
+
this.timer = setInterval(() => {
|
|
8183
|
+
this.nextTickAtMs = Date.now() + this.cfg.tickIntervalMs;
|
|
8184
|
+
this.tick();
|
|
8185
|
+
}, this.cfg.tickIntervalMs);
|
|
8186
|
+
};
|
|
8187
|
+
const firstTickDelayMs = this.startupGraceMs();
|
|
8188
|
+
this.nextTickAtMs = Date.now() + firstTickDelayMs;
|
|
8106
8189
|
this.heartbeatTimer = setInterval(() => {
|
|
8107
8190
|
this.logHeartbeat();
|
|
8108
8191
|
}, this.cfg.heartbeatLogMs);
|
|
8109
8192
|
this.logHeartbeat();
|
|
8193
|
+
if (firstTickDelayMs > 0) {
|
|
8194
|
+
console.log(`[RemoteBuddyAutonomousEngine] startup autonomy tick delayed by ${firstTickDelayMs}ms to leave cold-start capacity available for user work.`);
|
|
8195
|
+
this.startupGraceTimer = setTimeout(() => {
|
|
8196
|
+
this.startupGraceTimer = null;
|
|
8197
|
+
if (!this.runtimeEnabled)
|
|
8198
|
+
return;
|
|
8199
|
+
startInterval();
|
|
8200
|
+
this.nextTickAtMs = Date.now() + this.cfg.tickIntervalMs;
|
|
8201
|
+
this.tick();
|
|
8202
|
+
}, firstTickDelayMs);
|
|
8203
|
+
return;
|
|
8204
|
+
}
|
|
8205
|
+
startInterval();
|
|
8206
|
+
this.nextTickAtMs = Date.now() + this.cfg.tickIntervalMs;
|
|
8110
8207
|
this.tick();
|
|
8111
8208
|
}
|
|
8112
8209
|
stop() {
|
|
8210
|
+
this.clearStartupGraceTimer();
|
|
8113
8211
|
this.clearStartupFastTickTimer();
|
|
8114
8212
|
if (this.timer) {
|
|
8115
8213
|
clearInterval(this.timer);
|
|
@@ -8426,7 +8524,7 @@ function ensureWriteGlobsCoverTargetPaths(targetPaths, writeGlobs) {
|
|
|
8426
8524
|
}
|
|
8427
8525
|
return { normalizedWriteGlobs, uncoveredTargets, addedGlobs };
|
|
8428
8526
|
}
|
|
8429
|
-
function buildExecutionGuidance(plan, targetPaths, requiredValidationSteps = []) {
|
|
8527
|
+
function buildExecutionGuidance(plan, targetPaths, requiredValidationSteps = [], repoHintDiagnostics = []) {
|
|
8430
8528
|
const lines = [];
|
|
8431
8529
|
const targets = normalizePathHints(targetPaths.length > 0 ? targetPaths : plan.scope.write_globs ?? []);
|
|
8432
8530
|
if (targets.length > 0) {
|
|
@@ -8438,6 +8536,13 @@ function buildExecutionGuidance(plan, targetPaths, requiredValidationSteps = [])
|
|
|
8438
8536
|
lines.push("- Do not prepend a leading slash to target paths.");
|
|
8439
8537
|
lines.push("- These paths are relevance hints, not hard write boundaries; edit the behavior-owning files needed for the task and explain any expansion.");
|
|
8440
8538
|
}
|
|
8539
|
+
if (repoHintDiagnostics.length > 0) {
|
|
8540
|
+
lines.push("Repo hint preflight:");
|
|
8541
|
+
for (const diagnostic of repoHintDiagnostics.slice(0, 8)) {
|
|
8542
|
+
lines.push(`- ${diagnostic}`);
|
|
8543
|
+
}
|
|
8544
|
+
lines.push("- If a hinted path is absent, treat it as stale guidance unless the user explicitly asked to create that path. Prefer an existing repo-native owner or nearby test.");
|
|
8545
|
+
}
|
|
8441
8546
|
lines.push("Scope:");
|
|
8442
8547
|
lines.push(`- read_anywhere: ${plan.scope.read_anywhere ? "true" : "false"}`);
|
|
8443
8548
|
lines.push(`- write_allowed: ${plan.scope.write_allowed ? "true" : "false"}`);
|
|
@@ -8490,6 +8595,75 @@ function buildExecutionGuidance(plan, targetPaths, requiredValidationSteps = [])
|
|
|
8490
8595
|
return lines.join(`
|
|
8491
8596
|
`).trim();
|
|
8492
8597
|
}
|
|
8598
|
+
function pathHintHasGlob(value) {
|
|
8599
|
+
return /[*?[\]{}]/.test(value);
|
|
8600
|
+
}
|
|
8601
|
+
function pathHintLooksLikeConcreteFile(value) {
|
|
8602
|
+
const normalized = value.replace(/\\/g, "/").replace(/^\.\/+/, "");
|
|
8603
|
+
const tail = normalized.split("/").pop() ?? normalized;
|
|
8604
|
+
return /\.[A-Za-z0-9][A-Za-z0-9_-]{0,12}$/.test(tail);
|
|
8605
|
+
}
|
|
8606
|
+
function requestAllowsCreatingMissingPath(value) {
|
|
8607
|
+
return /\b(create|add|new|scaffold|generate|introduce|write)\b.{0,80}\b(file|test|module|component|script|page|route|fixture|helper)\b/i.test(value);
|
|
8608
|
+
}
|
|
8609
|
+
function shouldTreatMissingTargetAsStale(repoRoot, path, requestText) {
|
|
8610
|
+
const normalized = normalizeTargetPath(path);
|
|
8611
|
+
if (!normalized || normalized === "." || pathHintHasGlob(normalized))
|
|
8612
|
+
return false;
|
|
8613
|
+
if (!pathHintLooksLikeConcreteFile(normalized))
|
|
8614
|
+
return false;
|
|
8615
|
+
if (existsSync5(resolve5(repoRoot, normalized)))
|
|
8616
|
+
return false;
|
|
8617
|
+
if (requestAllowsCreatingMissingPath(requestText))
|
|
8618
|
+
return false;
|
|
8619
|
+
return true;
|
|
8620
|
+
}
|
|
8621
|
+
function sanitizeRepoNativeTargetHints(params) {
|
|
8622
|
+
const requestText = [
|
|
8623
|
+
params.prompt,
|
|
8624
|
+
params.plan.worker_instruction,
|
|
8625
|
+
params.plan.assistant_message,
|
|
8626
|
+
...params.plan.acceptance_criteria,
|
|
8627
|
+
...params.targetPaths
|
|
8628
|
+
].join(`
|
|
8629
|
+
`);
|
|
8630
|
+
const diagnostics = [];
|
|
8631
|
+
const staleHints = [];
|
|
8632
|
+
const targetPaths = params.targetPaths.filter((path) => {
|
|
8633
|
+
const normalized = normalizeTargetPath(path);
|
|
8634
|
+
if (!normalized)
|
|
8635
|
+
return false;
|
|
8636
|
+
if (!shouldTreatMissingTargetAsStale(params.repoRoot, normalized, requestText))
|
|
8637
|
+
return true;
|
|
8638
|
+
staleHints.push(normalized);
|
|
8639
|
+
diagnostics.push(`Path hint "${normalized}" does not exist in this checkout; it was removed as a canonical target and kept only as advisory context.`);
|
|
8640
|
+
return false;
|
|
8641
|
+
});
|
|
8642
|
+
if (staleHints.length > 0) {
|
|
8643
|
+
const staleLower = staleHints.map((path) => path.toLowerCase());
|
|
8644
|
+
params.plan.validation_steps = params.plan.validation_steps.filter((step) => {
|
|
8645
|
+
const lower = step.replace(/\\/g, "/").toLowerCase();
|
|
8646
|
+
return !staleLower.some((path) => lower.includes(path));
|
|
8647
|
+
});
|
|
8648
|
+
params.plan.scope.write_globs = params.plan.scope.write_globs.filter((glob) => {
|
|
8649
|
+
const normalized = normalizeTargetPath(glob);
|
|
8650
|
+
if (!normalized)
|
|
8651
|
+
return false;
|
|
8652
|
+
return !staleLower.includes(normalized.toLowerCase());
|
|
8653
|
+
});
|
|
8654
|
+
if (!params.plan.discovery) {
|
|
8655
|
+
params.plan.discovery = { ripgrep_queries: [] };
|
|
8656
|
+
}
|
|
8657
|
+
const keywords = new Set([...params.plan.discovery.keywords ?? []]);
|
|
8658
|
+
for (const path of staleHints) {
|
|
8659
|
+
const tail = path.split("/").pop();
|
|
8660
|
+
if (tail)
|
|
8661
|
+
keywords.add(tail.replace(/\.[^.]+$/, ""));
|
|
8662
|
+
}
|
|
8663
|
+
params.plan.discovery.keywords = [...keywords].slice(0, 12);
|
|
8664
|
+
}
|
|
8665
|
+
return { targetPaths, diagnostics, staleHints };
|
|
8666
|
+
}
|
|
8493
8667
|
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;
|
|
8494
8668
|
var VALIDATION_GENERIC_SAFE = /^(git\s+status\s+--porcelain|git\s+diff\b)/i;
|
|
8495
8669
|
var PATH_TOKEN_REGEX = /\b([A-Za-z0-9._/\-\\]+\.[A-Za-z0-9._-]+)\b/g;
|
|
@@ -8872,7 +9046,7 @@ class RemoteBuddyOrchestrator {
|
|
|
8872
9046
|
console.log(`[RemoteBuddy] Budgets: interactive=${this.executionBudgetInteractiveMs}ms normal=${this.executionBudgetNormalMs}ms background=${this.executionBudgetBackgroundMs}ms finalization=${this.finalizationBudgetMs}ms`);
|
|
8873
9047
|
console.log(`[RemoteBuddy] Failure log fetch on job failures: ${this.fetchFailureLogsOnJobFailure ? "on" : "off"}`);
|
|
8874
9048
|
console.log(`[RemoteBuddy] Persistent memory: ${this.memoryEnabled ? "on" : "off"} crossSession=${this.memoryIncludeCrossSession ? "on" : "off"} recallItems=${this.memoryMaxRecallItems} recallChars=${this.memoryMaxRecallChars} retentionDays=${this.memoryRetentionDays}`);
|
|
8875
|
-
console.log(`[RemoteBuddy] Autonomous engine: ${CONFIG.remotebuddy.autonomy.enabled ? "enabled" : "disabled"} tick=${CONFIG.remotebuddy.autonomy.tickIntervalMs}ms maxConcurrentObjectives=${CONFIG.remotebuddy.autonomy.maxConcurrentObjectives} maxDispatchPerHour=${CONFIG.remotebuddy.autonomy.maxDispatchPerHour} exploreRate=${CONFIG.remotebuddy.autonomy.exploreRate.toFixed(2)} allowDirtyWorktree=${CONFIG.remotebuddy.autonomy.allowDirtyWorktree ? "on" : "off"}`);
|
|
9049
|
+
console.log(`[RemoteBuddy] Autonomous engine: ${CONFIG.remotebuddy.autonomy.enabled ? "enabled" : "disabled"} tick=${CONFIG.remotebuddy.autonomy.tickIntervalMs}ms startupGrace=${CONFIG.remotebuddy.autonomy.startupGraceMs}ms maxConcurrentObjectives=${CONFIG.remotebuddy.autonomy.maxConcurrentObjectives} maxDispatchPerHour=${CONFIG.remotebuddy.autonomy.maxDispatchPerHour} exploreRate=${CONFIG.remotebuddy.autonomy.exploreRate.toFixed(2)} allowDirtyWorktree=${CONFIG.remotebuddy.autonomy.allowDirtyWorktree ? "on" : "off"}`);
|
|
8876
9050
|
console.log(`[RemoteBuddy] Autonomy runtime-config polling: every ${this.autonomyConfigPollMs}ms`);
|
|
8877
9051
|
}
|
|
8878
9052
|
async emitStartupStatus() {
|
|
@@ -9923,7 +10097,17 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
9923
10097
|
}
|
|
9924
10098
|
this.pushContext(`[user] ${toSingleLine(prompt, 700)}`, requestSessionId);
|
|
9925
10099
|
this.pushContext(`[plan] ${toSingleLine(JSON.stringify(plan), 900)}`, requestSessionId);
|
|
9926
|
-
|
|
10100
|
+
let targetPaths = autonomyMetadata && autonomyMetadata.targetPaths.length > 0 ? autonomyMetadata.targetPaths : plannerTargetPaths(plan, prompt);
|
|
10101
|
+
const repoHintPreflight = sanitizeRepoNativeTargetHints({
|
|
10102
|
+
repoRoot: this.repo,
|
|
10103
|
+
prompt,
|
|
10104
|
+
plan,
|
|
10105
|
+
targetPaths
|
|
10106
|
+
});
|
|
10107
|
+
targetPaths = repoHintPreflight.targetPaths;
|
|
10108
|
+
if (repoHintPreflight.diagnostics.length > 0) {
|
|
10109
|
+
console.warn(`[RemoteBuddy] Repo hint preflight: ${repoHintPreflight.diagnostics.slice(0, 3).join(" | ")}`);
|
|
10110
|
+
}
|
|
9927
10111
|
this.rememberPersistentMemory("plan", `intent=${plan.intent} worker=${plan.requires_worker ? "yes" : "no"} lane=${plan.lane} risk=${plan.risk_level} targets=${targetPaths.slice(0, 6).join(",") || "(none)"}`, requestId, requestSessionId);
|
|
9928
10112
|
const targetPath = targetPaths[0];
|
|
9929
10113
|
const isAnalysisFromEngine = plan.intent === "analysis" && Boolean(autonomyMetadata);
|
|
@@ -9961,8 +10145,9 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
9961
10145
|
}
|
|
9962
10146
|
if (!forceWorker) {
|
|
9963
10147
|
const missing = [];
|
|
9964
|
-
if (targetPaths.length === 0)
|
|
10148
|
+
if (targetPaths.length === 0 && repoHintPreflight.diagnostics.length === 0) {
|
|
9965
10149
|
missing.push("target_paths");
|
|
10150
|
+
}
|
|
9966
10151
|
if (plan.acceptance_criteria.length === 0)
|
|
9967
10152
|
missing.push("acceptance_criteria");
|
|
9968
10153
|
if (plan.validation_steps.length === 0)
|
|
@@ -9981,7 +10166,7 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
9981
10166
|
}
|
|
9982
10167
|
const canonicalInstruction = prompt.trim();
|
|
9983
10168
|
const rawPlannerInstruction = sanitizePlannerWorkerInstruction(String(plan.worker_instruction ?? ""), canonicalInstruction);
|
|
9984
|
-
const executionGuidance = buildExecutionGuidance(plan, targetPaths, requiredValidationSteps);
|
|
10169
|
+
const executionGuidance = buildExecutionGuidance(plan, targetPaths, requiredValidationSteps, repoHintPreflight.diagnostics);
|
|
9985
10170
|
const plannerWorkerInstruction = [rawPlannerInstruction, executionGuidance].filter(Boolean).join(`
|
|
9986
10171
|
|
|
9987
10172
|
`).trim();
|
|
@@ -10087,6 +10272,7 @@ Please reply with the missing details and I will enqueue a follow-up request.` :
|
|
|
10087
10272
|
} : {},
|
|
10088
10273
|
acceptanceCriteria: plan.acceptance_criteria,
|
|
10089
10274
|
validationSteps: plan.validation_steps,
|
|
10275
|
+
...repoHintPreflight.diagnostics.length > 0 ? { repoHintDiagnostics: repoHintPreflight.diagnostics } : {},
|
|
10090
10276
|
...requiredValidationSteps.length > 0 ? { requiredValidationSteps } : {},
|
|
10091
10277
|
queuePriority: priority,
|
|
10092
10278
|
queueWaitBudgetMs,
|