@pushpalsdev/cli 1.0.31 → 1.0.33
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 +33 -4
- package/monitor-ui/+not-found.html +1 -1
- package/monitor-ui/_expo/static/js/web/{entry-275c5f7972e2d2f4f0422fe2213a7f89.js → entry-5e6db7139bc13703a24f952bd64faf4c.js} +2 -2
- package/monitor-ui/_sitemap.html +1 -1
- package/monitor-ui/index.html +1 -1
- package/monitor-ui/modal.html +1 -1
- package/package.json +1 -1
- package/runtime/prompts/localbuddy/localbuddy_planner_git_diff_section.md +0 -1
- package/runtime/prompts/localbuddy/localbuddy_planner_git_status_section.md +0 -1
- package/runtime/prompts/localbuddy/localbuddy_planner_output_contract.md +1 -0
- package/runtime/prompts/remotebuddy/autonomy_ideation_system_prompt.md +31 -30
- package/runtime/prompts/remotebuddy/autonomy_scoring_system_prompt.md +2 -2
- package/runtime/prompts/remotebuddy/context_packer_user_prompt.md +1 -0
- package/runtime/prompts/remotebuddy/remotebuddy_system_prompt.md +1 -0
- package/runtime/prompts/review_agent/review_prompt_template.md +1 -0
- package/runtime/prompts/review_agent/reviewer.md +6 -4
- package/runtime/prompts/workerpals/commit_message_prompt.md +3 -0
- package/runtime/prompts/workerpals/miniswe_broker_system_prompt.md +10 -9
- package/runtime/prompts/workerpals/miniswe_strict_tool_use_guidance.md +1 -0
- package/runtime/prompts/workerpals/openai_codex_runtime_policy_appendix.md +1 -0
- package/runtime/prompts/workerpals/openai_codex_task_execute_system_prompt.md +2 -0
- package/runtime/prompts/workerpals/task_quality_critic_system_prompt.md +3 -2
- package/runtime/sandbox/apps/workerpals/src/backends/openai_codex_backend.ts +4 -4
- package/runtime/sandbox/apps/workerpals/src/backends/openhands_task_execute.ts +1 -4
- package/runtime/sandbox/apps/workerpals/src/common/execution_utils.ts +5 -3
- package/runtime/sandbox/apps/workerpals/src/common/generic_python_executor.ts +1 -4
- package/runtime/sandbox/apps/workerpals/src/common/worktree_cleanup.ts +3 -2
- package/runtime/sandbox/apps/workerpals/src/docker_executor.ts +42 -27
- package/runtime/sandbox/apps/workerpals/src/execute_job.ts +7 -7
- package/runtime/sandbox/apps/workerpals/src/job_runner.ts +7 -4
- package/runtime/sandbox/apps/workerpals/src/workerpals_main.ts +2 -1
- package/runtime/sandbox/packages/shared/src/autonomy_policy.ts +8 -3
- package/runtime/sandbox/packages/shared/src/communication.ts +19 -8
- package/runtime/sandbox/packages/shared/src/config.ts +9 -24
- package/runtime/sandbox/packages/shared/src/config_template_parity.ts +5 -6
- package/runtime/sandbox/packages/shared/src/git_backend.ts +5 -9
- package/runtime/sandbox/packages/shared/src/local_network.ts +3 -1
- package/runtime/sandbox/packages/shared/src/localbuddy_runtime.ts +4 -5
- package/runtime/sandbox/packages/shared/src/vision.ts +6 -2
- package/runtime/sandbox/prompts/workerpals/commit_message_prompt.md +3 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_broker_system_prompt.md +10 -9
- package/runtime/sandbox/prompts/workerpals/miniswe_strict_tool_use_guidance.md +1 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_runtime_policy_appendix.md +1 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_task_execute_system_prompt.md +2 -0
- package/runtime/sandbox/prompts/workerpals/task_quality_critic_system_prompt.md +3 -2
- package/runtime/vision.example.md +24 -5
|
@@ -2,38 +2,39 @@ You are RemoteBuddyAutonomousEngine ideation planner for a monorepo.
|
|
|
2
2
|
Generate objective candidates only from provided evidence signals.
|
|
3
3
|
Return strict JSON with this shape:
|
|
4
4
|
{
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
5
|
+
"candidates": [{
|
|
6
|
+
"id": "cand\_...",
|
|
7
|
+
"title": "...",
|
|
8
|
+
"objective_type": "flaky_test|lint_fix|type_fix|small_refactor|feature_small|feature_medium|feature_large|docs|dep_bump",
|
|
9
|
+
"problem_statement": "...",
|
|
10
|
+
"trigger_type": "test_failure|lint_failure|typecheck_failure|queue_health|regret_signal",
|
|
11
|
+
"component_area": "apps/server|apps/remotebuddy|apps/workerpals|apps/client|packages/protocol|packages/shared|tests/integration|tests/unit",
|
|
12
|
+
"target_paths": ["repo/relative/path"],
|
|
13
|
+
"scope": { "read_anywhere": false, "write_globs": ["repo/relative/glob"] },
|
|
14
|
+
"risk_level": "low|medium|high",
|
|
15
|
+
"expected_validation": ["command"],
|
|
16
|
+
"estimated_effort": "small|medium|large",
|
|
17
|
+
"why_now_signal_ids": ["sig_x"],
|
|
18
|
+
"confidence": 0.0,
|
|
19
|
+
"vision_alignment_reason": "...",
|
|
20
|
+
"vision_section_refs": ["6", "9"],
|
|
21
|
+
"feature_hypotheses": ["feature idea A", "feature idea B"],
|
|
22
|
+
"engine_trial": {
|
|
23
|
+
"building_block_id": "short_id",
|
|
24
|
+
"algorithm": "algorithm label",
|
|
25
|
+
"source": "llm|engine_mapped|engine_fallback",
|
|
26
|
+
"score": 0.0,
|
|
27
|
+
"objective_ids": ["objective_id"],
|
|
28
|
+
"gap_ids": ["gap_id"],
|
|
29
|
+
"summary": "short summary",
|
|
30
|
+
"hypothesis": "short hypothesis"
|
|
31
|
+
},
|
|
32
|
+
"requires_user_input": false,
|
|
33
|
+
"question_if_blocked": ""
|
|
34
|
+
}]
|
|
35
35
|
}
|
|
36
36
|
Constraints:
|
|
37
|
+
|
|
37
38
|
- You will receive `vision.markdown`; use it as inspiration and prioritize candidates that clearly advance that vision.
|
|
38
39
|
- You will also receive `vision.sections`; if numbered sections are present, cite at least one section number in `vision_section_refs`.
|
|
39
40
|
- You will also receive `vision.key_items`; prioritize alignment with `priorities` + `objectives`, respect `guardrails` + `constraints`, and avoid `non_goals`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Score each candidate and return top ids.
|
|
2
2
|
Return strict JSON:
|
|
3
3
|
{
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
"scores": [{ "id": "cand_1", "llm_score": 0.0, "rationale": "..." }],
|
|
5
|
+
"top_candidate_ids": ["cand_1", "cand_2", "cand_3"]
|
|
6
6
|
}
|
|
@@ -5,6 +5,7 @@ Current packed memory:
|
|
|
5
5
|
{{current_memory}}
|
|
6
6
|
|
|
7
7
|
Update the packed memory with maximal fidelity. Requirements:
|
|
8
|
+
|
|
8
9
|
- Preserve concrete instructions, constraints, IDs, file paths, env vars, and error text.
|
|
9
10
|
- Keep conflicting details if present; do not silently discard.
|
|
10
11
|
- Keep output under {{memory_char_budget}} characters.
|
|
@@ -23,6 +23,7 @@ Intent taxonomy (choose the single best fit):
|
|
|
23
23
|
- `other` — do NOT use `other` with `requires_worker=false`. `other` must always have `requires_worker=true`. When in doubt, prefer `code_change`.
|
|
24
24
|
|
|
25
25
|
Classification rules (applied in order):
|
|
26
|
+
|
|
26
27
|
1. Action verb present (add, fix, update, implement, create, remove, test, run, build, configure, refactor, improve, etc.) → `code_change` + `requires_worker=true`
|
|
27
28
|
2. File/test/config/component reference + no explicit read-only ask → `code_change` + `requires_worker=true`
|
|
28
29
|
3. Read-only analysis explicitly requested → `analysis` + `requires_worker=false`
|
|
@@ -7,6 +7,7 @@ Return an objective quality score and specific, actionable feedback. The ReviewA
|
|
|
7
7
|
## Rating Criteria
|
|
8
8
|
|
|
9
9
|
### 9.0-10.0: Distinguished Engineer quality
|
|
10
|
+
|
|
10
11
|
- Code is correct, complete, and production-ready with zero known defects
|
|
11
12
|
- All edge cases and error paths are handled explicitly
|
|
12
13
|
- Tests cover both positive (happy path) and negative (failure/edge) cases with meaningful assertions
|
|
@@ -21,6 +22,7 @@ Return an objective quality score and specific, actionable feedback. The ReviewA
|
|
|
21
22
|
### 1.0-6.9: Not production-ready - list specific issues and remediation steps
|
|
22
23
|
|
|
23
24
|
Common rejection reasons:
|
|
25
|
+
|
|
24
26
|
- Missing negative test assertions
|
|
25
27
|
- Incomplete error handling
|
|
26
28
|
- Tests that pass trivially (no real assertions)
|
|
@@ -32,8 +34,8 @@ Common rejection reasons:
|
|
|
32
34
|
|
|
33
35
|
Respond with a JSON object only (no markdown wrapper):
|
|
34
36
|
{
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
"score": <number 1.0-10.0>,
|
|
38
|
+
"summary": "<one sentence verdict>",
|
|
39
|
+
"issues": ["<issue 1>", "<issue 2>", ...],
|
|
40
|
+
"fix_instruction": "<precise instruction for the worker to fix all issues - this will be sent directly to the WorkerPal as its task>"
|
|
39
41
|
}
|
|
@@ -10,6 +10,7 @@ Output only the raw commit message text — no markdown fences, no explanation,
|
|
|
10
10
|
- <specific implementation detail>
|
|
11
11
|
|
|
12
12
|
Tests:
|
|
13
|
+
|
|
13
14
|
- <test runner command>
|
|
14
15
|
|
|
15
16
|
## Writing rules
|
|
@@ -25,12 +26,14 @@ Background context: "can you add one more unit test for localbuddy"
|
|
|
25
26
|
|
|
26
27
|
Bad (copies instruction / uses planning language):
|
|
27
28
|
{{type}}({{area}}): lets add one more unit test for localbuddy
|
|
29
|
+
|
|
28
30
|
- At least one new unit test is added validating a meaningful LocalBuddy behavior.
|
|
29
31
|
- All existing and new tests pass.
|
|
30
32
|
- No unrelated files are modified.
|
|
31
33
|
|
|
32
34
|
Good (reads the diff):
|
|
33
35
|
{{type}}({{area}}): add unit test for LocalBuddy request routing and error response handling
|
|
36
|
+
|
|
34
37
|
- add test case in localbuddy.test.ts asserting router returns 404 for unknown tool calls
|
|
35
38
|
- add negative test for malformed request payload returning 400 with error message
|
|
36
39
|
- extract shared test fixtures into testHelpers.ts to reduce duplication
|
|
@@ -5,18 +5,19 @@ Repository root: {{repo}}
|
|
|
5
5
|
|
|
6
6
|
Output format (STRICT JSON, no markdown, no extra keys unless specified):
|
|
7
7
|
{
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
"actions": [
|
|
9
|
+
{"type":"read_file","path":"README.md"},
|
|
10
|
+
{"type":"append_line","path":"README.md","line":"..."},
|
|
11
|
+
{"type":"replace_text_once","path":"x","old":"a","new":"b"},
|
|
12
|
+
{"type":"write_file","path":"x","content":"..."},
|
|
13
|
+
{"type":"run_shell","command":"git status --porcelain"}
|
|
14
|
+
],
|
|
15
|
+
"done": false,
|
|
16
|
+
"note": "short explanation"
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
Rules:
|
|
20
|
+
|
|
20
21
|
- Keep actions minimal and directly relevant.
|
|
21
22
|
- JSON syntax must be exact: use ":" between keys and values, never ",".
|
|
22
23
|
- Use double quotes for all keys and string values.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
CRITICAL: You must use tools to make progress.
|
|
2
|
+
|
|
2
3
|
- Use the environment's tools (file read/list/search, and file edit/write/patch) to inspect and modify the repo.
|
|
3
4
|
- Do NOT only describe what you would do; actually do it.
|
|
4
5
|
- Avoid broad scans; choose one target file quickly.
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
You are PushPals WorkerPal running via the OpenAI Codex CLI backend.
|
|
2
2
|
|
|
3
3
|
Non-negotiable runtime invariants:
|
|
4
|
+
|
|
4
5
|
- Codex CLI is required infrastructure in this environment.
|
|
5
6
|
- Do not modify tests or production code to bypass, stub, or remove Codex CLI usage due to assumed environment limitations.
|
|
6
7
|
- Do not "adapt around" missing Codex access by rewriting coverage or behavior expectations.
|
|
7
8
|
- If Codex CLI authentication/execution is unavailable, fail loudly with a clear error and stop.
|
|
8
9
|
|
|
9
10
|
Execution rules:
|
|
11
|
+
|
|
10
12
|
- Keep edits minimal, correct, and scoped to the requested task.
|
|
11
13
|
- Read relevant files before editing, then run focused validation.
|
|
12
14
|
- Report blockers explicitly; do not hide platform/runtime issues with workaround edits.
|
|
@@ -2,8 +2,9 @@ You are a strict code-review critic for worker-generated patches.
|
|
|
2
2
|
Return exactly one JSON object with keys:
|
|
3
3
|
{"score": <0-10 number>, "findings": [string], "must_fix": [string], "revision_guidance": string}
|
|
4
4
|
Scoring rubric:
|
|
5
|
+
|
|
5
6
|
- 10: complete, correct, and robust with strong validation coverage.
|
|
6
7
|
- 8-9: good quality with minor non-blocking issues.
|
|
7
8
|
- <=7: requires revision before commit.
|
|
8
|
-
must_fix must list blocking issues only.
|
|
9
|
-
Do not include markdown or prose outside JSON.
|
|
9
|
+
must_fix must list blocking issues only.
|
|
10
|
+
Do not include markdown or prose outside JSON.
|
|
@@ -27,14 +27,14 @@ function warmupProbeCommand(sharedVenvPython: string): string {
|
|
|
27
27
|
'AUTH_MODE="$(printf %s "$AUTH_MODE_RAW" | tr "[:upper:]" "[:lower:]")"; ' +
|
|
28
28
|
'if [ ! -x "$PY" ]; then PY="$(command -v python3 || command -v python || true)"; fi; ' +
|
|
29
29
|
'[ -n "$PY" ] || { echo "python runtime not found" >&2; exit 1; }; ' +
|
|
30
|
-
|
|
30
|
+
"if command -v bunx >/dev/null 2>&1; then " +
|
|
31
31
|
' CODEX_CMD="bunx --yes @openai/codex"; ' +
|
|
32
|
-
|
|
32
|
+
"elif command -v codex >/dev/null 2>&1; then " +
|
|
33
33
|
' CODEX_CMD="codex"; ' +
|
|
34
|
-
|
|
34
|
+
"else " +
|
|
35
35
|
' echo "Neither bunx nor codex was found in PATH" >&2; ' +
|
|
36
36
|
" exit 1; " +
|
|
37
|
-
|
|
37
|
+
"fi; " +
|
|
38
38
|
'sh -lc "$CODEX_CMD --version"; ' +
|
|
39
39
|
'NEED_LOGIN="0"; ' +
|
|
40
40
|
'if [ "$AUTH_MODE" = "chatgpt" ] || [ "$AUTH_MODE" = "chatgpt_login" ] || [ "$AUTH_MODE" = "subscription" ]; then NEED_LOGIN="1"; fi; ' +
|
|
@@ -55,10 +55,7 @@ function estimateJobTokenUsage(
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
function coerceJobTokenUsage(
|
|
59
|
-
value: unknown,
|
|
60
|
-
fallback: JobTokenUsage,
|
|
61
|
-
): JobTokenUsage {
|
|
58
|
+
function coerceJobTokenUsage(value: unknown, fallback: JobTokenUsage): JobTokenUsage {
|
|
62
59
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
63
60
|
return fallback;
|
|
64
61
|
}
|
|
@@ -16,8 +16,7 @@ export function resolveOutputCompactionPolicy(
|
|
|
16
16
|
const maxOutputChars = Number(overrides.maxOutputChars ?? worker.outputMaxChars);
|
|
17
17
|
const maxOutputLines = Number(overrides.maxOutputLines ?? worker.outputMaxLines);
|
|
18
18
|
const maxOutputHeadLines = Number(overrides.maxOutputHeadLines ?? worker.outputMaxHeadLines);
|
|
19
|
-
const executorResultPrefixRaw =
|
|
20
|
-
overrides.executorResultPrefix ?? worker.executorResultPrefix;
|
|
19
|
+
const executorResultPrefixRaw = overrides.executorResultPrefix ?? worker.executorResultPrefix;
|
|
21
20
|
const executorResultPrefix =
|
|
22
21
|
typeof executorResultPrefixRaw === "string" && executorResultPrefixRaw.length > 0
|
|
23
22
|
? executorResultPrefixRaw
|
|
@@ -42,7 +41,10 @@ export function resolveOutputCompactionPolicy(
|
|
|
42
41
|
|
|
43
42
|
// ---- Output truncation -------------------------------------------------------
|
|
44
43
|
|
|
45
|
-
export function compactJobOutput(
|
|
44
|
+
export function compactJobOutput(
|
|
45
|
+
text: string,
|
|
46
|
+
policyOverrides: Partial<OutputCompactionPolicy> = {},
|
|
47
|
+
): string {
|
|
46
48
|
if (!text) return "";
|
|
47
49
|
const policy = resolveOutputCompactionPolicy(policyOverrides);
|
|
48
50
|
const maxOutputChars = policy.maxOutputChars;
|
|
@@ -58,10 +58,7 @@ function estimateJobTokenUsage(
|
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
function coerceJobTokenUsage(
|
|
62
|
-
value: unknown,
|
|
63
|
-
fallback: JobTokenUsage,
|
|
64
|
-
): JobTokenUsage {
|
|
61
|
+
function coerceJobTokenUsage(value: unknown, fallback: JobTokenUsage): JobTokenUsage {
|
|
65
62
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
66
63
|
return fallback;
|
|
67
64
|
}
|
|
@@ -39,7 +39,9 @@ export async function forceDeleteWorktreePath(
|
|
|
39
39
|
const retries = Math.max(1, Math.floor(options.retries ?? 5));
|
|
40
40
|
const delayMs = Math.max(0, Math.floor(options.delayMs ?? 120));
|
|
41
41
|
const sleep = options.sleepFn ?? defaultSleep;
|
|
42
|
-
const removePath =
|
|
42
|
+
const removePath =
|
|
43
|
+
options.removeFn ??
|
|
44
|
+
((targetPath: string) => rmSync(targetPath, { recursive: true, force: true }));
|
|
43
45
|
const pathExists = options.existsFn ?? ((targetPath: string) => existsSync(targetPath));
|
|
44
46
|
let lastError = "";
|
|
45
47
|
|
|
@@ -63,4 +65,3 @@ export async function forceDeleteWorktreePath(
|
|
|
63
65
|
...(lastError ? { lastError } : {}),
|
|
64
66
|
};
|
|
65
67
|
}
|
|
66
|
-
|
|
@@ -75,7 +75,10 @@ function resolveDockerExecutable(): string {
|
|
|
75
75
|
return process.platform === "win32" ? "docker.exe" : "docker";
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
function resolveWorkerpalSandboxBuildContext(repoRoot: string): {
|
|
78
|
+
function resolveWorkerpalSandboxBuildContext(repoRoot: string): {
|
|
79
|
+
root: string;
|
|
80
|
+
dockerfilePath: string;
|
|
81
|
+
} {
|
|
79
82
|
const configuredRoot = String(process.env.PUSHPALS_WORKERPALS_SANDBOX_ROOT ?? "").trim();
|
|
80
83
|
const sandboxRoot = configuredRoot || repoRoot;
|
|
81
84
|
const dockerfilePath = configuredRoot
|
|
@@ -446,14 +449,11 @@ export class DockerExecutor {
|
|
|
446
449
|
});
|
|
447
450
|
await prune.exited;
|
|
448
451
|
|
|
449
|
-
proc = Bun.spawn(
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
stderr: "pipe",
|
|
455
|
-
},
|
|
456
|
-
);
|
|
452
|
+
proc = Bun.spawn(["git", "worktree", "add", "--force", "--detach", worktreePath, baseRef], {
|
|
453
|
+
cwd: this.options.repo,
|
|
454
|
+
stdout: "pipe",
|
|
455
|
+
stderr: "pipe",
|
|
456
|
+
});
|
|
457
457
|
exitCode = await proc.exited;
|
|
458
458
|
stdout = await new Response(proc.stdout).text();
|
|
459
459
|
stderr = await new Response(proc.stderr).text();
|
|
@@ -713,7 +713,10 @@ export class DockerExecutor {
|
|
|
713
713
|
|
|
714
714
|
args.push("--entrypoint", "/bin/sh", this.options.imageName, "-lc", startupCmd);
|
|
715
715
|
|
|
716
|
-
const proc = Bun.spawn([resolveDockerExecutable(), ...args], {
|
|
716
|
+
const proc = Bun.spawn([resolveDockerExecutable(), ...args], {
|
|
717
|
+
stdout: "pipe",
|
|
718
|
+
stderr: "pipe",
|
|
719
|
+
});
|
|
717
720
|
const [exitCode, stdout, stderr] = await Promise.all([
|
|
718
721
|
proc.exited,
|
|
719
722
|
new Response(proc.stdout).text(),
|
|
@@ -814,10 +817,13 @@ export class DockerExecutor {
|
|
|
814
817
|
stderr: string;
|
|
815
818
|
exitCode: number;
|
|
816
819
|
}> {
|
|
817
|
-
const proc = Bun.spawn(
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
820
|
+
const proc = Bun.spawn(
|
|
821
|
+
[resolveDockerExecutable(), "exec", this.warmContainerName, "/bin/sh", "-lc", command],
|
|
822
|
+
{
|
|
823
|
+
stdout: "pipe",
|
|
824
|
+
stderr: "pipe",
|
|
825
|
+
},
|
|
826
|
+
);
|
|
821
827
|
const [stdout, stderr, exitCode] = await Promise.all([
|
|
822
828
|
new Response(proc.stdout).text(),
|
|
823
829
|
new Response(proc.stderr).text(),
|
|
@@ -854,10 +860,13 @@ export class DockerExecutor {
|
|
|
854
860
|
}
|
|
855
861
|
|
|
856
862
|
private async readWarmContainerLogs(tail = 160): Promise<string> {
|
|
857
|
-
const proc = Bun.spawn(
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
863
|
+
const proc = Bun.spawn(
|
|
864
|
+
[resolveDockerExecutable(), "logs", "--tail", String(tail), this.warmContainerName],
|
|
865
|
+
{
|
|
866
|
+
stdout: "pipe",
|
|
867
|
+
stderr: "pipe",
|
|
868
|
+
},
|
|
869
|
+
);
|
|
861
870
|
const [stdout, stderr, exitCode] = await Promise.all([
|
|
862
871
|
new Response(proc.stdout).text(),
|
|
863
872
|
new Response(proc.stderr).text(),
|
|
@@ -1555,11 +1564,14 @@ export class DockerExecutor {
|
|
|
1555
1564
|
`[DockerExecutor] Worktree path already exists; forcing cleanup before create: ${worktreePath}`,
|
|
1556
1565
|
);
|
|
1557
1566
|
|
|
1558
|
-
const unregister = Bun.spawn(
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1567
|
+
const unregister = Bun.spawn(
|
|
1568
|
+
["git", "worktree", "remove", "--force", "--force", worktreePath],
|
|
1569
|
+
{
|
|
1570
|
+
cwd: this.options.repo,
|
|
1571
|
+
stdout: "pipe",
|
|
1572
|
+
stderr: "pipe",
|
|
1573
|
+
},
|
|
1574
|
+
);
|
|
1563
1575
|
await unregister.exited;
|
|
1564
1576
|
|
|
1565
1577
|
const prune = Bun.spawn(["git", "worktree", "prune"], {
|
|
@@ -1777,10 +1789,13 @@ export class DockerExecutor {
|
|
|
1777
1789
|
* Check if the Docker image exists locally
|
|
1778
1790
|
*/
|
|
1779
1791
|
private async imageExists(): Promise<boolean> {
|
|
1780
|
-
const proc = Bun.spawn(
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1792
|
+
const proc = Bun.spawn(
|
|
1793
|
+
[resolveDockerExecutable(), "image", "inspect", this.options.imageName],
|
|
1794
|
+
{
|
|
1795
|
+
stdout: "pipe",
|
|
1796
|
+
stderr: "pipe",
|
|
1797
|
+
},
|
|
1798
|
+
);
|
|
1784
1799
|
const exitCode = await proc.exited;
|
|
1785
1800
|
return exitCode === 0;
|
|
1786
1801
|
}
|
|
@@ -173,7 +173,10 @@ export function buildQualityGateRevisionIssues(
|
|
|
173
173
|
if (!critic || critic.score >= qualityCriticMinScore) {
|
|
174
174
|
return [...normalizedQualityIssues];
|
|
175
175
|
}
|
|
176
|
-
const merged = [
|
|
176
|
+
const merged = [
|
|
177
|
+
...normalizedQualityIssues,
|
|
178
|
+
...buildCriticRevisionIssues(critic, qualityCriticMinScore),
|
|
179
|
+
];
|
|
177
180
|
return [...new Set(merged)];
|
|
178
181
|
}
|
|
179
182
|
|
|
@@ -2569,7 +2572,8 @@ function validateTaskExecutePlanning(
|
|
|
2569
2572
|
if (!componentArea) {
|
|
2570
2573
|
return {
|
|
2571
2574
|
ok: false,
|
|
2572
|
-
message:
|
|
2575
|
+
message:
|
|
2576
|
+
"task.execute planning.targetPaths must resolve to a repo-relative componentArea",
|
|
2573
2577
|
};
|
|
2574
2578
|
}
|
|
2575
2579
|
if (
|
|
@@ -3056,11 +3060,7 @@ export async function executeJob(
|
|
|
3056
3060
|
return result;
|
|
3057
3061
|
}
|
|
3058
3062
|
|
|
3059
|
-
const issues = buildQualityGateRevisionIssues(
|
|
3060
|
-
quality.issues,
|
|
3061
|
-
critic,
|
|
3062
|
-
qualityCriticMinScore,
|
|
3063
|
-
);
|
|
3063
|
+
const issues = buildQualityGateRevisionIssues(quality.issues, critic, qualityCriticMinScore);
|
|
3064
3064
|
const issueSummary = issues.map((entry) => toSingleLine(entry, 180)).join(" | ");
|
|
3065
3065
|
if (revisionAttempt >= qualityMaxAutoRevisions) {
|
|
3066
3066
|
if (qualitySoftPassOnExhausted) {
|
|
@@ -67,10 +67,13 @@ echo "password=${token}"
|
|
|
67
67
|
writeFileSync(helperPath, helperScript, { mode: 0o755 });
|
|
68
68
|
|
|
69
69
|
// Remove any legacy URL rewrite rules that may have embedded token credentials.
|
|
70
|
-
const urlRules = Bun.spawnSync(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
const urlRules = Bun.spawnSync(
|
|
71
|
+
["git", "config", "--global", "--get-regexp", "^url\\..*\\.insteadOf$"],
|
|
72
|
+
{
|
|
73
|
+
stdout: "pipe",
|
|
74
|
+
stderr: "pipe",
|
|
75
|
+
},
|
|
76
|
+
);
|
|
74
77
|
if (urlRules.exitCode === 0) {
|
|
75
78
|
const lines = String(urlRules.stdout ?? "")
|
|
76
79
|
.split(/\r?\n/)
|
|
@@ -123,7 +123,8 @@ function buildWorkerLlmUsageEvent(
|
|
|
123
123
|
return {
|
|
124
124
|
service: "workerpals",
|
|
125
125
|
sessionId,
|
|
126
|
-
backend:
|
|
126
|
+
backend:
|
|
127
|
+
String(explicitUsage.backend ?? resolveExecutor(CONFIG)).trim() || resolveExecutor(CONFIG),
|
|
127
128
|
modelId: String(explicitUsage.modelId ?? llmConfig.model).trim() || llmConfig.model,
|
|
128
129
|
promptTokens,
|
|
129
130
|
completionTokens,
|
|
@@ -263,8 +263,9 @@ export function globBreadthScore(glob: string): number {
|
|
|
263
263
|
const rootWide = /^[\*]/.test(glob) || glob.startsWith("**/") ? 1 : 0;
|
|
264
264
|
const literalSegments = glob
|
|
265
265
|
.split("/")
|
|
266
|
-
.filter(
|
|
267
|
-
|
|
266
|
+
.filter(
|
|
267
|
+
(segment) => segment.length > 0 && !segment.includes("*") && !segment.includes("?"),
|
|
268
|
+
).length;
|
|
268
269
|
const shallowPenalty = Math.max(0, 2 - Math.min(literalSegments, 2));
|
|
269
270
|
return 4 * hasGlobStar + 2 * rootWide + Math.min(4, wildcardCount) + shallowPenalty;
|
|
270
271
|
}
|
|
@@ -343,7 +344,11 @@ export function validateScopeInvariants(
|
|
|
343
344
|
errors.push(`write_glob outside component root: ${normalized}`);
|
|
344
345
|
continue;
|
|
345
346
|
}
|
|
346
|
-
if (
|
|
347
|
+
if (
|
|
348
|
+
!normalizedTargetPaths.some(
|
|
349
|
+
(targetPath) => targetPath === prefix || targetPath.startsWith(`${prefix}/`),
|
|
350
|
+
)
|
|
351
|
+
) {
|
|
347
352
|
errors.push(`write_glob prefix does not align with target_paths: ${normalized}`);
|
|
348
353
|
continue;
|
|
349
354
|
}
|
|
@@ -21,7 +21,10 @@ function stripPresenceSourcePrefix(value: string): string {
|
|
|
21
21
|
|
|
22
22
|
export function normalizePresenceClientId(value: unknown): string {
|
|
23
23
|
const raw = stripPresenceSourcePrefix(String(value ?? "").trim());
|
|
24
|
-
return raw
|
|
24
|
+
return raw
|
|
25
|
+
.replace(/[^a-zA-Z0-9._-]+/g, "_")
|
|
26
|
+
.replace(/^_+|_+$/g, "")
|
|
27
|
+
.trim();
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
export function normalizePresenceClientLabel(value: unknown): string {
|
|
@@ -31,7 +34,9 @@ export function normalizePresenceClientLabel(value: unknown): string {
|
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
export function normalizePresenceLookupToken(value: unknown): string {
|
|
34
|
-
return normalizePresenceClientLabel(value)
|
|
37
|
+
return normalizePresenceClientLabel(value)
|
|
38
|
+
.toLowerCase()
|
|
39
|
+
.replace(/[^a-z0-9]+/g, "");
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
type SessionTransportPresence = {
|
|
@@ -91,8 +96,9 @@ export class CommunicationManager {
|
|
|
91
96
|
: `${normalizedFrom || "agent"}__${normalizedSessionId || "session"}`,
|
|
92
97
|
kind: "agent",
|
|
93
98
|
label: labelFrom || normalizedFrom || "Agent",
|
|
94
|
-
version: String(
|
|
95
|
-
.
|
|
99
|
+
version: String(
|
|
100
|
+
process.env.PUSHPALS_RUNTIME_TAG ?? process.env.npm_package_version ?? "",
|
|
101
|
+
).trim(),
|
|
96
102
|
platform: `${process.platform}/${process.arch}`,
|
|
97
103
|
repoRoot,
|
|
98
104
|
};
|
|
@@ -151,10 +157,15 @@ export class CommunicationManager {
|
|
|
151
157
|
text: string,
|
|
152
158
|
meta: EventMeta = {},
|
|
153
159
|
): Promise<boolean> {
|
|
154
|
-
return this.emitToSession(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
160
|
+
return this.emitToSession(
|
|
161
|
+
sessionId,
|
|
162
|
+
"message",
|
|
163
|
+
{ text },
|
|
164
|
+
{
|
|
165
|
+
...meta,
|
|
166
|
+
from: meta.from ?? "client",
|
|
167
|
+
},
|
|
168
|
+
);
|
|
158
169
|
}
|
|
159
170
|
|
|
160
171
|
async userMessage(text: string, meta: EventMeta = {}): Promise<boolean> {
|