@pushpalsdev/cli 1.0.93 → 1.0.95
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 +120 -15
- package/package.json +1 -1
- package/runtime/prompts/remotebuddy/remotebuddy_system_prompt.md +2 -2
- package/runtime/sandbox/.pushpals-remotebuddy-fallback.js +207 -53
- package/runtime/sandbox/apps/workerpals/src/backends/openai_codex/openai_codex_executor.py +219 -130
- package/runtime/sandbox/apps/workerpals/src/backends/openai_codex/test_openai_codex_runtime_config.py +57 -0
- package/runtime/sandbox/apps/workerpals/src/execute_job.ts +138 -105
package/dist/pushpals-cli.js
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
mkdirSync,
|
|
12
12
|
readdirSync,
|
|
13
13
|
readFileSync as readFileSync4,
|
|
14
|
+
renameSync,
|
|
14
15
|
rmSync as rmSync2,
|
|
15
16
|
writeFileSync
|
|
16
17
|
} from "fs";
|
|
@@ -1978,8 +1979,29 @@ async function runCommandWithEnv(command, cwd, env, timeoutMs) {
|
|
|
1978
1979
|
};
|
|
1979
1980
|
}
|
|
1980
1981
|
}
|
|
1982
|
+
function appendGitConfigEnv(env, key, value) {
|
|
1983
|
+
const existingCount = Number.parseInt(String(env.GIT_CONFIG_COUNT ?? "0").trim(), 10);
|
|
1984
|
+
const count = Number.isFinite(existingCount) && existingCount >= 0 ? existingCount : 0;
|
|
1985
|
+
for (let index = 0;index < count; index++) {
|
|
1986
|
+
if (String(env[`GIT_CONFIG_KEY_${index}`] ?? "") === key)
|
|
1987
|
+
return env;
|
|
1988
|
+
}
|
|
1989
|
+
return {
|
|
1990
|
+
...env,
|
|
1991
|
+
GIT_CONFIG_COUNT: String(count + 1),
|
|
1992
|
+
[`GIT_CONFIG_KEY_${count}`]: key,
|
|
1993
|
+
[`GIT_CONFIG_VALUE_${count}`]: value
|
|
1994
|
+
};
|
|
1995
|
+
}
|
|
1996
|
+
function withWindowsGitSchannelEnv(env, platform = process.platform) {
|
|
1997
|
+
if (platform !== "win32")
|
|
1998
|
+
return env;
|
|
1999
|
+
if (parseBooleanFlag(env.PUSHPALS_DISABLE_WINDOWS_GIT_SCHANNEL) === true)
|
|
2000
|
+
return env;
|
|
2001
|
+
return appendGitConfigEnv(env, "http.sslBackend", "schannel");
|
|
2002
|
+
}
|
|
1981
2003
|
async function runGitWithEnv(args, cwd, env) {
|
|
1982
|
-
return await runCommandWithEnv(["git", ...args], cwd, env);
|
|
2004
|
+
return await runCommandWithEnv(["git", ...args], cwd, withWindowsGitSchannelEnv(env));
|
|
1983
2005
|
}
|
|
1984
2006
|
async function runGit(args, cwd) {
|
|
1985
2007
|
return await runGitWithEnv(args, cwd, {
|
|
@@ -2264,15 +2286,59 @@ function resolveRuntimePlatformKey() {
|
|
|
2264
2286
|
throw new Error(`Unsupported platform for embedded runtime binaries: ${process.platform}/${process.arch}`);
|
|
2265
2287
|
}
|
|
2266
2288
|
async function fetchLatestReleaseTag() {
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2289
|
+
try {
|
|
2290
|
+
const response = await fetchWithTimeout(`${GITHUB_API_URL}/releases/latest`, { headers: GITHUB_HEADERS }, 20000);
|
|
2291
|
+
if (!response.ok) {
|
|
2292
|
+
throw new Error(`Failed to resolve latest release tag (HTTP ${response.status})`);
|
|
2293
|
+
}
|
|
2294
|
+
const payload = await response.json();
|
|
2295
|
+
const tagName = String(payload.tag_name ?? "").trim();
|
|
2296
|
+
if (!tagName)
|
|
2297
|
+
throw new Error("Latest release payload did not include tag_name");
|
|
2298
|
+
return tagName;
|
|
2299
|
+
} catch (err) {
|
|
2300
|
+
const fallback = await fetchLatestReleaseTagWithGitFallback(err);
|
|
2301
|
+
if (fallback)
|
|
2302
|
+
return fallback;
|
|
2303
|
+
throw err;
|
|
2270
2304
|
}
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2305
|
+
}
|
|
2306
|
+
async function fetchLatestReleaseTagWithGitFallback(cause) {
|
|
2307
|
+
const message = String(cause instanceof Error ? cause.message : cause ?? "");
|
|
2308
|
+
if (process.platform !== "win32" || !/certificate|cert_|unable to verify|self[- ]signed|tls|ssl/i.test(message) && !/fetch failed/i.test(message)) {
|
|
2309
|
+
return null;
|
|
2310
|
+
}
|
|
2311
|
+
console.warn("[pushpals] Bun could not verify the GitHub API certificate; resolving latest release tag with Git instead.");
|
|
2312
|
+
const result = await runGitWithEnv([
|
|
2313
|
+
"ls-remote",
|
|
2314
|
+
"--tags",
|
|
2315
|
+
"--refs",
|
|
2316
|
+
`https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}.git`,
|
|
2317
|
+
"refs/tags/v*"
|
|
2318
|
+
], process.cwd(), {
|
|
2319
|
+
...process.env,
|
|
2320
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
2321
|
+
GCM_INTERACTIVE: "Never"
|
|
2322
|
+
});
|
|
2323
|
+
if (!result.ok)
|
|
2324
|
+
return null;
|
|
2325
|
+
const tags = result.stdout.split(/\r?\n/g).map((line) => line.trim().match(/refs\/tags\/(v\d+\.\d+\.\d+(?:[-.][0-9A-Za-z.-]+)?)$/)?.[1]).filter((tag) => Boolean(tag));
|
|
2326
|
+
return sortReleaseTagsDescending(tags)[0] ?? null;
|
|
2327
|
+
}
|
|
2328
|
+
function sortReleaseTagsDescending(tags) {
|
|
2329
|
+
return [...new Set(tags)].sort((a, b) => compareReleaseTags(b, a));
|
|
2330
|
+
}
|
|
2331
|
+
function compareReleaseTags(a, b) {
|
|
2332
|
+
const parse = (value) => String(value).replace(/^v/i, "").split(/[.-]/g).map((part) => Number.parseInt(part, 10)).map((part) => Number.isFinite(part) ? part : 0);
|
|
2333
|
+
const left = parse(a);
|
|
2334
|
+
const right = parse(b);
|
|
2335
|
+
const max = Math.max(left.length, right.length, 3);
|
|
2336
|
+
for (let index = 0;index < max; index++) {
|
|
2337
|
+
const delta = (left[index] ?? 0) - (right[index] ?? 0);
|
|
2338
|
+
if (delta !== 0)
|
|
2339
|
+
return delta;
|
|
2340
|
+
}
|
|
2341
|
+
return a.localeCompare(b);
|
|
2276
2342
|
}
|
|
2277
2343
|
function resolvePreferredRuntimeReleaseTag(explicitTag, env = process.env) {
|
|
2278
2344
|
const fromArg = String(explicitTag ?? "").trim();
|
|
@@ -2588,7 +2654,7 @@ function buildEmbeddedRuntimeEnv(baseEnv, opts) {
|
|
|
2588
2654
|
const existing = env[key];
|
|
2589
2655
|
return typeof existing !== "string" || existing.trim().length === 0;
|
|
2590
2656
|
})) : {};
|
|
2591
|
-
|
|
2657
|
+
const runtimeEnv = {
|
|
2592
2658
|
...inherited,
|
|
2593
2659
|
PUSHPALS_REPO_ROOT_OVERRIDE: opts.repoRoot,
|
|
2594
2660
|
PUSHPALS_PROJECT_ROOT_OVERRIDE: opts.repoRoot,
|
|
@@ -2609,6 +2675,7 @@ function buildEmbeddedRuntimeEnv(baseEnv, opts) {
|
|
|
2609
2675
|
...typeof env.PUSHPALS_DOCKER_BIN === "string" && env.PUSHPALS_DOCKER_BIN.trim() ? { PUSHPALS_DOCKER_BIN: env.PUSHPALS_DOCKER_BIN.trim() } : {},
|
|
2610
2676
|
...typeof env.PUSHPALS_DOCKER_BIN_ABSOLUTE === "string" && env.PUSHPALS_DOCKER_BIN_ABSOLUTE.trim() ? { PUSHPALS_DOCKER_BIN_ABSOLUTE: env.PUSHPALS_DOCKER_BIN_ABSOLUTE.trim() } : {}
|
|
2611
2677
|
};
|
|
2678
|
+
return withWindowsGitSchannelEnv(runtimeEnv, platform);
|
|
2612
2679
|
}
|
|
2613
2680
|
function parseBooleanFlag(raw) {
|
|
2614
2681
|
const normalized = String(raw ?? "").trim().toLowerCase();
|
|
@@ -2752,13 +2819,51 @@ function readRemoteBuddyAutonomousEngineState(logPath) {
|
|
|
2752
2819
|
async function downloadBinaryAsset(tag, assetName, outPath) {
|
|
2753
2820
|
console.log(`[pushpals] Downloading embedded runtime binary ${assetName} from ${tag}...`);
|
|
2754
2821
|
const url = `${GITHUB_RELEASE_URL}/${encodeURIComponent(tag)}/${assetName}`;
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2822
|
+
try {
|
|
2823
|
+
const response = await fetchWithTimeout(url, { headers: GITHUB_HEADERS }, 60000);
|
|
2824
|
+
if (!response.ok) {
|
|
2825
|
+
throw new Error(`Failed to download ${assetName} from ${tag} (HTTP ${response.status})`);
|
|
2826
|
+
}
|
|
2827
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
2828
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
2829
|
+
await Bun.write(outPath, bytes);
|
|
2830
|
+
return;
|
|
2831
|
+
} catch (err) {
|
|
2832
|
+
const fallback = await downloadBinaryAssetWithWindowsCurlFallback(url, outPath, err);
|
|
2833
|
+
if (fallback)
|
|
2834
|
+
return;
|
|
2835
|
+
throw err;
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
async function downloadBinaryAssetWithWindowsCurlFallback(url, outPath, cause) {
|
|
2839
|
+
if (process.platform !== "win32")
|
|
2840
|
+
return false;
|
|
2841
|
+
const message = String(cause instanceof Error ? cause.message : cause ?? "");
|
|
2842
|
+
if (!/certificate|cert_|unable to verify|self[- ]signed|tls|ssl/i.test(message) && !/fetch failed/i.test(message)) {
|
|
2843
|
+
return false;
|
|
2758
2844
|
}
|
|
2759
|
-
const
|
|
2845
|
+
const tmpPath = `${outPath}.download-${process.pid}-${Date.now()}.tmp`;
|
|
2760
2846
|
mkdirSync(dirname(outPath), { recursive: true });
|
|
2761
|
-
|
|
2847
|
+
rmSync2(tmpPath, { force: true });
|
|
2848
|
+
console.warn("[pushpals] Bun could not verify the GitHub release certificate; retrying download with Windows curl certificate handling.");
|
|
2849
|
+
const result = await runCommandWithEnv([
|
|
2850
|
+
"curl.exe",
|
|
2851
|
+
"--fail",
|
|
2852
|
+
"--location",
|
|
2853
|
+
"--silent",
|
|
2854
|
+
"--show-error",
|
|
2855
|
+
"--ssl-no-revoke",
|
|
2856
|
+
"--output",
|
|
2857
|
+
tmpPath,
|
|
2858
|
+
url
|
|
2859
|
+
], process.cwd(), process.env, 120000);
|
|
2860
|
+
if (!result.ok || !existsSync5(tmpPath)) {
|
|
2861
|
+
rmSync2(tmpPath, { force: true });
|
|
2862
|
+
const detail = result.stderr || result.stdout || `exit ${result.exitCode}`;
|
|
2863
|
+
throw new Error(`Windows curl fallback failed while downloading runtime binary: ${detail}`);
|
|
2864
|
+
}
|
|
2865
|
+
renameSync(tmpPath, outPath);
|
|
2866
|
+
return true;
|
|
2762
2867
|
}
|
|
2763
2868
|
async function ensureRuntimeBinaries(runtimeRoot, runtimeTag) {
|
|
2764
2869
|
const platformKey = resolveRuntimePlatformKey();
|
package/package.json
CHANGED
|
@@ -45,8 +45,8 @@ Execution policy:
|
|
|
45
45
|
- `scope.read_anywhere` should default to `true` (do not set `false` unless user explicitly requested restrictive reading)
|
|
46
46
|
- `scope.write_allowed` should default to `true`
|
|
47
47
|
- `scope.write_globs` should be included as starting-point/relevance hints, not as hard write boundaries
|
|
48
|
-
- `scope.forbidden_globs` should be included only
|
|
49
|
-
- `scope.max_files_to_edit` should be included only
|
|
48
|
+
- `scope.forbidden_globs` should be included only as review guardrail hints, not as hard write blockers
|
|
49
|
+
- `scope.max_files_to_edit` should be included only as a planning/review hint; WorkerPal write access is repo-sandbox-wide
|
|
50
50
|
|
|
51
51
|
Quality gates:
|
|
52
52
|
|
|
@@ -4264,6 +4264,9 @@ var IDEATION_SYSTEM_PROMPT = loadPromptTemplate("remotebuddy/autonomy_ideation_s
|
|
|
4264
4264
|
var SCORING_SYSTEM_PROMPT = loadPromptTemplate("remotebuddy/autonomy_scoring_system_prompt.md").trim();
|
|
4265
4265
|
var PLANNING_SYSTEM_PROMPT = loadPromptTemplate("remotebuddy/autonomy_planning_system_prompt.md").trim();
|
|
4266
4266
|
var IDEATION_TIMEOUT_RECOVERY_INSTRUCTION = "Previous ideation timed out before you returned JSON. For this round only, stay within the time budget: prioritize the top 1-3 highest-confidence candidates, keep reasoning brief, avoid exhaustive exploration, and return valid JSON as soon as possible.";
|
|
4267
|
+
var STARTUP_FAST_TICK_MAX_ATTEMPTS = 4;
|
|
4268
|
+
var STARTUP_FAST_TICK_MAX_DELAY_MS = 15000;
|
|
4269
|
+
var STARTUP_STALE_LOCK_AFTER_MS = 30000;
|
|
4267
4270
|
var VISION_DOC_FNAME = "vision.md";
|
|
4268
4271
|
var MAX_VISION_SECTION_CHARS = 1200;
|
|
4269
4272
|
var DOCS_MIN_IMPACT_SIGNAL_FOR_NO_PENALTY = 0.45;
|
|
@@ -6100,6 +6103,92 @@ function buildEngineInspirationContext(params) {
|
|
|
6100
6103
|
commit_history_hints: commitHistoryHints
|
|
6101
6104
|
};
|
|
6102
6105
|
}
|
|
6106
|
+
function compactIdeationText(value, maxChars) {
|
|
6107
|
+
const text = asString2(value).trim();
|
|
6108
|
+
if (text.length <= maxChars)
|
|
6109
|
+
return text;
|
|
6110
|
+
return `${text.slice(0, Math.max(0, maxChars - 1)).trimEnd()}...`;
|
|
6111
|
+
}
|
|
6112
|
+
function compactIdeationTextList(values, maxItems, maxChars) {
|
|
6113
|
+
return values.slice(0, maxItems).map((value) => compactIdeationText(value, maxChars)).filter(Boolean);
|
|
6114
|
+
}
|
|
6115
|
+
function compactVisionContextForIdeationRetry(vision) {
|
|
6116
|
+
const compactKeyItems = Object.fromEntries(Object.entries(vision.key_items).map(([key, value]) => [
|
|
6117
|
+
key,
|
|
6118
|
+
Array.isArray(value) ? compactIdeationTextList(value, 6, 260) : compactIdeationText(value, 260)
|
|
6119
|
+
]));
|
|
6120
|
+
return {
|
|
6121
|
+
one_sentence: compactIdeationText(vision.one_sentence, 360),
|
|
6122
|
+
sections: vision.sections.slice(0, 4).map((section) => ({
|
|
6123
|
+
number: section.number,
|
|
6124
|
+
title: compactIdeationText(section.title, 160),
|
|
6125
|
+
markdown: compactIdeationText(section.markdown, 500),
|
|
6126
|
+
truncated: section.truncated || section.markdown.length > 500
|
|
6127
|
+
})),
|
|
6128
|
+
key_items: compactKeyItems,
|
|
6129
|
+
section_numbers: vision.section_numbers.slice(0, 8),
|
|
6130
|
+
truncated: vision.truncated
|
|
6131
|
+
};
|
|
6132
|
+
}
|
|
6133
|
+
function compactEngineInspirationForIdeationRetry(context) {
|
|
6134
|
+
return {
|
|
6135
|
+
compiled_repo_objectives: context.compiled_repo_objectives.slice(0, 4).map((objective) => ({
|
|
6136
|
+
id: objective.id,
|
|
6137
|
+
title: objective.title,
|
|
6138
|
+
weight: objective.weight,
|
|
6139
|
+
section_ref: objective.section_ref,
|
|
6140
|
+
category: objective.category,
|
|
6141
|
+
success_criteria: compactIdeationTextList(objective.success_criteria, 3, 220),
|
|
6142
|
+
validation_expectations: compactIdeationTextList(objective.validation_expectations, 3, 220)
|
|
6143
|
+
})),
|
|
6144
|
+
compiled_objectives: context.compiled_objectives.slice(0, 4).map((objective) => ({
|
|
6145
|
+
id: objective.id,
|
|
6146
|
+
title: compactIdeationText(objective.title, 220),
|
|
6147
|
+
weight: objective.weight,
|
|
6148
|
+
evidence: compactIdeationTextList(objective.evidence, 3, 220)
|
|
6149
|
+
})),
|
|
6150
|
+
opportunity_gaps: context.opportunity_gaps.slice(0, 4).map((gap) => ({
|
|
6151
|
+
id: gap.id,
|
|
6152
|
+
label: compactIdeationText(gap.label, 220),
|
|
6153
|
+
score: gap.score,
|
|
6154
|
+
evidence: compactIdeationTextList(gap.evidence, 3, 220)
|
|
6155
|
+
})),
|
|
6156
|
+
building_blocks: context.building_blocks.slice(0, 6).map((block) => ({
|
|
6157
|
+
id: block.id,
|
|
6158
|
+
algorithm: block.algorithm,
|
|
6159
|
+
summary: compactIdeationText(block.summary, 260),
|
|
6160
|
+
hypothesis: compactIdeationText(block.hypothesis, 260),
|
|
6161
|
+
score: block.score,
|
|
6162
|
+
objective_ids: block.objective_ids.slice(0, 3),
|
|
6163
|
+
gap_ids: block.gap_ids.slice(0, 3),
|
|
6164
|
+
candidate_shape: {
|
|
6165
|
+
objective_type: block.candidate_shape.objective_type,
|
|
6166
|
+
trigger_type: block.candidate_shape.trigger_type,
|
|
6167
|
+
component_area: block.candidate_shape.component_area,
|
|
6168
|
+
target_paths: block.candidate_shape.target_paths.slice(0, 4),
|
|
6169
|
+
write_globs: block.candidate_shape.write_globs.slice(0, 4)
|
|
6170
|
+
}
|
|
6171
|
+
})),
|
|
6172
|
+
source_patterns: context.source_patterns.slice(0, 4).map((pattern) => ({
|
|
6173
|
+
id: pattern.id,
|
|
6174
|
+
algorithm: pattern.algorithm,
|
|
6175
|
+
summary: compactIdeationText(pattern.summary, 260),
|
|
6176
|
+
tags: compactIdeationTextList(pattern.tags, 5, 80),
|
|
6177
|
+
quality_score: pattern.quality_score,
|
|
6178
|
+
freshness_score: pattern.freshness_score,
|
|
6179
|
+
source_trust_score: pattern.source_trust_score
|
|
6180
|
+
})),
|
|
6181
|
+
commit_history_hints: context.commit_history_hints.slice(0, 4).map((hint) => ({
|
|
6182
|
+
motif_id: hint.motif_id,
|
|
6183
|
+
label: compactIdeationText(hint.label, 220),
|
|
6184
|
+
count: hint.count,
|
|
6185
|
+
signal: hint.signal,
|
|
6186
|
+
objective_ids: hint.objective_ids.slice(0, 3),
|
|
6187
|
+
gap_ids: hint.gap_ids.slice(0, 3),
|
|
6188
|
+
sample_subjects: compactIdeationTextList(hint.sample_subjects, 3, 180)
|
|
6189
|
+
}))
|
|
6190
|
+
};
|
|
6191
|
+
}
|
|
6103
6192
|
function selectVisionSectionRefs(sectionRefs) {
|
|
6104
6193
|
const preferred = ["6", "7", "8", "4", "3", "0", "5"];
|
|
6105
6194
|
const normalized = sectionRefs.map((value) => asString2(value)).filter(Boolean);
|
|
@@ -6199,7 +6288,7 @@ function buildRepoVisionFallbackCandidates(params) {
|
|
|
6199
6288
|
const target = chooseRepoObjectiveTargetProfile(params.repoTargets ?? [], objective);
|
|
6200
6289
|
const targetPaths = target?.target_paths ?? [objective.section_ref ? `vision.md` : "README.md"];
|
|
6201
6290
|
const writeGlobs = target?.write_globs ?? targetPaths;
|
|
6202
|
-
const componentArea = target?.component_area ??
|
|
6291
|
+
const componentArea = target?.component_area ?? normalizeAutonomyComponentArea(pathDirname(targetPaths[0]) || targetPaths[0]) ?? "docs";
|
|
6203
6292
|
const triggerType = categoryTriggerType(objective.category, params.snapshotTopSignals);
|
|
6204
6293
|
const signalIds = pickSignalIdsForTrigger(params.snapshotTopSignals, triggerType);
|
|
6205
6294
|
const sectionRef = objective.section_ref || sectionRefs[0] || "";
|
|
@@ -6409,9 +6498,11 @@ class RemoteBuddyAutonomousEngine {
|
|
|
6409
6498
|
cfg;
|
|
6410
6499
|
runtimeEnabled = true;
|
|
6411
6500
|
timer = null;
|
|
6501
|
+
startupFastTickTimer = null;
|
|
6412
6502
|
heartbeatTimer = null;
|
|
6413
6503
|
inFlight = false;
|
|
6414
6504
|
nextTickAtMs = 0;
|
|
6505
|
+
startupFastTickAttemptsRemaining = 0;
|
|
6415
6506
|
currentRunId = null;
|
|
6416
6507
|
currentPhase = "idle";
|
|
6417
6508
|
currentPhaseStartedAtMs = 0;
|
|
@@ -6441,6 +6532,8 @@ class RemoteBuddyAutonomousEngine {
|
|
|
6441
6532
|
this.runtimeEnabled = Boolean(enabled);
|
|
6442
6533
|
if (!this.runtimeEnabled) {
|
|
6443
6534
|
this.nextTickAtMs = 0;
|
|
6535
|
+
this.startupFastTickAttemptsRemaining = 0;
|
|
6536
|
+
this.clearStartupFastTickTimer();
|
|
6444
6537
|
if (!this.currentRunId) {
|
|
6445
6538
|
this.lastOutcome = "skipped";
|
|
6446
6539
|
this.lastDetail = "disabled_by_runtime_config";
|
|
@@ -6494,6 +6587,38 @@ class RemoteBuddyAutonomousEngine {
|
|
|
6494
6587
|
lockStaleAfterMs() {
|
|
6495
6588
|
return Math.max(this.phaseTimeoutMs("ideation") + 30000, this.cfg.heartbeatLogMs * 2, 120000);
|
|
6496
6589
|
}
|
|
6590
|
+
startupLockStaleAfterMs() {
|
|
6591
|
+
return Math.min(this.lockStaleAfterMs(), Math.max(5000, Math.min(STARTUP_STALE_LOCK_AFTER_MS, Math.floor(this.cfg.tickIntervalMs / 4))));
|
|
6592
|
+
}
|
|
6593
|
+
lockStaleAfterMsForAcquire() {
|
|
6594
|
+
return this.startupFastTickAttemptsRemaining > 0 ? this.startupLockStaleAfterMs() : this.lockStaleAfterMs();
|
|
6595
|
+
}
|
|
6596
|
+
startupFastTickDelayMs() {
|
|
6597
|
+
return Math.max(1000, Math.min(STARTUP_FAST_TICK_MAX_DELAY_MS, Math.floor(this.cfg.tickIntervalMs / 10)));
|
|
6598
|
+
}
|
|
6599
|
+
clearStartupFastTickTimer() {
|
|
6600
|
+
if (this.startupFastTickTimer) {
|
|
6601
|
+
clearTimeout(this.startupFastTickTimer);
|
|
6602
|
+
this.startupFastTickTimer = null;
|
|
6603
|
+
}
|
|
6604
|
+
}
|
|
6605
|
+
scheduleStartupFastTick(reason) {
|
|
6606
|
+
if (!this.runtimeEnabled || !this.timer || this.startupFastTickTimer)
|
|
6607
|
+
return;
|
|
6608
|
+
if (this.startupFastTickAttemptsRemaining <= 0)
|
|
6609
|
+
return;
|
|
6610
|
+
const delayMs = this.startupFastTickDelayMs();
|
|
6611
|
+
this.startupFastTickAttemptsRemaining -= 1;
|
|
6612
|
+
this.nextTickAtMs = Date.now() + delayMs;
|
|
6613
|
+
console.log(`[RemoteBuddyAutonomousEngine] startup fast tick scheduled in ${delayMs}ms after ${reason} (remaining=${this.startupFastTickAttemptsRemaining}).`);
|
|
6614
|
+
this.startupFastTickTimer = setTimeout(() => {
|
|
6615
|
+
this.startupFastTickTimer = null;
|
|
6616
|
+
if (!this.runtimeEnabled || !this.timer)
|
|
6617
|
+
return;
|
|
6618
|
+
this.nextTickAtMs = Date.now() + this.cfg.tickIntervalMs;
|
|
6619
|
+
this.tick();
|
|
6620
|
+
}, delayMs);
|
|
6621
|
+
}
|
|
6497
6622
|
cycleBudgetMs() {
|
|
6498
6623
|
const ideationTimeoutMs = this.phaseTimeoutMs("ideation");
|
|
6499
6624
|
const scoringTimeoutMs = this.phaseTimeoutMs("scoring");
|
|
@@ -6806,7 +6931,7 @@ class RemoteBuddyAutonomousEngine {
|
|
|
6806
6931
|
sessionId: this.sessionId,
|
|
6807
6932
|
runId,
|
|
6808
6933
|
ttlMs,
|
|
6809
|
-
staleAfterMs: this.
|
|
6934
|
+
staleAfterMs: this.lockStaleAfterMsForAcquire()
|
|
6810
6935
|
})
|
|
6811
6936
|
});
|
|
6812
6937
|
if (res.ok)
|
|
@@ -7122,6 +7247,8 @@ ${JSON.stringify(input.messages ?? [])}`),
|
|
|
7122
7247
|
outcomeDetail = lockResult.reason ? compactStatusDetail(`lock_not_acquired:${lockResult.reason}`) : "lock_not_acquired";
|
|
7123
7248
|
return;
|
|
7124
7249
|
}
|
|
7250
|
+
this.startupFastTickAttemptsRemaining = 0;
|
|
7251
|
+
this.clearStartupFastTickTimer();
|
|
7125
7252
|
this.setPhase("prepare_worktree");
|
|
7126
7253
|
const ready = await this.ensureAutonomyRepoReady(runId);
|
|
7127
7254
|
if (!ready) {
|
|
@@ -7230,58 +7357,79 @@ ${JSON.stringify(input.messages ?? [])}`),
|
|
|
7230
7357
|
return;
|
|
7231
7358
|
}
|
|
7232
7359
|
this.setPhase("ideation");
|
|
7233
|
-
const
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7360
|
+
const buildIdeationInput = (ideationRecovery2, compactRetry) => {
|
|
7361
|
+
const reduced = compactRetry || Boolean(ideationRecovery2);
|
|
7362
|
+
const ideationTopSignals = snapshot.top_signals.slice(0, reduced ? 5 : 16);
|
|
7363
|
+
const ideationStateTraits = snapshot.state_traits.slice(0, reduced ? 6 : 24);
|
|
7364
|
+
const ideationFeedbackPriors = snapshot.feedback_priors.slice(0, reduced ? 4 : 20);
|
|
7365
|
+
const ideationEngineIdeaPriors = (snapshot.engine_idea_priors ?? []).slice(0, reduced ? 4 : 20);
|
|
7366
|
+
const ideationOpenObjectives = snapshot.open_objectives.slice(0, reduced ? 4 : 20);
|
|
7367
|
+
const ideationActiveCooldowns = snapshot.active_cooldowns.slice(0, reduced ? 4 : 20);
|
|
7368
|
+
const ideationRepoTargets = repoTargets.slice(0, reduced ? 4 : repoTargets.length);
|
|
7369
|
+
return {
|
|
7370
|
+
system: IDEATION_SYSTEM_PROMPT,
|
|
7371
|
+
json: true,
|
|
7372
|
+
maxTokens: reduced ? 900 : 2800,
|
|
7373
|
+
temperature: 0.2,
|
|
7374
|
+
messages: [
|
|
7375
|
+
...ideationRecovery2 ? [
|
|
7376
|
+
{
|
|
7377
|
+
role: "user",
|
|
7378
|
+
content: `${IDEATION_TIMEOUT_RECOVERY_INSTRUCTION} Previous timed-out run: ${ideationRecovery2.previousRunId}. Timeout budget for this round: ${this.phaseTimeoutMs("ideation")}ms.`
|
|
7379
|
+
}
|
|
7380
|
+
] : [],
|
|
7251
7381
|
{
|
|
7252
7382
|
role: "user",
|
|
7253
|
-
content:
|
|
7383
|
+
content: JSON.stringify({
|
|
7384
|
+
snapshot: {
|
|
7385
|
+
snapshot_id: snapshot.snapshot_id,
|
|
7386
|
+
top_signals: ideationTopSignals,
|
|
7387
|
+
state_traits: ideationStateTraits,
|
|
7388
|
+
feedback_priors: ideationFeedbackPriors,
|
|
7389
|
+
engine_idea_priors: ideationEngineIdeaPriors,
|
|
7390
|
+
open_objectives: ideationOpenObjectives,
|
|
7391
|
+
active_cooldowns: ideationActiveCooldowns
|
|
7392
|
+
},
|
|
7393
|
+
vision: reduced ? compactVisionContextForIdeationRetry(visionContext) : visionContext,
|
|
7394
|
+
repo_targets: ideationRepoTargets.map((target) => ({
|
|
7395
|
+
component_area: target.component_area,
|
|
7396
|
+
target_paths: target.target_paths,
|
|
7397
|
+
write_globs: target.write_globs,
|
|
7398
|
+
label: target.label,
|
|
7399
|
+
keywords: target.keywords.slice(0, reduced ? 4 : 8)
|
|
7400
|
+
})),
|
|
7401
|
+
engine_inspiration: reduced ? compactEngineInspirationForIdeationRetry(engineInspiration) : engineInspiration,
|
|
7402
|
+
limits: {
|
|
7403
|
+
ideation_max_candidates: reduced ? Math.max(1, Math.min(3, this.cfg.ideationMaxCandidates)) : this.cfg.ideationMaxCandidates,
|
|
7404
|
+
min_confidence: this.cfg.minConfidence
|
|
7405
|
+
}
|
|
7406
|
+
}, null, reduced ? 0 : 2)
|
|
7254
7407
|
}
|
|
7255
|
-
]
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
}
|
|
7281
|
-
}, null, 2)
|
|
7282
|
-
}
|
|
7283
|
-
]
|
|
7284
|
-
});
|
|
7408
|
+
]
|
|
7409
|
+
};
|
|
7410
|
+
};
|
|
7411
|
+
let ideationRecovery = this.consumeIdeationTimeoutRecovery();
|
|
7412
|
+
if (ideationRecovery) {
|
|
7413
|
+
console.warn(`[RemoteBuddyAutonomousEngine] tick ${runId}: applying one-shot ideation timeout recovery from ${ideationRecovery.previousRunId} after ${ideationRecovery.timeoutMs}ms timeout.`);
|
|
7414
|
+
}
|
|
7415
|
+
let ideationPhase;
|
|
7416
|
+
try {
|
|
7417
|
+
ideationPhase = await this.llmPhase("ideation", runId, snapshot.snapshot_id, buildIdeationInput(ideationRecovery, Boolean(ideationRecovery)));
|
|
7418
|
+
} catch (error) {
|
|
7419
|
+
if (error instanceof Error && error.message === "autonomy ideation phase timeout" && !ideationRecovery) {
|
|
7420
|
+
ideationRecovery = {
|
|
7421
|
+
previousRunId: runId,
|
|
7422
|
+
timedOutAt: new Date().toISOString(),
|
|
7423
|
+
timeoutMs: this.phaseTimeoutMs("ideation")
|
|
7424
|
+
};
|
|
7425
|
+
this.pendingIdeationTimeoutRecovery = null;
|
|
7426
|
+
console.warn(`[RemoteBuddyAutonomousEngine] tick ${runId}: ideation timed out; retrying once immediately with reduced context and budget-focused guidance.`);
|
|
7427
|
+
ideationPhase = await this.llmPhase("ideation", runId, snapshot.snapshot_id, buildIdeationInput(ideationRecovery, true));
|
|
7428
|
+
this.pendingIdeationTimeoutRecovery = null;
|
|
7429
|
+
} else {
|
|
7430
|
+
throw error;
|
|
7431
|
+
}
|
|
7432
|
+
}
|
|
7285
7433
|
llmCalls.push(ideationPhase.llmCall);
|
|
7286
7434
|
const ideationJson = ideationPhase.json;
|
|
7287
7435
|
if (this.isSnapshotExpired(snapshot) || Date.now() > cycleDeadline) {
|
|
@@ -7907,6 +8055,9 @@ Scope:
|
|
|
7907
8055
|
await this.releaseDispatchLock(runId);
|
|
7908
8056
|
this.inFlight = false;
|
|
7909
8057
|
this.markTickDone(outcome, outcomeDetail);
|
|
8058
|
+
if (!lockAcquired && outcomeDetail.startsWith("lock_not_acquired")) {
|
|
8059
|
+
this.scheduleStartupFastTick("dispatch lock contention");
|
|
8060
|
+
}
|
|
7910
8061
|
}
|
|
7911
8062
|
}
|
|
7912
8063
|
async enqueueFromAnalysis(instruction, autonomyCtx, originRequestId) {
|
|
@@ -7931,7 +8082,8 @@ Scope:
|
|
|
7931
8082
|
if (!this.runtimeEnabled || this.timer)
|
|
7932
8083
|
return;
|
|
7933
8084
|
console.log(`[RemoteBuddyAutonomousEngine] Using dedicated autonomy worktree ${this.autonomyRepo} (remote=${this.gitRemote} integration=${this.integrationBranch} base=${this.baseBranch}).`);
|
|
7934
|
-
this.
|
|
8085
|
+
this.startupFastTickAttemptsRemaining = STARTUP_FAST_TICK_MAX_ATTEMPTS;
|
|
8086
|
+
this.nextTickAtMs = Date.now();
|
|
7935
8087
|
this.timer = setInterval(() => {
|
|
7936
8088
|
this.nextTickAtMs = Date.now() + this.cfg.tickIntervalMs;
|
|
7937
8089
|
this.tick();
|
|
@@ -7943,6 +8095,7 @@ Scope:
|
|
|
7943
8095
|
this.tick();
|
|
7944
8096
|
}
|
|
7945
8097
|
stop() {
|
|
8098
|
+
this.clearStartupFastTickTimer();
|
|
7946
8099
|
if (this.timer) {
|
|
7947
8100
|
clearInterval(this.timer);
|
|
7948
8101
|
this.timer = null;
|
|
@@ -7951,6 +8104,7 @@ Scope:
|
|
|
7951
8104
|
clearInterval(this.heartbeatTimer);
|
|
7952
8105
|
this.heartbeatTimer = null;
|
|
7953
8106
|
}
|
|
8107
|
+
this.startupFastTickAttemptsRemaining = 0;
|
|
7954
8108
|
this.nextTickAtMs = 0;
|
|
7955
8109
|
}
|
|
7956
8110
|
}
|
|
@@ -8281,7 +8435,7 @@ function buildExecutionGuidance(plan, targetPaths, requiredValidationSteps = [])
|
|
|
8281
8435
|
lines.push(`- ${glob}`);
|
|
8282
8436
|
}
|
|
8283
8437
|
if (Array.isArray(plan.scope.forbidden_globs) && plan.scope.forbidden_globs.length > 0) {
|
|
8284
|
-
lines.push("
|
|
8438
|
+
lines.push("Review guardrail hints:");
|
|
8285
8439
|
for (const glob of plan.scope.forbidden_globs)
|
|
8286
8440
|
lines.push(`- ${glob}`);
|
|
8287
8441
|
}
|