@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.
@@ -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 WORKERPAL_STARTUP_READINESS_PROBE_MAX_MS = 15000;
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(5000, Math.min(resolveWorkerpalCapacityTimeoutMs(config), WORKERPAL_STARTUP_READINESS_PROBE_MAX_MS));
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushpalsdev/cli",
3
- "version": "1.1.18",
3
+ "version": "1.1.20",
4
4
  "description": "PushPals terminal CLI for LocalBuddy -> RemoteBuddy orchestration",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -75,6 +75,7 @@ session_id = "remotebuddy-dev"
75
75
  enabled = true
76
76
  kill_switch_enabled = false
77
77
  tick_interval_ms = 300000
78
+ startup_grace_ms = 120000
78
79
  heartbeat_log_ms = 30000
79
80
  vision_context_max_chars = 65536
80
81
  ideation_budget_ms = 20000
@@ -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 nextTickInMs = this.timer && this.nextTickAtMs > 0 ? Math.max(0, this.nextTickAtMs - now) : 0;
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
- const instruction = canonicalizeInstructionTextForBun(asString2(planningJson.instruction) || `${selected.candidate.title}
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
- this.nextTickAtMs = Date.now();
8102
- this.timer = setInterval(() => {
8103
- this.nextTickAtMs = Date.now() + this.cfg.tickIntervalMs;
8104
- this.tick();
8105
- }, this.cfg.tickIntervalMs);
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
- const targetPaths = autonomyMetadata && autonomyMetadata.targetPaths.length > 0 ? autonomyMetadata.targetPaths : plannerTargetPaths(plan, prompt);
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,