@pushpalsdev/cli 1.0.84 → 1.0.86
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 +11 -1
- package/package.json +1 -1
- package/runtime/configs/default.toml +8 -1
- package/runtime/configs/local.example.toml +8 -1
- package/runtime/prompts/remotebuddy/autonomy_ideation_system_prompt.md +2 -0
- package/runtime/sandbox/.pushpals-remotebuddy-fallback.js +11 -1
- package/runtime/sandbox/apps/workerpals/src/backends/openhands_task_execute.ts +2 -1
- package/runtime/sandbox/apps/workerpals/src/common/generic_python_executor.ts +2 -1
- package/runtime/sandbox/apps/workerpals/src/common/sandbox_env.ts +76 -0
- package/runtime/sandbox/apps/workerpals/src/execute_job.ts +643 -146
- package/runtime/sandbox/configs/default.toml +8 -1
- package/runtime/sandbox/configs/local.example.toml +8 -1
- package/runtime/sandbox/packages/shared/src/config.ts +34 -1
package/dist/pushpals-cli.js
CHANGED
|
@@ -450,7 +450,7 @@ var DEFAULT_CONFIG_DIR = "configs";
|
|
|
450
450
|
var TRUTHY2 = new Set(["1", "true", "yes", "on"]);
|
|
451
451
|
var FALSY2 = new Set(["0", "false", "no", "off"]);
|
|
452
452
|
var DEFAULT_WORKERPALS_QUALITY_CRITIC_MIN_SCORE = 8;
|
|
453
|
-
var DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS =
|
|
453
|
+
var DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS = 3;
|
|
454
454
|
var DEFAULT_WORKERPALS_FILE_MODIFYING_JOBS = ["task.execute"];
|
|
455
455
|
var DEFAULT_WORKERPALS_OUTPUT_MAX_CHARS = 192 * 1024;
|
|
456
456
|
var DEFAULT_WORKERPALS_OUTPUT_MAX_LINES = 600;
|
|
@@ -817,6 +817,7 @@ function loadPushPalsConfig(options = {}) {
|
|
|
817
817
|
const workerOpenAICodexPython = firstNonEmpty(process.env.PUSHPALS_OPENAI_CODEX_PYTHON, asString(workerNode.openai_codex_python, "python"), "python");
|
|
818
818
|
const workerOpenAICodexTimeoutMs = Math.max(1e4, asInt(workerNode.openai_codex_timeout_ms, 7200000));
|
|
819
819
|
const workerQualityMaxAutoRevisions = Math.max(0, Math.min(10, asInt(parseIntEnv("WORKERPALS_QUALITY_MAX_AUTO_REVISIONS") ?? workerNode.quality_max_auto_revisions, DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS)));
|
|
820
|
+
const workerQualityValidationMaxAutoRevisions = Math.max(0, Math.min(10, asInt(parseIntEnv("WORKERPALS_QUALITY_VALIDATION_MAX_AUTO_REVISIONS") ?? workerNode.quality_validation_max_auto_revisions, DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS)));
|
|
820
821
|
const workerFileModifyingJobs = (() => {
|
|
821
822
|
const envRaw = firstNonEmpty(process.env.WORKERPALS_FILE_MODIFYING_JOBS);
|
|
822
823
|
const parsed = envRaw ? envRaw.split(",").map((entry) => entry.trim()).filter(Boolean) : asStringArray(workerNode.file_modifying_jobs);
|
|
@@ -829,6 +830,10 @@ function loadPushPalsConfig(options = {}) {
|
|
|
829
830
|
const workerQualityValidationStepTimeoutMs = Math.max(1000, asInt(parseIntEnv("WORKERPALS_QUALITY_VALIDATION_STEP_TIMEOUT_MS") ?? workerNode.quality_validation_step_timeout_ms, DEFAULT_WORKERPALS_QUALITY_VALIDATION_STEP_TIMEOUT_MS));
|
|
830
831
|
const workerQualityCriticTimeoutMs = Math.max(1000, asInt(parseIntEnv("WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS") ?? workerNode.quality_critic_timeout_ms, DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS));
|
|
831
832
|
const workerQualitySoftPassOnExhausted = parseBoolEnv("WORKERPALS_QUALITY_SOFT_PASS_ON_EXHAUSTED") ?? asBoolean(workerNode.quality_soft_pass_on_exhausted, true);
|
|
833
|
+
const workerQualityScopeGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_SCOPE_GATE_ENABLED") ?? asBoolean(workerNode.quality_scope_gate_enabled, true);
|
|
834
|
+
const workerQualityValidationGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_VALIDATION_GATE_ENABLED") ?? asBoolean(workerNode.quality_validation_gate_enabled, true);
|
|
835
|
+
const workerQualityCriticGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_CRITIC_GATE_ENABLED") ?? asBoolean(workerNode.quality_critic_gate_enabled, true);
|
|
836
|
+
const workerQualityPublishGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_PUBLISH_GATE_ENABLED") ?? asBoolean(workerNode.quality_publish_gate_enabled, true);
|
|
832
837
|
const workerQualityCriticMinScore = (() => {
|
|
833
838
|
const configThresholdRaw = workerNode.quality_critic_min_score == null ? "" : String(workerNode.quality_critic_min_score);
|
|
834
839
|
const raw = firstNonEmpty(process.env.WORKERPALS_QUALITY_CRITIC_MIN_SCORE, configThresholdRaw, String(DEFAULT_WORKERPALS_QUALITY_CRITIC_MIN_SCORE));
|
|
@@ -1119,6 +1124,11 @@ function loadPushPalsConfig(options = {}) {
|
|
|
1119
1124
|
outputMaxLines: workerOutputMaxLines,
|
|
1120
1125
|
outputMaxHeadLines: workerOutputMaxHeadLines,
|
|
1121
1126
|
qualityMaxAutoRevisions: workerQualityMaxAutoRevisions,
|
|
1127
|
+
qualityValidationMaxAutoRevisions: workerQualityValidationMaxAutoRevisions,
|
|
1128
|
+
qualityScopeGateEnabled: workerQualityScopeGateEnabled,
|
|
1129
|
+
qualityValidationGateEnabled: workerQualityValidationGateEnabled,
|
|
1130
|
+
qualityCriticGateEnabled: workerQualityCriticGateEnabled,
|
|
1131
|
+
qualityPublishGateEnabled: workerQualityPublishGateEnabled,
|
|
1122
1132
|
qualityValidationStepTimeoutMs: workerQualityValidationStepTimeoutMs,
|
|
1123
1133
|
qualityCriticTimeoutMs: workerQualityCriticTimeoutMs,
|
|
1124
1134
|
qualitySoftPassOnExhausted: workerQualitySoftPassOnExhausted,
|
package/package.json
CHANGED
|
@@ -150,7 +150,14 @@ file_modifying_jobs = ["task.execute"]
|
|
|
150
150
|
output_max_chars = 196608
|
|
151
151
|
output_max_lines = 600
|
|
152
152
|
output_max_head_lines = 120
|
|
153
|
-
quality_max_auto_revisions =
|
|
153
|
+
quality_max_auto_revisions = 3
|
|
154
|
+
quality_validation_max_auto_revisions = 3
|
|
155
|
+
quality_scope_gate_enabled = true
|
|
156
|
+
quality_validation_gate_enabled = true
|
|
157
|
+
quality_critic_gate_enabled = true
|
|
158
|
+
quality_publish_gate_enabled = true
|
|
159
|
+
# Browser/e2e validation commands get a longer built-in floor (10m) because they
|
|
160
|
+
# may need to start a dev server and run browser automation.
|
|
154
161
|
quality_validation_step_timeout_ms = 180000
|
|
155
162
|
quality_critic_timeout_ms = 45000
|
|
156
163
|
quality_soft_pass_on_exhausted = true
|
|
@@ -66,7 +66,14 @@ file_modifying_jobs = ["task.execute"]
|
|
|
66
66
|
output_max_chars = 196608
|
|
67
67
|
output_max_lines = 600
|
|
68
68
|
output_max_head_lines = 120
|
|
69
|
-
quality_max_auto_revisions =
|
|
69
|
+
quality_max_auto_revisions = 3
|
|
70
|
+
quality_validation_max_auto_revisions = 3
|
|
71
|
+
quality_scope_gate_enabled = true
|
|
72
|
+
quality_validation_gate_enabled = true
|
|
73
|
+
quality_critic_gate_enabled = true
|
|
74
|
+
quality_publish_gate_enabled = true
|
|
75
|
+
# Browser/e2e validation commands get a longer built-in floor (10m) because they
|
|
76
|
+
# may need to start a dev server and run browser automation.
|
|
70
77
|
quality_validation_step_timeout_ms = 180000
|
|
71
78
|
quality_critic_timeout_ms = 45000
|
|
72
79
|
quality_soft_pass_on_exhausted = true
|
|
@@ -57,6 +57,8 @@ Constraints:
|
|
|
57
57
|
- `feature_hypotheses` may contain any suitable product/engineering features; keep each item concise and actionable.
|
|
58
58
|
- target_paths must be literal repo-relative paths.
|
|
59
59
|
- write_globs must be repo-relative globs.
|
|
60
|
+
- Choose target_paths that own the behavior being improved, not thin route wrappers, re-export files, or shell components, unless the requested change is explicitly at that wrapper boundary.
|
|
61
|
+
- For UI/game/product-surface objectives, prefer files that render or compute the relevant state directly; use wrapper files only for navigation, mounting, or screen-level chrome work.
|
|
60
62
|
- do not invent evidence ids.
|
|
61
63
|
- If all signals are low/noisy, it is valid to return zero candidates.
|
|
62
64
|
- Treat a low `sig_queue_health` value as maintenance-window evidence for safe proactive work, not only incident response.
|
|
@@ -734,7 +734,7 @@ var DEFAULT_CONFIG_DIR = "configs";
|
|
|
734
734
|
var TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
735
735
|
var FALSY = new Set(["0", "false", "no", "off"]);
|
|
736
736
|
var DEFAULT_WORKERPALS_QUALITY_CRITIC_MIN_SCORE = 8;
|
|
737
|
-
var DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS =
|
|
737
|
+
var DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS = 3;
|
|
738
738
|
var DEFAULT_WORKERPALS_FILE_MODIFYING_JOBS = ["task.execute"];
|
|
739
739
|
var DEFAULT_WORKERPALS_OUTPUT_MAX_CHARS = 192 * 1024;
|
|
740
740
|
var DEFAULT_WORKERPALS_OUTPUT_MAX_LINES = 600;
|
|
@@ -1103,6 +1103,7 @@ function loadPushPalsConfig(options = {}) {
|
|
|
1103
1103
|
const workerOpenAICodexPython = firstNonEmpty(process.env.PUSHPALS_OPENAI_CODEX_PYTHON, asString(workerNode.openai_codex_python, "python"), "python");
|
|
1104
1104
|
const workerOpenAICodexTimeoutMs = Math.max(1e4, asInt(workerNode.openai_codex_timeout_ms, 7200000));
|
|
1105
1105
|
const workerQualityMaxAutoRevisions = Math.max(0, Math.min(10, asInt(parseIntEnv("WORKERPALS_QUALITY_MAX_AUTO_REVISIONS") ?? workerNode.quality_max_auto_revisions, DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS)));
|
|
1106
|
+
const workerQualityValidationMaxAutoRevisions = Math.max(0, Math.min(10, asInt(parseIntEnv("WORKERPALS_QUALITY_VALIDATION_MAX_AUTO_REVISIONS") ?? workerNode.quality_validation_max_auto_revisions, DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS)));
|
|
1106
1107
|
const workerFileModifyingJobs = (() => {
|
|
1107
1108
|
const envRaw = firstNonEmpty(process.env.WORKERPALS_FILE_MODIFYING_JOBS);
|
|
1108
1109
|
const parsed = envRaw ? envRaw.split(",").map((entry) => entry.trim()).filter(Boolean) : asStringArray(workerNode.file_modifying_jobs);
|
|
@@ -1115,6 +1116,10 @@ function loadPushPalsConfig(options = {}) {
|
|
|
1115
1116
|
const workerQualityValidationStepTimeoutMs = Math.max(1000, asInt(parseIntEnv("WORKERPALS_QUALITY_VALIDATION_STEP_TIMEOUT_MS") ?? workerNode.quality_validation_step_timeout_ms, DEFAULT_WORKERPALS_QUALITY_VALIDATION_STEP_TIMEOUT_MS));
|
|
1116
1117
|
const workerQualityCriticTimeoutMs = Math.max(1000, asInt(parseIntEnv("WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS") ?? workerNode.quality_critic_timeout_ms, DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS));
|
|
1117
1118
|
const workerQualitySoftPassOnExhausted = parseBoolEnv("WORKERPALS_QUALITY_SOFT_PASS_ON_EXHAUSTED") ?? asBoolean(workerNode.quality_soft_pass_on_exhausted, true);
|
|
1119
|
+
const workerQualityScopeGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_SCOPE_GATE_ENABLED") ?? asBoolean(workerNode.quality_scope_gate_enabled, true);
|
|
1120
|
+
const workerQualityValidationGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_VALIDATION_GATE_ENABLED") ?? asBoolean(workerNode.quality_validation_gate_enabled, true);
|
|
1121
|
+
const workerQualityCriticGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_CRITIC_GATE_ENABLED") ?? asBoolean(workerNode.quality_critic_gate_enabled, true);
|
|
1122
|
+
const workerQualityPublishGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_PUBLISH_GATE_ENABLED") ?? asBoolean(workerNode.quality_publish_gate_enabled, true);
|
|
1118
1123
|
const workerQualityCriticMinScore = (() => {
|
|
1119
1124
|
const configThresholdRaw = workerNode.quality_critic_min_score == null ? "" : String(workerNode.quality_critic_min_score);
|
|
1120
1125
|
const raw = firstNonEmpty(process.env.WORKERPALS_QUALITY_CRITIC_MIN_SCORE, configThresholdRaw, String(DEFAULT_WORKERPALS_QUALITY_CRITIC_MIN_SCORE));
|
|
@@ -1405,6 +1410,11 @@ function loadPushPalsConfig(options = {}) {
|
|
|
1405
1410
|
outputMaxLines: workerOutputMaxLines,
|
|
1406
1411
|
outputMaxHeadLines: workerOutputMaxHeadLines,
|
|
1407
1412
|
qualityMaxAutoRevisions: workerQualityMaxAutoRevisions,
|
|
1413
|
+
qualityValidationMaxAutoRevisions: workerQualityValidationMaxAutoRevisions,
|
|
1414
|
+
qualityScopeGateEnabled: workerQualityScopeGateEnabled,
|
|
1415
|
+
qualityValidationGateEnabled: workerQualityValidationGateEnabled,
|
|
1416
|
+
qualityCriticGateEnabled: workerQualityCriticGateEnabled,
|
|
1417
|
+
qualityPublishGateEnabled: workerQualityPublishGateEnabled,
|
|
1408
1418
|
qualityValidationStepTimeoutMs: workerQualityValidationStepTimeoutMs,
|
|
1409
1419
|
qualityCriticTimeoutMs: workerQualityCriticTimeoutMs,
|
|
1410
1420
|
qualitySoftPassOnExhausted: workerQualitySoftPassOnExhausted,
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
parseStructuredResult,
|
|
17
17
|
filterResultLines,
|
|
18
18
|
} from "../common/execution_utils.js";
|
|
19
|
+
import { buildWorkerSandboxWritableEnv } from "../common/sandbox_env.js";
|
|
19
20
|
import { computeTimeoutWarningWindow } from "../timeout_policy.js";
|
|
20
21
|
|
|
21
22
|
// ---- Script path (resolved relative to this file) ----------------------------
|
|
@@ -286,7 +287,7 @@ export async function executeWithOpenHands(
|
|
|
286
287
|
stdout: "pipe",
|
|
287
288
|
stderr: "pipe",
|
|
288
289
|
env: {
|
|
289
|
-
...
|
|
290
|
+
...buildWorkerSandboxWritableEnv(repo),
|
|
290
291
|
PUSHPALS_REPO_PATH: repo,
|
|
291
292
|
PUSHPALS_ASSIGNED_REPO_ROOT: repo,
|
|
292
293
|
PYTHONIOENCODING: "utf-8",
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
filterResultLines,
|
|
19
19
|
streamLines,
|
|
20
20
|
} from "./execution_utils.js";
|
|
21
|
+
import { buildWorkerSandboxWritableEnv } from "./sandbox_env.js";
|
|
21
22
|
|
|
22
23
|
interface GenericPythonExecutorConfig {
|
|
23
24
|
backendName: string;
|
|
@@ -180,7 +181,7 @@ export function createGenericPythonExecutor(
|
|
|
180
181
|
stdout: "pipe",
|
|
181
182
|
stderr: "pipe",
|
|
182
183
|
env: {
|
|
183
|
-
...
|
|
184
|
+
...buildWorkerSandboxWritableEnv(repo),
|
|
184
185
|
PUSHPALS_REPO_PATH: repo,
|
|
185
186
|
PUSHPALS_ASSIGNED_REPO_ROOT: repo,
|
|
186
187
|
PYTHONIOENCODING: "utf-8",
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
import { existsSync, mkdirSync } from "fs";
|
|
3
|
+
import { homedir, tmpdir } from "os";
|
|
4
|
+
import { basename, resolve } from "path";
|
|
5
|
+
|
|
6
|
+
function stringEnv(source: NodeJS.ProcessEnv = process.env): Record<string, string> {
|
|
7
|
+
const env: Record<string, string> = {};
|
|
8
|
+
for (const [key, value] of Object.entries(source)) {
|
|
9
|
+
if (typeof value === "string") env[key] = value;
|
|
10
|
+
}
|
|
11
|
+
return env;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function safeRepoSlug(repo: string): string {
|
|
15
|
+
const leaf = basename(resolve(repo)).replace(/[^A-Za-z0-9_.-]+/g, "-") || "repo";
|
|
16
|
+
const hash = createHash("sha256").update(resolve(repo)).digest("hex").slice(0, 12);
|
|
17
|
+
return `${leaf}-${hash}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function defaultExpoPortForRepo(repo: string): string {
|
|
21
|
+
const hashPrefix = createHash("sha256").update(resolve(repo)).digest("hex").slice(0, 8);
|
|
22
|
+
const offset = Number.parseInt(hashPrefix, 16) % 1_000;
|
|
23
|
+
return String(19_006 + offset);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function ensureDirs(paths: string[]): void {
|
|
27
|
+
for (const path of paths) {
|
|
28
|
+
try {
|
|
29
|
+
mkdirSync(path, { recursive: true });
|
|
30
|
+
} catch {
|
|
31
|
+
// Best effort: command output will expose any remaining filesystem blocker.
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function resolveOriginalHome(env: Record<string, string>): string {
|
|
37
|
+
return env.HOME || env.USERPROFILE || homedir();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function resolveCodexHome(env: Record<string, string>, originalHome: string): string | undefined {
|
|
41
|
+
if (env.CODEX_HOME) return env.CODEX_HOME;
|
|
42
|
+
const defaultCodexHome = resolve(originalHome, ".codex");
|
|
43
|
+
return existsSync(defaultCodexHome) ? defaultCodexHome : undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function buildWorkerSandboxWritableEnv(
|
|
47
|
+
repo: string,
|
|
48
|
+
sourceEnv: NodeJS.ProcessEnv = process.env,
|
|
49
|
+
): Record<string, string> {
|
|
50
|
+
const env = stringEnv(sourceEnv);
|
|
51
|
+
const originalHome = resolveOriginalHome(env);
|
|
52
|
+
const codexHome = resolveCodexHome(env, originalHome);
|
|
53
|
+
const baseDir = resolve(tmpdir(), "pushpals-worker-env", safeRepoSlug(repo));
|
|
54
|
+
const homeDir = resolve(baseDir, "home");
|
|
55
|
+
const cacheDir = resolve(baseDir, "cache");
|
|
56
|
+
const expoDir = resolve(baseDir, "expo");
|
|
57
|
+
const defaultExpoPort = defaultExpoPortForRepo(repo);
|
|
58
|
+
ensureDirs([homeDir, cacheDir, expoDir, resolve(cacheDir, "npm")]);
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
...env,
|
|
62
|
+
...(codexHome ? { CODEX_HOME: codexHome } : {}),
|
|
63
|
+
HOME: homeDir,
|
|
64
|
+
USERPROFILE: homeDir,
|
|
65
|
+
XDG_CACHE_HOME: cacheDir,
|
|
66
|
+
npm_config_cache: resolve(cacheDir, "npm"),
|
|
67
|
+
EXPO_HOME: expoDir,
|
|
68
|
+
EXPO_NO_TELEMETRY: env.EXPO_NO_TELEMETRY ?? "1",
|
|
69
|
+
EXPO_NO_INTERACTIVE: env.EXPO_NO_INTERACTIVE ?? "1",
|
|
70
|
+
CI: env.CI ?? "1",
|
|
71
|
+
BROWSER: env.BROWSER ?? "none",
|
|
72
|
+
EXPO_DEV_SERVER_PORT: env.EXPO_DEV_SERVER_PORT ?? defaultExpoPort,
|
|
73
|
+
RCT_METRO_PORT: env.RCT_METRO_PORT ?? defaultExpoPort,
|
|
74
|
+
PUSHPALS_VALIDATION_REPO: repo,
|
|
75
|
+
};
|
|
76
|
+
}
|