@algosuite/vo-mcp 0.2.0-beta.60 → 0.2.0-beta.61
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/ci/check-local-pr-overlap.js +117 -7
- package/dist/install-cli.js +68 -6
- package/dist/install-cli.js.map +3 -3
- package/dist/runner-cli.js +758 -319
- package/dist/runner-cli.js.map +4 -4
- package/package.json +1 -1
package/dist/runner-cli.js
CHANGED
|
@@ -636,6 +636,49 @@ var init_codex_mcp_config = __esm({
|
|
|
636
636
|
}
|
|
637
637
|
});
|
|
638
638
|
|
|
639
|
+
// src/remote-mcp-entry.ts
|
|
640
|
+
function remoteMcpOptIn(env2) {
|
|
641
|
+
return (env2["VO_REMOTE_MCP"] ?? "").trim() === "1";
|
|
642
|
+
}
|
|
643
|
+
function remoteMcpUrl(controlPlaneUrl2) {
|
|
644
|
+
return `${controlPlaneUrl2.trim().replace(/\/+$/, "")}${REMOTE_MCP_ENDPOINT_PATH}`;
|
|
645
|
+
}
|
|
646
|
+
function buildRemoteMcpEntry(controlPlaneUrl2) {
|
|
647
|
+
return {
|
|
648
|
+
type: "http",
|
|
649
|
+
url: remoteMcpUrl(controlPlaneUrl2),
|
|
650
|
+
headers: { Authorization: REMOTE_MCP_AUTH_HEADER }
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
function asRecord(value) {
|
|
654
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
655
|
+
}
|
|
656
|
+
function isManagedRemoteMcpEntry(value) {
|
|
657
|
+
const entry = asRecord(value);
|
|
658
|
+
if (!entry) return false;
|
|
659
|
+
if (entry["type"] !== "http") return false;
|
|
660
|
+
const url = entry["url"];
|
|
661
|
+
if (typeof url !== "string" || !url.endsWith(REMOTE_MCP_ENDPOINT_PATH)) return false;
|
|
662
|
+
const headers = asRecord(entry["headers"]);
|
|
663
|
+
return headers?.["Authorization"] === REMOTE_MCP_AUTH_HEADER;
|
|
664
|
+
}
|
|
665
|
+
function remoteMcpEntryIsCurrent(value, desired) {
|
|
666
|
+
const entry = asRecord(value);
|
|
667
|
+
if (!entry) return false;
|
|
668
|
+
const headers = asRecord(entry["headers"]);
|
|
669
|
+
return entry["type"] === desired.type && entry["url"] === desired.url && headers?.["Authorization"] === desired.headers["Authorization"];
|
|
670
|
+
}
|
|
671
|
+
var REMOTE_MCP_SERVER_KEY, REMOTE_MCP_TOKEN_ENV, REMOTE_MCP_ENDPOINT_PATH, REMOTE_MCP_AUTH_HEADER;
|
|
672
|
+
var init_remote_mcp_entry = __esm({
|
|
673
|
+
"src/remote-mcp-entry.ts"() {
|
|
674
|
+
"use strict";
|
|
675
|
+
REMOTE_MCP_SERVER_KEY = "vo-mcp-remote";
|
|
676
|
+
REMOTE_MCP_TOKEN_ENV = "VO_MCP_REMOTE_TOKEN";
|
|
677
|
+
REMOTE_MCP_ENDPOINT_PATH = "/api/v1/mcp";
|
|
678
|
+
REMOTE_MCP_AUTH_HEADER = `Bearer \${${REMOTE_MCP_TOKEN_ENV}}`;
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
|
|
639
682
|
// src/autostart.ts
|
|
640
683
|
import { homedir as homedir4, platform as platform2 } from "node:os";
|
|
641
684
|
import { isAbsolute as isAbsolute2, join as join5 } from "node:path";
|
|
@@ -913,7 +956,20 @@ function preferredNodeCommand(existing) {
|
|
|
913
956
|
function resolveVoMcpCliPath() {
|
|
914
957
|
return join6(dirname4(fileURLToPath(import.meta.url)), "cli.js");
|
|
915
958
|
}
|
|
916
|
-
function
|
|
959
|
+
function reconcileRemoteEntry(mcpServers, remote) {
|
|
960
|
+
const existing = mcpServers[REMOTE_MCP_SERVER_KEY];
|
|
961
|
+
if (remote) {
|
|
962
|
+
return {
|
|
963
|
+
fragment: { [REMOTE_MCP_SERVER_KEY]: remote },
|
|
964
|
+
current: remoteMcpEntryIsCurrent(existing, remote)
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
if (existing !== void 0 && isManagedRemoteMcpEntry(existing)) {
|
|
968
|
+
return { fragment: { [REMOTE_MCP_SERVER_KEY]: void 0 }, current: false };
|
|
969
|
+
}
|
|
970
|
+
return { fragment: {}, current: true };
|
|
971
|
+
}
|
|
972
|
+
function installMcpConfigAt(configPath, cliPath, controlPlaneUrl2, log2, label, launcher = { launcherPath: null, ...INSTALL_LAUNCHER }, remote = null) {
|
|
917
973
|
if (launcher.onlyExisting && !existsSync7(configPath)) return;
|
|
918
974
|
const read = readClaudeConfig(configPath);
|
|
919
975
|
if (read.kind === "invalid" || read.kind === "empty" && launcher.onlyExisting) {
|
|
@@ -927,8 +983,9 @@ function installMcpConfigAt(configPath, cliPath, controlPlaneUrl2, log2, label,
|
|
|
927
983
|
const voEntry = managedEntry ?? mcpServers["vo"];
|
|
928
984
|
const { launcherPath } = launcher;
|
|
929
985
|
const fallbackCli = chooseFallbackCli(voEntry?.env?.[MCP_FALLBACK_CLI_ENV], cliPath, launcher.sticky, launcherPath ? dirname4(launcherPath) : null);
|
|
930
|
-
const
|
|
931
|
-
|
|
986
|
+
const localCurrent = launcherPath ? !isStaleVoMcpEntry(voEntry, launcherPath, fallbackCli) : Boolean(voEntry?.args?.some((a) => a.includes(cliPath)));
|
|
987
|
+
const remoteState = reconcileRemoteEntry(mcpServers, remote);
|
|
988
|
+
if (localCurrent && remoteState.current) {
|
|
932
989
|
log2(` ${label} already current: ${configPath}`);
|
|
933
990
|
return;
|
|
934
991
|
}
|
|
@@ -948,7 +1005,11 @@ function installMcpConfigAt(configPath, cliPath, controlPlaneUrl2, log2, label,
|
|
|
948
1005
|
...preservedEnv,
|
|
949
1006
|
...launcherPath && fallbackCli ? { [MCP_FALLBACK_CLI_ENV]: fallbackCli } : {}
|
|
950
1007
|
}
|
|
951
|
-
}
|
|
1008
|
+
},
|
|
1009
|
+
// Additive remote entry (11.3 slice A) — or the removal of one we wrote,
|
|
1010
|
+
// when the operator has opted back out. Spread LAST so it can delete its
|
|
1011
|
+
// own key; it never names 'vo-mcp', so the stdio entry above is safe.
|
|
1012
|
+
...remoteState.fragment
|
|
952
1013
|
}
|
|
953
1014
|
};
|
|
954
1015
|
if (readMtime !== null && (!existsSync7(configPath) || statSync3(configPath).mtimeMs !== readMtime)) {
|
|
@@ -957,6 +1018,12 @@ function installMcpConfigAt(configPath, cliPath, controlPlaneUrl2, log2, label,
|
|
|
957
1018
|
}
|
|
958
1019
|
writeClaudeConfig(configPath, merged);
|
|
959
1020
|
log2(`\u2713 Wrote vo-mcp to ${label}: ${configPath}`);
|
|
1021
|
+
if (remote) {
|
|
1022
|
+
log2(` + remote MCP entry '${REMOTE_MCP_SERVER_KEY}' \u2192 ${remote.url} (serves vo_skill_list; every other tool stays on the local vo-mcp entry)`);
|
|
1023
|
+
log2(` Set ${REMOTE_MCP_TOKEN_ENV} in your environment to authenticate it \u2014 no token is written to this file.`);
|
|
1024
|
+
} else if (!remoteState.current) {
|
|
1025
|
+
log2(` - removed the remote MCP entry '${REMOTE_MCP_SERVER_KEY}' (VO_REMOTE_MCP is not set to 1)`);
|
|
1026
|
+
}
|
|
960
1027
|
}
|
|
961
1028
|
function installMcpConfig(log2, env2, mode = "install") {
|
|
962
1029
|
const home = env2["HOME"]?.trim() || env2["USERPROFILE"]?.trim() || homedir5();
|
|
@@ -964,6 +1031,7 @@ function installMcpConfig(log2, env2, mode = "install") {
|
|
|
964
1031
|
const plat = platform3();
|
|
965
1032
|
const cliPath = resolveVoMcpCliPath();
|
|
966
1033
|
const controlPlaneUrl2 = env2["VO_CONTROL_PLANE_URL"]?.trim() || DEFAULT_CONTROL_PLANE_URL2;
|
|
1034
|
+
const remote = remoteMcpOptIn(env2) ? buildRemoteMcpEntry(controlPlaneUrl2) : null;
|
|
967
1035
|
const launcherPath = writeMcpLauncherForEnv(env2, log2, { platform: plat, home });
|
|
968
1036
|
const launcher = { launcherPath, ...mode === "heal" ? HEAL_LAUNCHER : INSTALL_LAUNCHER };
|
|
969
1037
|
const leg = (label, run) => {
|
|
@@ -974,8 +1042,8 @@ function installMcpConfig(log2, env2, mode = "install") {
|
|
|
974
1042
|
log2(` \u26A0 ${label}: could not update the MCP registration \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
975
1043
|
}
|
|
976
1044
|
};
|
|
977
|
-
leg("Claude Code CLI", () => installMcpConfigAt(resolveCodeConfigPath(home), cliPath, controlPlaneUrl2, log2, "Claude Code CLI", launcher));
|
|
978
|
-
leg("Claude Desktop", () => installMcpConfigAt(resolveDesktopConfigPath(home, plat, appData), cliPath, controlPlaneUrl2, log2, "Claude Desktop", launcher));
|
|
1045
|
+
leg("Claude Code CLI", () => installMcpConfigAt(resolveCodeConfigPath(home), cliPath, controlPlaneUrl2, log2, "Claude Code CLI", launcher, remote));
|
|
1046
|
+
leg("Claude Desktop", () => installMcpConfigAt(resolveDesktopConfigPath(home, plat, appData), cliPath, controlPlaneUrl2, log2, "Claude Desktop", launcher, remote));
|
|
979
1047
|
const codexPath = resolveCodexConfigPath(home);
|
|
980
1048
|
if (launcher.onlyExisting && !existsSync7(codexPath)) return;
|
|
981
1049
|
leg("Codex", () => {
|
|
@@ -1088,6 +1156,7 @@ var init_install = __esm({
|
|
|
1088
1156
|
init_codex_mcp_config();
|
|
1089
1157
|
init_mcp_launcher();
|
|
1090
1158
|
init_config_backup();
|
|
1159
|
+
init_remote_mcp_entry();
|
|
1091
1160
|
init_autostart();
|
|
1092
1161
|
DEFAULT_CONTROL_PLANE_URL2 = "https://vo-control-plane-bzjphrajaq-uc.a.run.app";
|
|
1093
1162
|
INSTALL_LAUNCHER = { sticky: false, onlyExisting: false };
|
|
@@ -7284,18 +7353,49 @@ function publicationTitlePrompt(task = {}) {
|
|
|
7284
7353
|
if (!original) return prompt;
|
|
7285
7354
|
return original.split(/\n\n(?:Operator follow-up instructions|Treat these as task-scoped)/u)[0].trim() || prompt;
|
|
7286
7355
|
}
|
|
7356
|
+
async function resolveInPlacePrMetadata(worktreeDir, prNumber, { note, env: env2, runFn } = {}) {
|
|
7357
|
+
try {
|
|
7358
|
+
const raw = await runFn("gh", ["pr", "view", String(prNumber), "--json", "title,body"], worktreeDir, { env: env2 });
|
|
7359
|
+
const parsed = JSON.parse(String(raw || "{}"));
|
|
7360
|
+
const title = String(parsed?.title || "").trim();
|
|
7361
|
+
if (!title) return { editMetadata: false };
|
|
7362
|
+
const kept = String(parsed?.body ?? "").split(IN_PLACE_REPAIR_NOTE_MARKER)[0].trimEnd();
|
|
7363
|
+
const trailer = String(note || "").trim();
|
|
7364
|
+
return {
|
|
7365
|
+
editMetadata: true,
|
|
7366
|
+
title,
|
|
7367
|
+
body: trailer ? `${kept}
|
|
7368
|
+
|
|
7369
|
+
${IN_PLACE_REPAIR_NOTE_MARKER}
|
|
7370
|
+
${trailer}` : kept
|
|
7371
|
+
};
|
|
7372
|
+
} catch {
|
|
7373
|
+
return { editMetadata: false };
|
|
7374
|
+
}
|
|
7375
|
+
}
|
|
7287
7376
|
async function syncExistingPrAsync(worktreeDir, existing, options = {}) {
|
|
7288
|
-
const {
|
|
7289
|
-
|
|
7290
|
-
|
|
7377
|
+
const {
|
|
7378
|
+
title,
|
|
7379
|
+
body,
|
|
7380
|
+
draft = false,
|
|
7381
|
+
demoteToDraft = false,
|
|
7382
|
+
editMetadata = true,
|
|
7383
|
+
preserveDraftState = false,
|
|
7384
|
+
env: env2,
|
|
7385
|
+
runFn
|
|
7386
|
+
} = options;
|
|
7387
|
+
if (editMetadata) await editExistingPrMetadata(worktreeDir, existing.number, { title, body, env: env2, runFn });
|
|
7388
|
+
const markedReady = !preserveDraftState && !draft && existing.isDraft;
|
|
7291
7389
|
if (markedReady) await markExistingPrReady(worktreeDir, existing.number, { env: env2, runFn });
|
|
7292
7390
|
const demoted = draft && demoteToDraft && existing.isDraft === false;
|
|
7293
7391
|
if (demoted) await demoteExistingPrToDraft(worktreeDir, existing.number, { env: env2, runFn });
|
|
7294
7392
|
return { markedReady, demoted };
|
|
7295
7393
|
}
|
|
7394
|
+
var IN_PLACE_REPAIR_NOTE_MARKER;
|
|
7296
7395
|
var init_existing_pr_publication = __esm({
|
|
7297
7396
|
"../../scripts/virtual-office/code-runner/existing-pr-publication.mjs"() {
|
|
7298
7397
|
"use strict";
|
|
7398
|
+
IN_PLACE_REPAIR_NOTE_MARKER = "<!-- vo-repair-in-place -->";
|
|
7299
7399
|
}
|
|
7300
7400
|
});
|
|
7301
7401
|
|
|
@@ -7932,6 +8032,485 @@ var init_process_runner2 = __esm({
|
|
|
7932
8032
|
}
|
|
7933
8033
|
});
|
|
7934
8034
|
|
|
8035
|
+
// ../../scripts/virtual-office/code-runner/superseded-pr-cleanup.mjs
|
|
8036
|
+
async function closeSupersededPrAsync(worktreeDir, prNumber, replacementUrl, githubToken = null, { runCommand = defaultRunCommand, expectedHeadSha = null } = {}) {
|
|
8037
|
+
if (!Number.isInteger(prNumber) || prNumber <= 0) return { closed: false, reason: "invalid_pr_number" };
|
|
8038
|
+
if (!REPLACEMENT_URL_RE.test(String(replacementUrl || ""))) return { closed: false, reason: "invalid_replacement_url" };
|
|
8039
|
+
const env2 = githubToken ? installationTokenEnv(githubToken) : void 0;
|
|
8040
|
+
const raw = await runCommand("gh", ["pr", "view", String(prNumber), "--json", "state,headRefOid"], worktreeDir, { env: env2 });
|
|
8041
|
+
const view = JSON.parse(raw || "{}");
|
|
8042
|
+
if (view?.state !== "OPEN") return { closed: false, reason: "not_open" };
|
|
8043
|
+
const liveHead = String(view?.headRefOid || "").trim().toLowerCase();
|
|
8044
|
+
const derivedFrom = String(expectedHeadSha || "").trim().toLowerCase();
|
|
8045
|
+
if (derivedFrom && liveHead && liveHead !== derivedFrom) {
|
|
8046
|
+
return { closed: false, reason: "head_moved" };
|
|
8047
|
+
}
|
|
8048
|
+
const replacementNumber = String(replacementUrl).match(REPLACEMENT_URL_RE)[1];
|
|
8049
|
+
await runCommand(
|
|
8050
|
+
"gh",
|
|
8051
|
+
[
|
|
8052
|
+
"pr",
|
|
8053
|
+
"close",
|
|
8054
|
+
String(prNumber),
|
|
8055
|
+
"--comment",
|
|
8056
|
+
`Superseded by ${replacementUrl} (PR #${replacementNumber}), rebuilt from current main by AlgoHQ repair. Closed at publish time so this lineage keeps exactly one open PR.`
|
|
8057
|
+
],
|
|
8058
|
+
worktreeDir,
|
|
8059
|
+
{ env: env2, timeout: 6e4 }
|
|
8060
|
+
);
|
|
8061
|
+
return { closed: true, reason: null };
|
|
8062
|
+
}
|
|
8063
|
+
async function cleanupSupersededPrAsync({
|
|
8064
|
+
worktreeDir,
|
|
8065
|
+
supersedesPrNumber,
|
|
8066
|
+
supersedesHeadSha = null,
|
|
8067
|
+
replacementUrl,
|
|
8068
|
+
replacementNumber,
|
|
8069
|
+
githubToken,
|
|
8070
|
+
runCommand,
|
|
8071
|
+
retry = (fn) => fn(),
|
|
8072
|
+
onCleanupWarning
|
|
8073
|
+
}) {
|
|
8074
|
+
if (!supersedesPrNumber) return {};
|
|
8075
|
+
if (Number(supersedesPrNumber) === Number(replacementNumber)) {
|
|
8076
|
+
return { supersededPrClosed: false };
|
|
8077
|
+
}
|
|
8078
|
+
try {
|
|
8079
|
+
const result = await retry(
|
|
8080
|
+
() => closeSupersededPrAsync(worktreeDir, supersedesPrNumber, replacementUrl, githubToken, {
|
|
8081
|
+
runCommand,
|
|
8082
|
+
expectedHeadSha: supersedesHeadSha
|
|
8083
|
+
})
|
|
8084
|
+
);
|
|
8085
|
+
return {
|
|
8086
|
+
supersededPrClosed: result.closed,
|
|
8087
|
+
...result.closed ? {} : { supersededPrCloseSkipped: result.reason }
|
|
8088
|
+
};
|
|
8089
|
+
} catch (err) {
|
|
8090
|
+
const message = err && err.message ? err.message : String(err);
|
|
8091
|
+
onCleanupWarning?.(
|
|
8092
|
+
`[publish] replacement PR #${replacementNumber} is open; source PR #${supersedesPrNumber} cleanup failed: ${message}`
|
|
8093
|
+
);
|
|
8094
|
+
return { supersededPrClosed: false, supersededPrCloseError: message };
|
|
8095
|
+
}
|
|
8096
|
+
}
|
|
8097
|
+
var defaultRunCommand, REPLACEMENT_URL_RE;
|
|
8098
|
+
var init_superseded_pr_cleanup = __esm({
|
|
8099
|
+
"../../scripts/virtual-office/code-runner/superseded-pr-cleanup.mjs"() {
|
|
8100
|
+
"use strict";
|
|
8101
|
+
init_publish();
|
|
8102
|
+
init_process_runner2();
|
|
8103
|
+
defaultRunCommand = (cmd, args, cwd, opts = {}) => runProcess2(cmd, args, { cwd, ...opts });
|
|
8104
|
+
REPLACEMENT_URL_RE = /^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/(\d+)$/u;
|
|
8105
|
+
}
|
|
8106
|
+
});
|
|
8107
|
+
|
|
8108
|
+
// ../../scripts/virtual-office/code-runner/github-git-auth-env.mjs
|
|
8109
|
+
function githubGitAuthEnv2(githubToken) {
|
|
8110
|
+
if (!githubToken) return void 0;
|
|
8111
|
+
const credentials = Buffer.from(`x-access-token:${githubToken}`).toString("base64");
|
|
8112
|
+
return {
|
|
8113
|
+
GIT_CONFIG_COUNT: "1",
|
|
8114
|
+
GIT_CONFIG_KEY_0: "http.https://github.com/.extraheader",
|
|
8115
|
+
GIT_CONFIG_VALUE_0: `AUTHORIZATION: basic ${credentials}`
|
|
8116
|
+
};
|
|
8117
|
+
}
|
|
8118
|
+
var init_github_git_auth_env = __esm({
|
|
8119
|
+
"../../scripts/virtual-office/code-runner/github-git-auth-env.mjs"() {
|
|
8120
|
+
"use strict";
|
|
8121
|
+
}
|
|
8122
|
+
});
|
|
8123
|
+
|
|
8124
|
+
// ../../scripts/virtual-office/code-runner/resume-branch.mjs
|
|
8125
|
+
function compactBranchSegment(value) {
|
|
8126
|
+
return String(value || "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "resume";
|
|
8127
|
+
}
|
|
8128
|
+
function defaultRunCommand2(cmd, args, cwd, opts = {}) {
|
|
8129
|
+
return runProcess2(cmd, args, { cwd, ...opts });
|
|
8130
|
+
}
|
|
8131
|
+
function buildResumeLocalBranchName(remoteBranch, {
|
|
8132
|
+
now = () => /* @__PURE__ */ new Date(),
|
|
8133
|
+
pid = process.pid,
|
|
8134
|
+
random = secureUnitRandom
|
|
8135
|
+
} = {}) {
|
|
8136
|
+
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
8137
|
+
const unique = `${pid}-${random().toString(36).slice(2, 8)}`;
|
|
8138
|
+
return `vo/resume-${compactBranchSegment(remoteBranch)}-${stamp}-${unique}`;
|
|
8139
|
+
}
|
|
8140
|
+
async function assertBranchName(worktreeDir, branch, { env: env2, runCommand = defaultRunCommand2 } = {}) {
|
|
8141
|
+
const candidate = String(branch || "").trim();
|
|
8142
|
+
if (!candidate) throw new Error("resume branch is required");
|
|
8143
|
+
await runCommand("git", ["check-ref-format", "--branch", candidate], worktreeDir, {
|
|
8144
|
+
env: env2,
|
|
8145
|
+
timeout: 3e4
|
|
8146
|
+
});
|
|
8147
|
+
return candidate;
|
|
8148
|
+
}
|
|
8149
|
+
async function restoreContinuationBranch(worktreeDir, remoteBranch, {
|
|
8150
|
+
env: env2,
|
|
8151
|
+
runCommand = defaultRunCommand2,
|
|
8152
|
+
localBranchName
|
|
8153
|
+
} = {}) {
|
|
8154
|
+
const branch = await assertBranchName(worktreeDir, remoteBranch, { env: env2, runCommand });
|
|
8155
|
+
const localBranch = await assertBranchName(
|
|
8156
|
+
worktreeDir,
|
|
8157
|
+
localBranchName || buildResumeLocalBranchName(branch),
|
|
8158
|
+
{ env: env2, runCommand }
|
|
8159
|
+
);
|
|
8160
|
+
await runCommand(
|
|
8161
|
+
"git",
|
|
8162
|
+
["fetch", "origin", `${branch}:refs/remotes/origin/${branch}`],
|
|
8163
|
+
worktreeDir,
|
|
8164
|
+
{ env: env2, timeout: 12e4 }
|
|
8165
|
+
);
|
|
8166
|
+
await runCommand("git", ["checkout", "-b", localBranch, `origin/${branch}`], worktreeDir, {
|
|
8167
|
+
env: env2,
|
|
8168
|
+
timeout: 6e4
|
|
8169
|
+
});
|
|
8170
|
+
return { localBranch, remoteBranch: branch };
|
|
8171
|
+
}
|
|
8172
|
+
function parseHeadRefName(output) {
|
|
8173
|
+
const parsed = JSON.parse(String(output || "{}"));
|
|
8174
|
+
const branch = String(parsed?.headRefName || "").trim();
|
|
8175
|
+
return branch || null;
|
|
8176
|
+
}
|
|
8177
|
+
async function resolvePrHeadBranch(worktreeDir, {
|
|
8178
|
+
repo,
|
|
8179
|
+
prNumber,
|
|
8180
|
+
githubToken = null,
|
|
8181
|
+
allowAmbientGithubFallback = false,
|
|
8182
|
+
runCommand = defaultRunCommand2
|
|
8183
|
+
} = {}) {
|
|
8184
|
+
if (!repo || !Number.isInteger(prNumber) || prNumber <= 0) return null;
|
|
8185
|
+
const args = ["pr", "view", String(prNumber), "-R", String(repo), "--json", "headRefName"];
|
|
8186
|
+
try {
|
|
8187
|
+
const output = await runCommand("gh", args, worktreeDir, {
|
|
8188
|
+
env: githubToken ? installationTokenEnv(githubToken) : void 0,
|
|
8189
|
+
timeout: 6e4
|
|
8190
|
+
});
|
|
8191
|
+
return parseHeadRefName(output);
|
|
8192
|
+
} catch (error) {
|
|
8193
|
+
if (!githubToken || !allowAmbientGithubFallback) throw error;
|
|
8194
|
+
}
|
|
8195
|
+
const fallback = await runCommand("gh", args, worktreeDir, { timeout: 6e4 });
|
|
8196
|
+
return parseHeadRefName(fallback);
|
|
8197
|
+
}
|
|
8198
|
+
async function prepareContinuationBranch(worktreeDir, {
|
|
8199
|
+
task,
|
|
8200
|
+
parentTask,
|
|
8201
|
+
githubToken = null,
|
|
8202
|
+
allowAmbientGithubFallback = false,
|
|
8203
|
+
runCommand = defaultRunCommand2
|
|
8204
|
+
} = {}) {
|
|
8205
|
+
const continuationBranch = task?.pr_branch || parentTask?.pr_branch || (parentTask?.pr_number ? await resolvePrHeadBranch(worktreeDir, {
|
|
8206
|
+
repo: task?.repo,
|
|
8207
|
+
prNumber: parentTask.pr_number,
|
|
8208
|
+
githubToken,
|
|
8209
|
+
allowAmbientGithubFallback,
|
|
8210
|
+
runCommand
|
|
8211
|
+
}) : null);
|
|
8212
|
+
if (!continuationBranch && (task?.pr_branch || parentTask?.pr_url || parentTask?.pr_number)) {
|
|
8213
|
+
throw new Error(`continuation PR branch could not be resolved for task ${task?.code_task_id || "unknown"}`);
|
|
8214
|
+
}
|
|
8215
|
+
if (!continuationBranch) return null;
|
|
8216
|
+
return restoreContinuationBranch(worktreeDir, continuationBranch, {
|
|
8217
|
+
env: githubToken ? installationTokenEnv(githubToken) : void 0,
|
|
8218
|
+
runCommand
|
|
8219
|
+
});
|
|
8220
|
+
}
|
|
8221
|
+
var init_resume_branch = __esm({
|
|
8222
|
+
"../../scripts/virtual-office/code-runner/resume-branch.mjs"() {
|
|
8223
|
+
"use strict";
|
|
8224
|
+
init_publish();
|
|
8225
|
+
init_process_runner2();
|
|
8226
|
+
init_secure_random();
|
|
8227
|
+
}
|
|
8228
|
+
});
|
|
8229
|
+
|
|
8230
|
+
// ../../scripts/virtual-office/code-runner/repair-source-materialization.mjs
|
|
8231
|
+
function assertRepairSource({ repo, prNumber, headSha }) {
|
|
8232
|
+
if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u.test(String(repo || ""))) {
|
|
8233
|
+
throw new Error("repair source repository is invalid");
|
|
8234
|
+
}
|
|
8235
|
+
if (!Number.isInteger(prNumber) || prNumber < 1) {
|
|
8236
|
+
throw new Error("repair source PR number is invalid");
|
|
8237
|
+
}
|
|
8238
|
+
if (!/^[0-9a-f]{40}$/u.test(String(headSha || "").toLowerCase())) {
|
|
8239
|
+
throw new Error("repair source head SHA is missing or invalid");
|
|
8240
|
+
}
|
|
8241
|
+
}
|
|
8242
|
+
function defaultRun(command, args, cwd, options = {}) {
|
|
8243
|
+
const env2 = options.env ? { ...process.env, ...options.env } : process.env;
|
|
8244
|
+
return runProcess2(command, args, { cwd, ...options, env: env2 });
|
|
8245
|
+
}
|
|
8246
|
+
async function materializeRepairSource(worktreeDir, {
|
|
8247
|
+
repo,
|
|
8248
|
+
prNumber,
|
|
8249
|
+
headSha,
|
|
8250
|
+
githubToken = null,
|
|
8251
|
+
runCommand = defaultRun
|
|
8252
|
+
} = {}) {
|
|
8253
|
+
assertRepairSource({ repo, prNumber, headSha });
|
|
8254
|
+
const expectedHead = headSha.toLowerCase();
|
|
8255
|
+
const sourceRef = `refs/vo/repairs/${expectedHead}`;
|
|
8256
|
+
const env2 = githubToken ? githubGitAuthEnv2(githubToken) : void 0;
|
|
8257
|
+
await runCommand(
|
|
8258
|
+
"git",
|
|
8259
|
+
["fetch", "origin", `+refs/pull/${prNumber}/head:${sourceRef}`],
|
|
8260
|
+
worktreeDir,
|
|
8261
|
+
{ env: env2, timeout: 12e4 }
|
|
8262
|
+
);
|
|
8263
|
+
const fetchedHead = String(await runCommand(
|
|
8264
|
+
"git",
|
|
8265
|
+
["rev-parse", sourceRef],
|
|
8266
|
+
worktreeDir,
|
|
8267
|
+
{ env: env2, timeout: 3e4 }
|
|
8268
|
+
)).trim().toLowerCase();
|
|
8269
|
+
if (fetchedHead !== expectedHead) {
|
|
8270
|
+
throw new Error(`repair source head changed: admitted ${expectedHead}, fetched ${fetchedHead || "none"}`);
|
|
8271
|
+
}
|
|
8272
|
+
let conflicted = false;
|
|
8273
|
+
try {
|
|
8274
|
+
await runCommand(
|
|
8275
|
+
"git",
|
|
8276
|
+
["merge", "--squash", "--no-commit", sourceRef],
|
|
8277
|
+
worktreeDir,
|
|
8278
|
+
{ env: env2, timeout: 12e4 }
|
|
8279
|
+
);
|
|
8280
|
+
} catch (error) {
|
|
8281
|
+
const conflicts = String(await runCommand(
|
|
8282
|
+
"git",
|
|
8283
|
+
["diff", "--name-only", "--diff-filter=U"],
|
|
8284
|
+
worktreeDir,
|
|
8285
|
+
{ env: env2, timeout: 3e4 }
|
|
8286
|
+
)).trim();
|
|
8287
|
+
if (!conflicts) throw error;
|
|
8288
|
+
conflicted = true;
|
|
8289
|
+
}
|
|
8290
|
+
const status = String(await runCommand(
|
|
8291
|
+
"git",
|
|
8292
|
+
["status", "--porcelain"],
|
|
8293
|
+
worktreeDir,
|
|
8294
|
+
{ env: env2, timeout: 3e4 }
|
|
8295
|
+
)).trim();
|
|
8296
|
+
if (!status) {
|
|
8297
|
+
throw new Error(`repair source ${repo}#${prNumber}@${expectedHead} produced no changes on current main`);
|
|
8298
|
+
}
|
|
8299
|
+
return { sourceRef, headSha: expectedHead, conflicted };
|
|
8300
|
+
}
|
|
8301
|
+
var init_repair_source_materialization = __esm({
|
|
8302
|
+
"../../scripts/virtual-office/code-runner/repair-source-materialization.mjs"() {
|
|
8303
|
+
"use strict";
|
|
8304
|
+
init_process_runner2();
|
|
8305
|
+
init_github_git_auth_env();
|
|
8306
|
+
}
|
|
8307
|
+
});
|
|
8308
|
+
|
|
8309
|
+
// ../../scripts/virtual-office/code-runner/repair-publication-strategy.mjs
|
|
8310
|
+
function isPushRejection(error) {
|
|
8311
|
+
if (!error) return false;
|
|
8312
|
+
const text = `${error.message || ""} ${error.stderr || ""} ${error.stdout || ""}`;
|
|
8313
|
+
return PUSH_REJECTION_RE.test(text);
|
|
8314
|
+
}
|
|
8315
|
+
function defaultRun2(command, args, cwd, options = {}) {
|
|
8316
|
+
return runProcess2(command, args, { cwd, ...options });
|
|
8317
|
+
}
|
|
8318
|
+
function decideRepairStrategy({
|
|
8319
|
+
headBranch,
|
|
8320
|
+
prState,
|
|
8321
|
+
isCrossRepository = false,
|
|
8322
|
+
admittedHeadSha = null,
|
|
8323
|
+
liveHeadSha = null,
|
|
8324
|
+
branchFetched = true,
|
|
8325
|
+
behindCount = null,
|
|
8326
|
+
maxBehindCommits = DEFAULT_MAX_BEHIND_COMMITS,
|
|
8327
|
+
inPlaceEnabled = true
|
|
8328
|
+
} = {}) {
|
|
8329
|
+
const supersede = (reason) => ({ strategy: "supersede", reason });
|
|
8330
|
+
if (!inPlaceEnabled) return supersede(SUPERSEDE_REASONS.disabled);
|
|
8331
|
+
if (!String(headBranch || "").trim()) return supersede(SUPERSEDE_REASONS.branchUnresolved);
|
|
8332
|
+
if (prState && String(prState).toUpperCase() !== "OPEN") return supersede(SUPERSEDE_REASONS.predecessorNotOpen);
|
|
8333
|
+
if (isCrossRepository) return supersede(SUPERSEDE_REASONS.crossForkHead);
|
|
8334
|
+
if (admittedHeadSha && liveHeadSha && String(liveHeadSha).toLowerCase() !== String(admittedHeadSha).toLowerCase()) {
|
|
8335
|
+
return supersede(SUPERSEDE_REASONS.headMoved);
|
|
8336
|
+
}
|
|
8337
|
+
if (branchFetched === false) return supersede(SUPERSEDE_REASONS.branchDeleted);
|
|
8338
|
+
if (Number.isInteger(behindCount) && Number.isInteger(maxBehindCommits) && behindCount > maxBehindCommits) {
|
|
8339
|
+
return supersede(SUPERSEDE_REASONS.baseDiverged);
|
|
8340
|
+
}
|
|
8341
|
+
return { strategy: "in-place", reason: null };
|
|
8342
|
+
}
|
|
8343
|
+
async function viewRepairPr(worktreeDir, { repo, prNumber, env: env2, runCommand }) {
|
|
8344
|
+
try {
|
|
8345
|
+
const raw = await runCommand("gh", [
|
|
8346
|
+
"pr",
|
|
8347
|
+
"view",
|
|
8348
|
+
String(prNumber),
|
|
8349
|
+
"-R",
|
|
8350
|
+
String(repo),
|
|
8351
|
+
"--json",
|
|
8352
|
+
"headRefName,headRefOid,isCrossRepository,state"
|
|
8353
|
+
], worktreeDir, { env: env2, timeout: 6e4 });
|
|
8354
|
+
const parsed = JSON.parse(String(raw || "{}"));
|
|
8355
|
+
return {
|
|
8356
|
+
headBranch: String(parsed.headRefName || "").trim(),
|
|
8357
|
+
liveHeadSha: String(parsed.headRefOid || "").trim().toLowerCase() || null,
|
|
8358
|
+
isCrossRepository: Boolean(parsed.isCrossRepository),
|
|
8359
|
+
prState: String(parsed.state || "").trim() || null
|
|
8360
|
+
};
|
|
8361
|
+
} catch {
|
|
8362
|
+
return { headBranch: "", liveHeadSha: null, isCrossRepository: false, prState: null };
|
|
8363
|
+
}
|
|
8364
|
+
}
|
|
8365
|
+
async function probeRepairBranch(worktreeDir, { headBranch, env: env2, runCommand }) {
|
|
8366
|
+
try {
|
|
8367
|
+
await runCommand("git", ["check-ref-format", "--branch", headBranch], worktreeDir, { env: env2, timeout: 3e4 });
|
|
8368
|
+
await runCommand(
|
|
8369
|
+
"git",
|
|
8370
|
+
["fetch", "origin", `+refs/heads/${headBranch}:refs/remotes/origin/${headBranch}`],
|
|
8371
|
+
worktreeDir,
|
|
8372
|
+
{ env: env2, timeout: 12e4 }
|
|
8373
|
+
);
|
|
8374
|
+
} catch {
|
|
8375
|
+
return { branchFetched: false, fetchedHeadSha: null, behindCount: null };
|
|
8376
|
+
}
|
|
8377
|
+
const fetchedHeadSha = String(await runCommand(
|
|
8378
|
+
"git",
|
|
8379
|
+
["rev-parse", `refs/remotes/origin/${headBranch}`],
|
|
8380
|
+
worktreeDir,
|
|
8381
|
+
{ env: env2, timeout: 3e4 }
|
|
8382
|
+
) || "").trim().toLowerCase() || null;
|
|
8383
|
+
let behindCount = null;
|
|
8384
|
+
try {
|
|
8385
|
+
const raw = await runCommand(
|
|
8386
|
+
"git",
|
|
8387
|
+
["rev-list", "--count", `refs/remotes/origin/${headBranch}..origin/main`],
|
|
8388
|
+
worktreeDir,
|
|
8389
|
+
{ env: env2, timeout: 6e4 }
|
|
8390
|
+
);
|
|
8391
|
+
const parsed = Number.parseInt(String(raw || "").trim(), 10);
|
|
8392
|
+
if (Number.isInteger(parsed)) behindCount = parsed;
|
|
8393
|
+
} catch {
|
|
8394
|
+
}
|
|
8395
|
+
return { branchFetched: true, fetchedHeadSha, behindCount };
|
|
8396
|
+
}
|
|
8397
|
+
async function planRepairPublication(worktreeDir, {
|
|
8398
|
+
repo,
|
|
8399
|
+
prNumber,
|
|
8400
|
+
headSha,
|
|
8401
|
+
githubToken = null,
|
|
8402
|
+
env: env2 = process.env,
|
|
8403
|
+
maxBehindCommits = DEFAULT_MAX_BEHIND_COMMITS,
|
|
8404
|
+
runCommand = defaultRun2,
|
|
8405
|
+
materializeSource = materializeRepairSource,
|
|
8406
|
+
log: log2 = () => {
|
|
8407
|
+
}
|
|
8408
|
+
} = {}) {
|
|
8409
|
+
if (!Number.isInteger(prNumber) || prNumber <= 0) {
|
|
8410
|
+
throw new Error("structured repair PR target is invalid; refusing publication");
|
|
8411
|
+
}
|
|
8412
|
+
const ghEnv = githubToken ? installationTokenEnv(githubToken) : void 0;
|
|
8413
|
+
const gitEnv = githubToken ? githubGitAuthEnv2(githubToken) : void 0;
|
|
8414
|
+
const admittedHeadSha = String(headSha || "").trim().toLowerCase() || null;
|
|
8415
|
+
const inPlaceEnabled = String(env2?.[REPAIR_IN_PLACE_ENV] ?? "1") !== "0";
|
|
8416
|
+
const view = await viewRepairPr(worktreeDir, { repo, prNumber, env: ghEnv, runCommand });
|
|
8417
|
+
let decision = decideRepairStrategy({ ...view, admittedHeadSha, inPlaceEnabled, maxBehindCommits });
|
|
8418
|
+
let probe = { branchFetched: true, fetchedHeadSha: null, behindCount: null };
|
|
8419
|
+
if (decision.strategy === "in-place") {
|
|
8420
|
+
probe = await probeRepairBranch(worktreeDir, { headBranch: view.headBranch, env: gitEnv, runCommand });
|
|
8421
|
+
decision = decideRepairStrategy({
|
|
8422
|
+
...view,
|
|
8423
|
+
...probe,
|
|
8424
|
+
admittedHeadSha,
|
|
8425
|
+
inPlaceEnabled,
|
|
8426
|
+
maxBehindCommits,
|
|
8427
|
+
// The fetched tip is the authoritative head; `gh` can lag a force-push.
|
|
8428
|
+
liveHeadSha: probe.fetchedHeadSha || view.liveHeadSha
|
|
8429
|
+
});
|
|
8430
|
+
}
|
|
8431
|
+
if (decision.strategy === "supersede") {
|
|
8432
|
+
log2(`repair PR #${prNumber}: in-place impossible (${decision.reason}) \u2014 superseding from current main`);
|
|
8433
|
+
const source = await materializeSource(worktreeDir, { repo, prNumber, headSha, githubToken, runCommand });
|
|
8434
|
+
return { strategy: "supersede", reason: decision.reason, prNumber, headSha: source.headSha, conflicted: source.conflicted };
|
|
8435
|
+
}
|
|
8436
|
+
const localBranch = buildResumeLocalBranchName(view.headBranch);
|
|
8437
|
+
await runCommand("git", ["check-ref-format", "--branch", localBranch], worktreeDir, { env: gitEnv, timeout: 3e4 });
|
|
8438
|
+
await runCommand(
|
|
8439
|
+
"git",
|
|
8440
|
+
["checkout", "-b", localBranch, `refs/remotes/origin/${view.headBranch}`],
|
|
8441
|
+
worktreeDir,
|
|
8442
|
+
{ env: gitEnv, timeout: 6e4 }
|
|
8443
|
+
);
|
|
8444
|
+
log2(`repair PR #${prNumber}: repairing IN PLACE on its own branch ${view.headBranch} (local ${localBranch})`);
|
|
8445
|
+
return {
|
|
8446
|
+
strategy: "in-place",
|
|
8447
|
+
reason: null,
|
|
8448
|
+
prNumber,
|
|
8449
|
+
remoteBranch: view.headBranch,
|
|
8450
|
+
localBranch,
|
|
8451
|
+
headSha: probe.fetchedHeadSha || admittedHeadSha
|
|
8452
|
+
};
|
|
8453
|
+
}
|
|
8454
|
+
async function pushWithInPlaceFallback({
|
|
8455
|
+
worktreeDir,
|
|
8456
|
+
branch,
|
|
8457
|
+
remoteBranch,
|
|
8458
|
+
githubToken,
|
|
8459
|
+
allowAmbientFallback = false,
|
|
8460
|
+
supersedeFallback = false,
|
|
8461
|
+
push,
|
|
8462
|
+
retry,
|
|
8463
|
+
onFallback = () => {
|
|
8464
|
+
}
|
|
8465
|
+
}) {
|
|
8466
|
+
const target = String(remoteBranch || branch);
|
|
8467
|
+
try {
|
|
8468
|
+
const tokenUsed = await retry(() => push(worktreeDir, branch, githubToken, { allowAmbientFallback, remoteBranch: target }));
|
|
8469
|
+
return { tokenUsed, remoteBranch: target, fallbackReason: null };
|
|
8470
|
+
} catch (error) {
|
|
8471
|
+
if (!supersedeFallback || target === branch || !isPushRejection(error)) throw error;
|
|
8472
|
+
onFallback(error);
|
|
8473
|
+
const tokenUsed = await retry(() => push(worktreeDir, branch, githubToken, { allowAmbientFallback, remoteBranch: branch }));
|
|
8474
|
+
return { tokenUsed, remoteBranch: branch, fallbackReason: SUPERSEDE_REASONS.pushRejected };
|
|
8475
|
+
}
|
|
8476
|
+
}
|
|
8477
|
+
var REPAIR_IN_PLACE_ENV, DEFAULT_MAX_BEHIND_COMMITS, SUPERSEDE_REASONS, PUSH_REJECTION_RE;
|
|
8478
|
+
var init_repair_publication_strategy = __esm({
|
|
8479
|
+
"../../scripts/virtual-office/code-runner/repair-publication-strategy.mjs"() {
|
|
8480
|
+
"use strict";
|
|
8481
|
+
init_process_runner2();
|
|
8482
|
+
init_publish();
|
|
8483
|
+
init_github_git_auth_env();
|
|
8484
|
+
init_resume_branch();
|
|
8485
|
+
init_repair_source_materialization();
|
|
8486
|
+
REPAIR_IN_PLACE_ENV = "VO_CODE_RUNNER_REPAIR_IN_PLACE";
|
|
8487
|
+
DEFAULT_MAX_BEHIND_COMMITS = 20;
|
|
8488
|
+
SUPERSEDE_REASONS = Object.freeze({
|
|
8489
|
+
branchUnresolved: "branch_unresolved",
|
|
8490
|
+
predecessorNotOpen: "predecessor_not_open",
|
|
8491
|
+
crossForkHead: "cross_fork_head",
|
|
8492
|
+
headMoved: "head_moved",
|
|
8493
|
+
branchDeleted: "branch_deleted",
|
|
8494
|
+
baseDiverged: "base_diverged",
|
|
8495
|
+
pushRejected: "push_rejected",
|
|
8496
|
+
disabled: "in_place_disabled"
|
|
8497
|
+
});
|
|
8498
|
+
PUSH_REJECTION_RE = new RegExp([
|
|
8499
|
+
"\\[rejected\\]",
|
|
8500
|
+
"non-fast-forward",
|
|
8501
|
+
"fetch first",
|
|
8502
|
+
"updates were rejected",
|
|
8503
|
+
"protected branch",
|
|
8504
|
+
"pre-receive hook declined",
|
|
8505
|
+
"remote rejected",
|
|
8506
|
+
"permission to [^\\s]+ denied",
|
|
8507
|
+
"write access to repository not granted",
|
|
8508
|
+
"refusing to allow",
|
|
8509
|
+
"the requested url returned error: 40[13]"
|
|
8510
|
+
].join("|"), "iu");
|
|
8511
|
+
}
|
|
8512
|
+
});
|
|
8513
|
+
|
|
7935
8514
|
// ../../scripts/virtual-office/code-runner/partial-pr-continuation.mjs
|
|
7936
8515
|
function partialPrContinuationResult(run = {}, maxLength = 2e3, reason = null) {
|
|
7937
8516
|
const summary = String(run.summary || "agent stopped before completing the task").trim();
|
|
@@ -7974,10 +8553,10 @@ async function retryTransientAsync(fn, { attempts = 3, baseMs = 5e3, capMs = 3e4
|
|
|
7974
8553
|
}
|
|
7975
8554
|
throw lastErr;
|
|
7976
8555
|
}
|
|
7977
|
-
function
|
|
8556
|
+
function defaultRunCommand3(cmd, args, cwd, opts = {}) {
|
|
7978
8557
|
return runProcess2(cmd, args, { cwd, ...opts });
|
|
7979
8558
|
}
|
|
7980
|
-
async function resolveOrCreateBranchAsync(worktreeDir, branchPrefix, runCommand =
|
|
8559
|
+
async function resolveOrCreateBranchAsync(worktreeDir, branchPrefix, runCommand = defaultRunCommand3) {
|
|
7981
8560
|
let branch = "";
|
|
7982
8561
|
try {
|
|
7983
8562
|
branch = await runCommand("git", ["branch", "--show-current"], worktreeDir, { timeout: 3e4 });
|
|
@@ -8023,7 +8602,7 @@ async function maybeArmAutoMergeAsync({
|
|
|
8023
8602
|
githubToken,
|
|
8024
8603
|
armAutoMerge,
|
|
8025
8604
|
draft,
|
|
8026
|
-
runCommand =
|
|
8605
|
+
runCommand = defaultRunCommand3
|
|
8027
8606
|
}) {
|
|
8028
8607
|
if (!armAutoMerge || draft) return { autoMergeArmed: false };
|
|
8029
8608
|
try {
|
|
@@ -8039,14 +8618,14 @@ async function maybeArmAutoMergeAsync({
|
|
|
8039
8618
|
};
|
|
8040
8619
|
}
|
|
8041
8620
|
}
|
|
8042
|
-
async function listChangedFilesAsync(cwd, { runCommand =
|
|
8621
|
+
async function listChangedFilesAsync(cwd, { runCommand = defaultRunCommand3 } = {}) {
|
|
8043
8622
|
const out = await runCommand("git", ["-c", "core.quotepath=false", "status", "--porcelain", "-z"], cwd, {
|
|
8044
8623
|
timeout: 6e4,
|
|
8045
8624
|
raw: true
|
|
8046
8625
|
});
|
|
8047
8626
|
return parsePorcelainZ(out);
|
|
8048
8627
|
}
|
|
8049
|
-
async function listCommittedFilesAsync(cwd, base = "origin/main", { runCommand =
|
|
8628
|
+
async function listCommittedFilesAsync(cwd, base = "origin/main", { runCommand = defaultRunCommand3 } = {}) {
|
|
8050
8629
|
try {
|
|
8051
8630
|
await runCommand("git", ["fetch", "origin", "main"], cwd, { timeout: 6e4 });
|
|
8052
8631
|
} catch {
|
|
@@ -8068,7 +8647,7 @@ async function commitWorkLocallyAsync(worktreeDir, files, {
|
|
|
8068
8647
|
branchPrefix = "vo/code-task",
|
|
8069
8648
|
botName = "vo-code-runner",
|
|
8070
8649
|
botEmail = "vo-code-runner@algosuite.ai",
|
|
8071
|
-
runCommand =
|
|
8650
|
+
runCommand = defaultRunCommand3
|
|
8072
8651
|
} = {}) {
|
|
8073
8652
|
const cleaned = (files || []).filter((file) => !isAgentScratch(file));
|
|
8074
8653
|
if (cleaned.length === 0) throw new Error("commitWorkLocallyAsync: only scratch files, nothing to commit");
|
|
@@ -8082,65 +8661,29 @@ async function commitWorkLocallyAsync(worktreeDir, files, {
|
|
|
8082
8661
|
await runCommand("git", ["commit", "-m", compactTitle(title, 180)], worktreeDir);
|
|
8083
8662
|
return { branch, truncated: false, committed: true };
|
|
8084
8663
|
}
|
|
8085
|
-
async function existingPrUrlAsync(worktreeDir, branch, githubToken = null, { runCommand =
|
|
8086
|
-
try {
|
|
8087
|
-
const out = await runCommand(
|
|
8088
|
-
"gh",
|
|
8089
|
-
["pr", "list", "--head", branch, "--state", "open", "--json", "url,number,isDraft", "--limit", "1"],
|
|
8090
|
-
worktreeDir,
|
|
8091
|
-
{ env: githubToken ? installationTokenEnv(githubToken) : void 0 }
|
|
8092
|
-
);
|
|
8093
|
-
const list = JSON.parse(out || "[]");
|
|
8094
|
-
if (Array.isArray(list) && list[0] && list[0].url) {
|
|
8095
|
-
return { url: String(list[0].url), number: Number(list[0].number), isDraft: Boolean(list[0].isDraft) };
|
|
8096
|
-
}
|
|
8097
|
-
} catch {
|
|
8098
|
-
}
|
|
8099
|
-
return null;
|
|
8100
|
-
}
|
|
8101
|
-
async function closeSupersededPrAsync(worktreeDir, prNumber, replacementUrl, githubToken = null, { runCommand = defaultRunCommand } = {}) {
|
|
8102
|
-
if (!Number.isInteger(prNumber) || prNumber <= 0) return false;
|
|
8103
|
-
if (!/^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+$/u.test(String(replacementUrl || ""))) return false;
|
|
8104
|
-
const env2 = githubToken ? installationTokenEnv(githubToken) : void 0;
|
|
8105
|
-
const raw = await runCommand("gh", ["pr", "view", String(prNumber), "--json", "state"], worktreeDir, { env: env2 });
|
|
8106
|
-
const state = JSON.parse(raw || "{}")?.state;
|
|
8107
|
-
if (state !== "OPEN") return false;
|
|
8108
|
-
await runCommand(
|
|
8109
|
-
"gh",
|
|
8110
|
-
["pr", "close", String(prNumber), "--comment", `Superseded by ${replacementUrl}, rebuilt from current main by AlgoHQ repair.`],
|
|
8111
|
-
worktreeDir,
|
|
8112
|
-
{ env: env2, timeout: 6e4 }
|
|
8113
|
-
);
|
|
8114
|
-
return true;
|
|
8115
|
-
}
|
|
8116
|
-
async function cleanupSupersededPrAsync({
|
|
8117
|
-
worktreeDir,
|
|
8118
|
-
supersedesPrNumber,
|
|
8119
|
-
replacementUrl,
|
|
8120
|
-
replacementNumber,
|
|
8121
|
-
githubToken,
|
|
8122
|
-
runCommand,
|
|
8123
|
-
onCleanupWarning
|
|
8124
|
-
}) {
|
|
8125
|
-
if (!supersedesPrNumber) return {};
|
|
8126
|
-
if (Number(supersedesPrNumber) === Number(replacementNumber)) {
|
|
8127
|
-
return { supersededPrClosed: false };
|
|
8128
|
-
}
|
|
8664
|
+
async function existingPrUrlAsync(worktreeDir, branch, githubToken = null, { runCommand = defaultRunCommand3 } = {}) {
|
|
8129
8665
|
try {
|
|
8130
|
-
const
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
} catch (err) {
|
|
8136
|
-
const message = err && err.message ? err.message : String(err);
|
|
8137
|
-
onCleanupWarning?.(
|
|
8138
|
-
`[publish] replacement PR #${replacementNumber} is open; source PR #${supersedesPrNumber} cleanup failed: ${message}`
|
|
8666
|
+
const out = await runCommand(
|
|
8667
|
+
"gh",
|
|
8668
|
+
["pr", "list", "--head", branch, "--state", "open", "--json", "url,number,isDraft", "--limit", "1"],
|
|
8669
|
+
worktreeDir,
|
|
8670
|
+
{ env: githubToken ? installationTokenEnv(githubToken) : void 0 }
|
|
8139
8671
|
);
|
|
8140
|
-
|
|
8672
|
+
const list = JSON.parse(out || "[]");
|
|
8673
|
+
if (Array.isArray(list) && list[0] && list[0].url) {
|
|
8674
|
+
return { url: String(list[0].url), number: Number(list[0].number), isDraft: Boolean(list[0].isDraft) };
|
|
8675
|
+
}
|
|
8676
|
+
} catch {
|
|
8141
8677
|
}
|
|
8678
|
+
return null;
|
|
8679
|
+
}
|
|
8680
|
+
function cleanupSuperseded(options) {
|
|
8681
|
+
return cleanupSupersededPrAsync({
|
|
8682
|
+
...options,
|
|
8683
|
+
retry: (fn) => retryTransientAsync(fn, { onRetry: gitRetryLog("gh pr close superseded") })
|
|
8684
|
+
});
|
|
8142
8685
|
}
|
|
8143
|
-
async function pushBranchAsync(worktreeDir, branch, githubToken, { runCommand =
|
|
8686
|
+
async function pushBranchAsync(worktreeDir, branch, githubToken, { runCommand = defaultRunCommand3, allowAmbientFallback = false, remoteBranch = branch } = {}) {
|
|
8144
8687
|
const pushRef = remoteBranch && remoteBranch !== branch ? `HEAD:refs/heads/${remoteBranch}` : branch;
|
|
8145
8688
|
const { primary, fallback } = pushPlan(pushRef, githubToken, { allowAmbientFallback });
|
|
8146
8689
|
try {
|
|
@@ -8166,9 +8709,13 @@ async function openCodeTaskPrAsync(worktreeDir, files, {
|
|
|
8166
8709
|
targetBranch = null,
|
|
8167
8710
|
targetPrNumber = null,
|
|
8168
8711
|
supersedesPrNumber = null,
|
|
8712
|
+
supersedesHeadSha = null,
|
|
8169
8713
|
deferSupersededPrCleanup = false,
|
|
8714
|
+
preserveExistingPr = false,
|
|
8715
|
+
inPlaceNote = "",
|
|
8716
|
+
supersedeFallback = false,
|
|
8170
8717
|
onCleanupWarning = console.error,
|
|
8171
|
-
runCommand =
|
|
8718
|
+
runCommand = defaultRunCommand3,
|
|
8172
8719
|
runOverlapGate = runLocalPrOverlapGateAsync
|
|
8173
8720
|
} = {}) {
|
|
8174
8721
|
if (!Array.isArray(files) || files.length === 0) throw new Error("openCodeTaskPrAsync: no files to commit");
|
|
@@ -8189,7 +8736,9 @@ async function openCodeTaskPrAsync(worktreeDir, files, {
|
|
|
8189
8736
|
truncated = committed.truncated;
|
|
8190
8737
|
newCommit = committed.committed;
|
|
8191
8738
|
}
|
|
8192
|
-
|
|
8739
|
+
let prBranch = String(targetBranch || branch).trim() || branch;
|
|
8740
|
+
let inPlacePrNumber = targetPrNumber;
|
|
8741
|
+
let inPlace = preserveExistingPr;
|
|
8193
8742
|
const overlap = await runOverlapGate(worktreeDir, files.filter((file) => !isAgentScratch(file)), {
|
|
8194
8743
|
branch: prBranch,
|
|
8195
8744
|
env: githubToken ? installationTokenEnv(githubToken) : process.env,
|
|
@@ -8198,26 +8747,47 @@ async function openCodeTaskPrAsync(worktreeDir, files, {
|
|
|
8198
8747
|
const policy = applyOverlapPublishPolicy({ overlap, draft, body });
|
|
8199
8748
|
({ draft, body } = policy);
|
|
8200
8749
|
const { overlapDraft, overlapBlockedBy } = policy;
|
|
8201
|
-
const
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
8750
|
+
const pushed = await pushWithInPlaceFallback({
|
|
8751
|
+
worktreeDir,
|
|
8752
|
+
branch,
|
|
8753
|
+
remoteBranch: prBranch,
|
|
8754
|
+
githubToken,
|
|
8755
|
+
allowAmbientFallback: allowAmbientGithubFallback,
|
|
8756
|
+
supersedeFallback,
|
|
8757
|
+
push: (dir, localBranch, token2, options) => pushBranchAsync(dir, localBranch, token2, { ...options, runCommand }),
|
|
8758
|
+
retry: (fn) => retryTransientAsync(fn, { onRetry: gitRetryLog("git push") }),
|
|
8759
|
+
onFallback: (err) => onCleanupWarning?.(
|
|
8760
|
+
`[publish] in-place push to ${prBranch} was refused (${String(err?.message || err).replace(/\s+/g, " ").slice(0, 160)}); superseding on ${branch} instead`
|
|
8761
|
+
)
|
|
8762
|
+
});
|
|
8763
|
+
const tokenUsed = pushed.tokenUsed;
|
|
8764
|
+
if (pushed.fallbackReason) {
|
|
8765
|
+
prBranch = pushed.remoteBranch;
|
|
8766
|
+
inPlacePrNumber = null;
|
|
8767
|
+
inPlace = false;
|
|
8768
|
+
}
|
|
8209
8769
|
const authToken = tokenUsed ? githubToken : null;
|
|
8210
8770
|
const existing = await existingPrUrlAsync(worktreeDir, prBranch, authToken, { runCommand });
|
|
8211
|
-
if (
|
|
8212
|
-
throw new Error(`explicit target PR #${
|
|
8771
|
+
if (inPlacePrNumber && existing?.number !== inPlacePrNumber) {
|
|
8772
|
+
throw new Error(`explicit target PR #${inPlacePrNumber} was not found on ${prBranch}; refusing duplicate publication`);
|
|
8213
8773
|
}
|
|
8214
8774
|
if (existing) {
|
|
8775
|
+
const env2 = authToken ? installationTokenEnv(authToken) : void 0;
|
|
8776
|
+
const meta = inPlace ? await resolveInPlacePrMetadata(worktreeDir, existing.number, {
|
|
8777
|
+
note: overlapDraft ? `${body}` : inPlaceNote,
|
|
8778
|
+
env: env2,
|
|
8779
|
+
runFn: runCommand
|
|
8780
|
+
}) : { editMetadata: true, title: compactTitle(title), body };
|
|
8215
8781
|
const { markedReady } = await retryTransientAsync(() => syncExistingPrAsync(worktreeDir, existing, {
|
|
8216
|
-
title: compactTitle(title),
|
|
8217
|
-
body,
|
|
8782
|
+
title: meta.title ?? compactTitle(title),
|
|
8783
|
+
body: meta.body ?? body,
|
|
8218
8784
|
draft,
|
|
8219
8785
|
demoteToDraft: overlapDraft,
|
|
8220
|
-
|
|
8786
|
+
editMetadata: meta.editMetadata !== false,
|
|
8787
|
+
// Promoting a draft would remove the only merge guard it carries, and the
|
|
8788
|
+
// repair task did not author that readiness decision. Demotion still runs.
|
|
8789
|
+
preserveDraftState: inPlace,
|
|
8790
|
+
env: env2,
|
|
8221
8791
|
runFn: runCommand
|
|
8222
8792
|
}), { onRetry: gitRetryLog("gh pr sync") });
|
|
8223
8793
|
const autoMerge2 = await maybeArmAutoMergeAsync({
|
|
@@ -8228,9 +8798,10 @@ async function openCodeTaskPrAsync(worktreeDir, files, {
|
|
|
8228
8798
|
draft,
|
|
8229
8799
|
runCommand
|
|
8230
8800
|
});
|
|
8231
|
-
const superseded2 = deferSupersededPrCleanup ? {} : await
|
|
8801
|
+
const superseded2 = deferSupersededPrCleanup ? {} : await cleanupSuperseded({
|
|
8232
8802
|
worktreeDir,
|
|
8233
8803
|
supersedesPrNumber,
|
|
8804
|
+
supersedesHeadSha,
|
|
8234
8805
|
replacementUrl: existing.url,
|
|
8235
8806
|
replacementNumber: existing.number,
|
|
8236
8807
|
githubToken: authToken,
|
|
@@ -8245,6 +8816,7 @@ async function openCodeTaskPrAsync(worktreeDir, files, {
|
|
|
8245
8816
|
newCommit,
|
|
8246
8817
|
resumed: true,
|
|
8247
8818
|
markedReady,
|
|
8819
|
+
repairedInPlace: inPlace,
|
|
8248
8820
|
overlapDraft,
|
|
8249
8821
|
overlapBlockedBy,
|
|
8250
8822
|
...autoMerge2,
|
|
@@ -8272,16 +8844,28 @@ async function openCodeTaskPrAsync(worktreeDir, files, {
|
|
|
8272
8844
|
draft,
|
|
8273
8845
|
runCommand
|
|
8274
8846
|
});
|
|
8275
|
-
const superseded = deferSupersededPrCleanup ? {} : await
|
|
8847
|
+
const superseded = deferSupersededPrCleanup ? {} : await cleanupSuperseded({
|
|
8276
8848
|
worktreeDir,
|
|
8277
8849
|
supersedesPrNumber,
|
|
8850
|
+
supersedesHeadSha,
|
|
8278
8851
|
replacementUrl: prUrl,
|
|
8279
8852
|
replacementNumber: prNumber,
|
|
8280
8853
|
githubToken: authToken,
|
|
8281
8854
|
runCommand,
|
|
8282
8855
|
onCleanupWarning
|
|
8283
8856
|
});
|
|
8284
|
-
return {
|
|
8857
|
+
return {
|
|
8858
|
+
prUrl,
|
|
8859
|
+
prNumber,
|
|
8860
|
+
branch: prBranch,
|
|
8861
|
+
truncated,
|
|
8862
|
+
newCommit,
|
|
8863
|
+
overlapDraft,
|
|
8864
|
+
overlapBlockedBy,
|
|
8865
|
+
...pushed.fallbackReason ? { supersedeFallbackReason: pushed.fallbackReason } : {},
|
|
8866
|
+
...autoMerge,
|
|
8867
|
+
...superseded
|
|
8868
|
+
};
|
|
8285
8869
|
}
|
|
8286
8870
|
var sleep;
|
|
8287
8871
|
var init_publish_async = __esm({
|
|
@@ -8295,7 +8879,10 @@ var init_publish_async = __esm({
|
|
|
8295
8879
|
init_secure_random();
|
|
8296
8880
|
init_pr_overlap_gate();
|
|
8297
8881
|
init_existing_pr_publication();
|
|
8882
|
+
init_superseded_pr_cleanup();
|
|
8883
|
+
init_repair_publication_strategy();
|
|
8298
8884
|
init_partial_pr_continuation();
|
|
8885
|
+
init_superseded_pr_cleanup();
|
|
8299
8886
|
sleep = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
8300
8887
|
}
|
|
8301
8888
|
});
|
|
@@ -11572,13 +12159,15 @@ function buildCiFixPrompt({ prNumber, repo, branch, headSha, failedChecks, prPat
|
|
|
11572
12159
|
`Verified source head SHA: ${headSha || "unknown"}`,
|
|
11573
12160
|
`Failing checks: ${failedChecks?.length ? failedChecks.join(", ") : "unknown"}`,
|
|
11574
12161
|
"",
|
|
11575
|
-
"Before you start, the runner materializes the complete verified PR head into your
|
|
12162
|
+
"Before you start, the runner materializes the complete verified PR head into your worktree.",
|
|
11576
12163
|
"The bounded patch excerpt and failed-job evidence below are diagnostic context only, never source transfer.",
|
|
11577
12164
|
"Diagnose the actual failure from this evidence and the worktree; do not substitute unrelated current-main failures.",
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
"in
|
|
11581
|
-
"
|
|
12165
|
+
`Fix it. The runner owns publication and by DEFAULT repairs PR #${prNumber} IN PLACE on its own`,
|
|
12166
|
+
"branch \u2014 one PR per lineage, no superseding duplicate; it opens a replacement PR only when",
|
|
12167
|
+
"in-place repair is impossible (branch deleted, fork head, push refused, base hopelessly",
|
|
12168
|
+
"diverged). Do NOT open, close, rebase or supersede any PR yourself, and do not add a",
|
|
12169
|
+
"VO-ALLOW-PR-OVERLAP marker. Your work ships: finish the fix completely, leave UNCOMMITTED",
|
|
12170
|
+
"edits, and the runner publishes for you. A denied git/gh is EXPECTED; do NOT retry it.",
|
|
11582
12171
|
"",
|
|
11583
12172
|
HEADLESS_EXECUTION_CONTRACT,
|
|
11584
12173
|
"",
|
|
@@ -12140,112 +12729,6 @@ var init_pr_watcher = __esm({
|
|
|
12140
12729
|
}
|
|
12141
12730
|
});
|
|
12142
12731
|
|
|
12143
|
-
// ../../scripts/virtual-office/code-runner/resume-branch.mjs
|
|
12144
|
-
function compactBranchSegment(value) {
|
|
12145
|
-
return String(value || "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "resume";
|
|
12146
|
-
}
|
|
12147
|
-
function defaultRunCommand2(cmd, args, cwd, opts = {}) {
|
|
12148
|
-
return runProcess2(cmd, args, { cwd, ...opts });
|
|
12149
|
-
}
|
|
12150
|
-
function buildResumeLocalBranchName(remoteBranch, {
|
|
12151
|
-
now = () => /* @__PURE__ */ new Date(),
|
|
12152
|
-
pid = process.pid,
|
|
12153
|
-
random = secureUnitRandom
|
|
12154
|
-
} = {}) {
|
|
12155
|
-
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
12156
|
-
const unique = `${pid}-${random().toString(36).slice(2, 8)}`;
|
|
12157
|
-
return `vo/resume-${compactBranchSegment(remoteBranch)}-${stamp}-${unique}`;
|
|
12158
|
-
}
|
|
12159
|
-
async function assertBranchName(worktreeDir, branch, { env: env2, runCommand = defaultRunCommand2 } = {}) {
|
|
12160
|
-
const candidate = String(branch || "").trim();
|
|
12161
|
-
if (!candidate) throw new Error("resume branch is required");
|
|
12162
|
-
await runCommand("git", ["check-ref-format", "--branch", candidate], worktreeDir, {
|
|
12163
|
-
env: env2,
|
|
12164
|
-
timeout: 3e4
|
|
12165
|
-
});
|
|
12166
|
-
return candidate;
|
|
12167
|
-
}
|
|
12168
|
-
async function restoreContinuationBranch(worktreeDir, remoteBranch, {
|
|
12169
|
-
env: env2,
|
|
12170
|
-
runCommand = defaultRunCommand2,
|
|
12171
|
-
localBranchName
|
|
12172
|
-
} = {}) {
|
|
12173
|
-
const branch = await assertBranchName(worktreeDir, remoteBranch, { env: env2, runCommand });
|
|
12174
|
-
const localBranch = await assertBranchName(
|
|
12175
|
-
worktreeDir,
|
|
12176
|
-
localBranchName || buildResumeLocalBranchName(branch),
|
|
12177
|
-
{ env: env2, runCommand }
|
|
12178
|
-
);
|
|
12179
|
-
await runCommand(
|
|
12180
|
-
"git",
|
|
12181
|
-
["fetch", "origin", `${branch}:refs/remotes/origin/${branch}`],
|
|
12182
|
-
worktreeDir,
|
|
12183
|
-
{ env: env2, timeout: 12e4 }
|
|
12184
|
-
);
|
|
12185
|
-
await runCommand("git", ["checkout", "-b", localBranch, `origin/${branch}`], worktreeDir, {
|
|
12186
|
-
env: env2,
|
|
12187
|
-
timeout: 6e4
|
|
12188
|
-
});
|
|
12189
|
-
return { localBranch, remoteBranch: branch };
|
|
12190
|
-
}
|
|
12191
|
-
function parseHeadRefName(output) {
|
|
12192
|
-
const parsed = JSON.parse(String(output || "{}"));
|
|
12193
|
-
const branch = String(parsed?.headRefName || "").trim();
|
|
12194
|
-
return branch || null;
|
|
12195
|
-
}
|
|
12196
|
-
async function resolvePrHeadBranch(worktreeDir, {
|
|
12197
|
-
repo,
|
|
12198
|
-
prNumber,
|
|
12199
|
-
githubToken = null,
|
|
12200
|
-
allowAmbientGithubFallback = false,
|
|
12201
|
-
runCommand = defaultRunCommand2
|
|
12202
|
-
} = {}) {
|
|
12203
|
-
if (!repo || !Number.isInteger(prNumber) || prNumber <= 0) return null;
|
|
12204
|
-
const args = ["pr", "view", String(prNumber), "-R", String(repo), "--json", "headRefName"];
|
|
12205
|
-
try {
|
|
12206
|
-
const output = await runCommand("gh", args, worktreeDir, {
|
|
12207
|
-
env: githubToken ? installationTokenEnv(githubToken) : void 0,
|
|
12208
|
-
timeout: 6e4
|
|
12209
|
-
});
|
|
12210
|
-
return parseHeadRefName(output);
|
|
12211
|
-
} catch (error) {
|
|
12212
|
-
if (!githubToken || !allowAmbientGithubFallback) throw error;
|
|
12213
|
-
}
|
|
12214
|
-
const fallback = await runCommand("gh", args, worktreeDir, { timeout: 6e4 });
|
|
12215
|
-
return parseHeadRefName(fallback);
|
|
12216
|
-
}
|
|
12217
|
-
async function prepareContinuationBranch(worktreeDir, {
|
|
12218
|
-
task,
|
|
12219
|
-
parentTask,
|
|
12220
|
-
githubToken = null,
|
|
12221
|
-
allowAmbientGithubFallback = false,
|
|
12222
|
-
runCommand = defaultRunCommand2
|
|
12223
|
-
} = {}) {
|
|
12224
|
-
const continuationBranch = task?.pr_branch || parentTask?.pr_branch || (parentTask?.pr_number ? await resolvePrHeadBranch(worktreeDir, {
|
|
12225
|
-
repo: task?.repo,
|
|
12226
|
-
prNumber: parentTask.pr_number,
|
|
12227
|
-
githubToken,
|
|
12228
|
-
allowAmbientGithubFallback,
|
|
12229
|
-
runCommand
|
|
12230
|
-
}) : null);
|
|
12231
|
-
if (!continuationBranch && (task?.pr_branch || parentTask?.pr_url || parentTask?.pr_number)) {
|
|
12232
|
-
throw new Error(`continuation PR branch could not be resolved for task ${task?.code_task_id || "unknown"}`);
|
|
12233
|
-
}
|
|
12234
|
-
if (!continuationBranch) return null;
|
|
12235
|
-
return restoreContinuationBranch(worktreeDir, continuationBranch, {
|
|
12236
|
-
env: githubToken ? installationTokenEnv(githubToken) : void 0,
|
|
12237
|
-
runCommand
|
|
12238
|
-
});
|
|
12239
|
-
}
|
|
12240
|
-
var init_resume_branch = __esm({
|
|
12241
|
-
"../../scripts/virtual-office/code-runner/resume-branch.mjs"() {
|
|
12242
|
-
"use strict";
|
|
12243
|
-
init_publish();
|
|
12244
|
-
init_process_runner2();
|
|
12245
|
-
init_secure_random();
|
|
12246
|
-
}
|
|
12247
|
-
});
|
|
12248
|
-
|
|
12249
12732
|
// ../../scripts/virtual-office/code-runner/existing-pr-target.mjs
|
|
12250
12733
|
function explicitExistingPrNumber(prompt) {
|
|
12251
12734
|
const match = String(prompt || "").match(
|
|
@@ -12253,9 +12736,32 @@ function explicitExistingPrNumber(prompt) {
|
|
|
12253
12736
|
);
|
|
12254
12737
|
return match ? Number(match[1]) : null;
|
|
12255
12738
|
}
|
|
12739
|
+
function inPlaceRepairNote({ task, repairPlan } = {}) {
|
|
12740
|
+
const head = String(repairPlan?.headSha || "").slice(0, 7) || "unknown";
|
|
12741
|
+
const taskId = String(task?.code_task_id || "unknown").slice(0, 8);
|
|
12742
|
+
return `_AlgoHQ autonomous repair updated this PR **in place** on its own branch \`${repairPlan?.remoteBranch}\` (code task \`${taskId}\`, from head \`${head}\`). No superseding PR was opened._`;
|
|
12743
|
+
}
|
|
12744
|
+
function inPlaceTarget({ task, repairPlan }) {
|
|
12745
|
+
return {
|
|
12746
|
+
targetBranch: repairPlan.remoteBranch,
|
|
12747
|
+
targetPrNumber: repairPlan.prNumber,
|
|
12748
|
+
supersedesPrNumber: repairPlan.prNumber,
|
|
12749
|
+
supersedesHeadSha: repairPlan.headSha ?? null,
|
|
12750
|
+
repairStrategy: "in-place",
|
|
12751
|
+
// The predecessor IS this PR: never rewrite its title/body (they carry CI
|
|
12752
|
+
// markers) and never promote its draft flag (the only merge guard it has).
|
|
12753
|
+
preserveExistingPr: true,
|
|
12754
|
+
inPlaceNote: inPlaceRepairNote({ task, repairPlan }),
|
|
12755
|
+
// A remote that REFUSES the in-place push is the enumerated `push_rejected`
|
|
12756
|
+
// escape; publication then falls back to a fresh branch + supersede.
|
|
12757
|
+
supersedeFallback: true,
|
|
12758
|
+
deferSupersededPrCleanup: false
|
|
12759
|
+
};
|
|
12760
|
+
}
|
|
12256
12761
|
async function resolvePublicationTarget({
|
|
12257
12762
|
task,
|
|
12258
12763
|
continuationRestore,
|
|
12764
|
+
repairPlan = null,
|
|
12259
12765
|
worktreeDir,
|
|
12260
12766
|
githubToken,
|
|
12261
12767
|
allowAmbientGithubFallback,
|
|
@@ -12266,16 +12772,26 @@ async function resolvePublicationTarget({
|
|
|
12266
12772
|
if (!Number.isInteger(repairPrNumber) || repairPrNumber <= 0) {
|
|
12267
12773
|
throw new Error("structured repair PR target is invalid; refusing publication");
|
|
12268
12774
|
}
|
|
12775
|
+
if (repairPlan?.strategy === "in-place" && repairPlan.remoteBranch && !continuationRestore) {
|
|
12776
|
+
return inPlaceTarget({ task, repairPlan });
|
|
12777
|
+
}
|
|
12269
12778
|
return {
|
|
12270
12779
|
targetBranch: continuationRestore?.remoteBranch || void 0,
|
|
12271
|
-
supersedesPrNumber: repairPrNumber
|
|
12780
|
+
supersedesPrNumber: repairPrNumber,
|
|
12781
|
+
// Closing the predecessor at PUBLISH time is what stops a repair lineage
|
|
12782
|
+
// from growing a third open layer; the head-SHA guard in
|
|
12783
|
+
// closeSupersededPrAsync is what keeps that close safe.
|
|
12784
|
+
supersedesHeadSha: repairPlan?.strategy === "supersede" ? repairPlan.headSha ?? null : null,
|
|
12785
|
+
repairStrategy: repairPlan?.strategy ?? "supersede",
|
|
12786
|
+
deferSupersededPrCleanup: !(repairPlan?.strategy === "supersede" && repairPlan.headSha)
|
|
12272
12787
|
};
|
|
12273
12788
|
}
|
|
12274
12789
|
const targetPrNumber = explicitExistingPrNumber(task?.prompt);
|
|
12275
12790
|
if (!targetPrNumber) {
|
|
12276
12791
|
return {
|
|
12277
12792
|
targetBranch: continuationRestore?.remoteBranch || void 0,
|
|
12278
|
-
supersedesPrNumber: supersededSourcePrNumber(task?.prompt)
|
|
12793
|
+
supersedesPrNumber: supersededSourcePrNumber(task?.prompt),
|
|
12794
|
+
deferSupersededPrCleanup: true
|
|
12279
12795
|
};
|
|
12280
12796
|
}
|
|
12281
12797
|
const targetBranch = await resolveBranch(worktreeDir, {
|
|
@@ -12287,7 +12803,7 @@ async function resolvePublicationTarget({
|
|
|
12287
12803
|
if (!targetBranch) {
|
|
12288
12804
|
throw new Error(`explicit target PR #${targetPrNumber} head branch could not be resolved; refusing duplicate publication`);
|
|
12289
12805
|
}
|
|
12290
|
-
return { targetBranch, targetPrNumber, supersedesPrNumber: targetPrNumber };
|
|
12806
|
+
return { targetBranch, targetPrNumber, supersedesPrNumber: targetPrNumber, deferSupersededPrCleanup: true };
|
|
12291
12807
|
}
|
|
12292
12808
|
var init_existing_pr_target = __esm({
|
|
12293
12809
|
"../../scripts/virtual-office/code-runner/existing-pr-target.mjs"() {
|
|
@@ -14722,7 +15238,7 @@ var init_inference_task_runner = __esm({
|
|
|
14722
15238
|
import fs12 from "node:fs";
|
|
14723
15239
|
import fsp11 from "node:fs/promises";
|
|
14724
15240
|
import path19 from "node:path";
|
|
14725
|
-
async function
|
|
15241
|
+
async function defaultRun3(command, args, cwd, options = {}) {
|
|
14726
15242
|
return runProcess2(command, args, { cwd, timeout: 6e4, ...options });
|
|
14727
15243
|
}
|
|
14728
15244
|
async function git2(run, cwd, args, options = {}) {
|
|
@@ -14758,7 +15274,7 @@ async function isVerifiedRemoteFastForward(baseline, current, run) {
|
|
|
14758
15274
|
return false;
|
|
14759
15275
|
}
|
|
14760
15276
|
}
|
|
14761
|
-
async function captureCanonicalBaseline(worktreeDir, { run =
|
|
15277
|
+
async function captureCanonicalBaseline(worktreeDir, { run = defaultRun3 } = {}) {
|
|
14762
15278
|
const root = await canonicalRootForWorktree(worktreeDir, run);
|
|
14763
15279
|
if (!root) return { root: null, head: null, status: "", standalone: true };
|
|
14764
15280
|
const state = await snapshot(root, run);
|
|
@@ -14818,7 +15334,7 @@ async function restoreExactCanonicalPaths(baseline, evidence, run) {
|
|
|
14818
15334
|
await fsp11.rm(target, { force: true });
|
|
14819
15335
|
}
|
|
14820
15336
|
}
|
|
14821
|
-
async function assertCanonicalIsolation(baseline, { worktreeDir, taskId, run =
|
|
15337
|
+
async function assertCanonicalIsolation(baseline, { worktreeDir, taskId, run = defaultRun3, now = () => /* @__PURE__ */ new Date() } = {}) {
|
|
14822
15338
|
if (baseline.standalone) return { ok: true, standalone: true };
|
|
14823
15339
|
const current = await snapshot(baseline.root, run);
|
|
14824
15340
|
if (current.head === baseline.head && !current.status) return { ok: true };
|
|
@@ -15058,7 +15574,7 @@ async function closeCancelledReplacementPr({
|
|
|
15058
15574
|
githubToken,
|
|
15059
15575
|
log: log2 = () => {
|
|
15060
15576
|
},
|
|
15061
|
-
runCommand =
|
|
15577
|
+
runCommand = defaultRunCommand4,
|
|
15062
15578
|
reason = "the operator cancelled the task before publication was acknowledged"
|
|
15063
15579
|
}) {
|
|
15064
15580
|
if (!Number.isInteger(pr?.prNumber) || pr.prNumber <= 0 || pr.resumed) return false;
|
|
@@ -15279,7 +15795,7 @@ async function finalizePublishedPr({
|
|
|
15279
15795
|
log2(`task ${id} \u2192 PR ${pr.prUrl}`);
|
|
15280
15796
|
return false;
|
|
15281
15797
|
}
|
|
15282
|
-
var
|
|
15798
|
+
var defaultRunCommand4;
|
|
15283
15799
|
var init_publication_outcome = __esm({
|
|
15284
15800
|
"../../scripts/virtual-office/code-runner/publication-outcome.mjs"() {
|
|
15285
15801
|
"use strict";
|
|
@@ -15292,14 +15808,14 @@ var init_publication_outcome = __esm({
|
|
|
15292
15808
|
init_outcome_commit();
|
|
15293
15809
|
init_terminal_delivery();
|
|
15294
15810
|
init_rate_limit_resume();
|
|
15295
|
-
|
|
15811
|
+
defaultRunCommand4 = (cmd, args, cwd, opts = {}) => runProcess2(cmd, args, { cwd, ...opts });
|
|
15296
15812
|
}
|
|
15297
15813
|
});
|
|
15298
15814
|
|
|
15299
15815
|
// ../../scripts/virtual-office/code-runner/committed-scratch-cleanup.mjs
|
|
15300
15816
|
import fsp12 from "node:fs/promises";
|
|
15301
15817
|
import path20 from "node:path";
|
|
15302
|
-
function
|
|
15818
|
+
function defaultRun4(command, args, cwd, options = {}) {
|
|
15303
15819
|
return runProcess2(command, args, { cwd, ...options });
|
|
15304
15820
|
}
|
|
15305
15821
|
async function resolveSafeScratchTarget(worktreeDir, file) {
|
|
@@ -15323,7 +15839,7 @@ async function resolveSafeScratchTarget(worktreeDir, file) {
|
|
|
15323
15839
|
}
|
|
15324
15840
|
return target;
|
|
15325
15841
|
}
|
|
15326
|
-
async function sanitizePublicationScratch(worktreeDir, scratchFiles, { base = "origin/main", runCommand =
|
|
15842
|
+
async function sanitizePublicationScratch(worktreeDir, scratchFiles, { base = "origin/main", runCommand = defaultRun4 } = {}) {
|
|
15327
15843
|
const unique = [...new Set(scratchFiles || [])];
|
|
15328
15844
|
if (unique.length === 0) return false;
|
|
15329
15845
|
const targets = /* @__PURE__ */ new Map();
|
|
@@ -15610,7 +16126,7 @@ var init_recovery_ledger = __esm({
|
|
|
15610
16126
|
});
|
|
15611
16127
|
|
|
15612
16128
|
// ../../scripts/virtual-office/code-runner/no-changes-terminal-status.mjs
|
|
15613
|
-
function
|
|
16129
|
+
function defaultRunCommand5(cmd, args, cwd, opts = {}) {
|
|
15614
16130
|
return runProcess2(cmd, args, { cwd, ...opts });
|
|
15615
16131
|
}
|
|
15616
16132
|
function explicitTaskOutcome(summary) {
|
|
@@ -15662,10 +16178,15 @@ async function closeSupersededSourceOnNoChanges({
|
|
|
15662
16178
|
run,
|
|
15663
16179
|
worktreeDir,
|
|
15664
16180
|
githubToken,
|
|
16181
|
+
repairInPlace = false,
|
|
15665
16182
|
log: log2 = () => {
|
|
15666
16183
|
},
|
|
15667
|
-
runCommand =
|
|
16184
|
+
runCommand = defaultRunCommand5
|
|
15668
16185
|
} = {}) {
|
|
16186
|
+
if (repairInPlace) {
|
|
16187
|
+
log2("no-changes: repair ran IN PLACE \u2014 leaving the repaired PR open");
|
|
16188
|
+
return false;
|
|
16189
|
+
}
|
|
15669
16190
|
const structured = task?.repair_pr_number;
|
|
15670
16191
|
if (structured !== void 0 && structured !== null && (!Number.isInteger(structured) || structured <= 0)) {
|
|
15671
16192
|
throw new Error("structured repair PR target is invalid; refusing no-change completion");
|
|
@@ -15702,10 +16223,11 @@ async function finalizeNoChangesOutcome({
|
|
|
15702
16223
|
maxTurns,
|
|
15703
16224
|
worktreeDir,
|
|
15704
16225
|
githubToken,
|
|
16226
|
+
repairPlan = null,
|
|
15705
16227
|
safeProgress: safeProgress2,
|
|
15706
16228
|
log: log2 = () => {
|
|
15707
16229
|
},
|
|
15708
|
-
runCommand =
|
|
16230
|
+
runCommand = defaultRunCommand5,
|
|
15709
16231
|
beginCommit = deliverOutcomeCommit,
|
|
15710
16232
|
deliverTerminal = deliverTerminalRun
|
|
15711
16233
|
} = {}) {
|
|
@@ -15720,7 +16242,15 @@ async function finalizeNoChangesOutcome({
|
|
|
15720
16242
|
message: "committing verified no-change outcome and source PR cleanup"
|
|
15721
16243
|
});
|
|
15722
16244
|
if (!committing) return terminal;
|
|
15723
|
-
await closeSupersededSourceOnNoChanges({
|
|
16245
|
+
await closeSupersededSourceOnNoChanges({
|
|
16246
|
+
task,
|
|
16247
|
+
run,
|
|
16248
|
+
worktreeDir,
|
|
16249
|
+
githubToken,
|
|
16250
|
+
log: log2,
|
|
16251
|
+
runCommand,
|
|
16252
|
+
repairInPlace: repairPlan?.strategy === "in-place"
|
|
16253
|
+
});
|
|
15724
16254
|
}
|
|
15725
16255
|
const persisted = await deliverTerminal({
|
|
15726
16256
|
client,
|
|
@@ -16089,101 +16619,6 @@ var init_daemon_config = __esm({
|
|
|
16089
16619
|
}
|
|
16090
16620
|
});
|
|
16091
16621
|
|
|
16092
|
-
// ../../scripts/virtual-office/code-runner/github-git-auth-env.mjs
|
|
16093
|
-
function githubGitAuthEnv2(githubToken) {
|
|
16094
|
-
if (!githubToken) return void 0;
|
|
16095
|
-
const credentials = Buffer.from(`x-access-token:${githubToken}`).toString("base64");
|
|
16096
|
-
return {
|
|
16097
|
-
GIT_CONFIG_COUNT: "1",
|
|
16098
|
-
GIT_CONFIG_KEY_0: "http.https://github.com/.extraheader",
|
|
16099
|
-
GIT_CONFIG_VALUE_0: `AUTHORIZATION: basic ${credentials}`
|
|
16100
|
-
};
|
|
16101
|
-
}
|
|
16102
|
-
var init_github_git_auth_env = __esm({
|
|
16103
|
-
"../../scripts/virtual-office/code-runner/github-git-auth-env.mjs"() {
|
|
16104
|
-
"use strict";
|
|
16105
|
-
}
|
|
16106
|
-
});
|
|
16107
|
-
|
|
16108
|
-
// ../../scripts/virtual-office/code-runner/repair-source-materialization.mjs
|
|
16109
|
-
function assertRepairSource({ repo, prNumber, headSha }) {
|
|
16110
|
-
if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u.test(String(repo || ""))) {
|
|
16111
|
-
throw new Error("repair source repository is invalid");
|
|
16112
|
-
}
|
|
16113
|
-
if (!Number.isInteger(prNumber) || prNumber < 1) {
|
|
16114
|
-
throw new Error("repair source PR number is invalid");
|
|
16115
|
-
}
|
|
16116
|
-
if (!/^[0-9a-f]{40}$/u.test(String(headSha || "").toLowerCase())) {
|
|
16117
|
-
throw new Error("repair source head SHA is missing or invalid");
|
|
16118
|
-
}
|
|
16119
|
-
}
|
|
16120
|
-
function defaultRun3(command, args, cwd, options = {}) {
|
|
16121
|
-
const env2 = options.env ? { ...process.env, ...options.env } : process.env;
|
|
16122
|
-
return runProcess2(command, args, { cwd, ...options, env: env2 });
|
|
16123
|
-
}
|
|
16124
|
-
async function materializeRepairSource(worktreeDir, {
|
|
16125
|
-
repo,
|
|
16126
|
-
prNumber,
|
|
16127
|
-
headSha,
|
|
16128
|
-
githubToken = null,
|
|
16129
|
-
runCommand = defaultRun3
|
|
16130
|
-
} = {}) {
|
|
16131
|
-
assertRepairSource({ repo, prNumber, headSha });
|
|
16132
|
-
const expectedHead = headSha.toLowerCase();
|
|
16133
|
-
const sourceRef = `refs/vo/repairs/${expectedHead}`;
|
|
16134
|
-
const env2 = githubToken ? githubGitAuthEnv2(githubToken) : void 0;
|
|
16135
|
-
await runCommand(
|
|
16136
|
-
"git",
|
|
16137
|
-
["fetch", "origin", `+refs/pull/${prNumber}/head:${sourceRef}`],
|
|
16138
|
-
worktreeDir,
|
|
16139
|
-
{ env: env2, timeout: 12e4 }
|
|
16140
|
-
);
|
|
16141
|
-
const fetchedHead = String(await runCommand(
|
|
16142
|
-
"git",
|
|
16143
|
-
["rev-parse", sourceRef],
|
|
16144
|
-
worktreeDir,
|
|
16145
|
-
{ env: env2, timeout: 3e4 }
|
|
16146
|
-
)).trim().toLowerCase();
|
|
16147
|
-
if (fetchedHead !== expectedHead) {
|
|
16148
|
-
throw new Error(`repair source head changed: admitted ${expectedHead}, fetched ${fetchedHead || "none"}`);
|
|
16149
|
-
}
|
|
16150
|
-
let conflicted = false;
|
|
16151
|
-
try {
|
|
16152
|
-
await runCommand(
|
|
16153
|
-
"git",
|
|
16154
|
-
["merge", "--squash", "--no-commit", sourceRef],
|
|
16155
|
-
worktreeDir,
|
|
16156
|
-
{ env: env2, timeout: 12e4 }
|
|
16157
|
-
);
|
|
16158
|
-
} catch (error) {
|
|
16159
|
-
const conflicts = String(await runCommand(
|
|
16160
|
-
"git",
|
|
16161
|
-
["diff", "--name-only", "--diff-filter=U"],
|
|
16162
|
-
worktreeDir,
|
|
16163
|
-
{ env: env2, timeout: 3e4 }
|
|
16164
|
-
)).trim();
|
|
16165
|
-
if (!conflicts) throw error;
|
|
16166
|
-
conflicted = true;
|
|
16167
|
-
}
|
|
16168
|
-
const status = String(await runCommand(
|
|
16169
|
-
"git",
|
|
16170
|
-
["status", "--porcelain"],
|
|
16171
|
-
worktreeDir,
|
|
16172
|
-
{ env: env2, timeout: 3e4 }
|
|
16173
|
-
)).trim();
|
|
16174
|
-
if (!status) {
|
|
16175
|
-
throw new Error(`repair source ${repo}#${prNumber}@${expectedHead} produced no changes on current main`);
|
|
16176
|
-
}
|
|
16177
|
-
return { sourceRef, headSha: expectedHead, conflicted };
|
|
16178
|
-
}
|
|
16179
|
-
var init_repair_source_materialization = __esm({
|
|
16180
|
-
"../../scripts/virtual-office/code-runner/repair-source-materialization.mjs"() {
|
|
16181
|
-
"use strict";
|
|
16182
|
-
init_process_runner2();
|
|
16183
|
-
init_github_git_auth_env();
|
|
16184
|
-
}
|
|
16185
|
-
});
|
|
16186
|
-
|
|
16187
16622
|
// ../../scripts/virtual-office/code-runner/task-worktree-preparation.mjs
|
|
16188
16623
|
async function prepareTaskWorktree({ client, task, cfg, safeProgress: safeProgress2, log: log2 }) {
|
|
16189
16624
|
const id = task.code_task_id;
|
|
@@ -16214,6 +16649,7 @@ async function prepareTaskWorktree({ client, task, cfg, safeProgress: safeProgre
|
|
|
16214
16649
|
githubToken,
|
|
16215
16650
|
allowAmbientGithubFallback: cfg.allowAmbientGithub
|
|
16216
16651
|
});
|
|
16652
|
+
let repairPlan = null;
|
|
16217
16653
|
if (continuationRestore) {
|
|
16218
16654
|
log2(`task ${id}: restored continuation branch ${continuationRestore.remoteBranch} into ${continuationRestore.localBranch}`);
|
|
16219
16655
|
} else if (task.repair_pr_number) {
|
|
@@ -16221,22 +16657,23 @@ async function prepareTaskWorktree({ client, task, cfg, safeProgress: safeProgre
|
|
|
16221
16657
|
"preparing_worktree",
|
|
16222
16658
|
`Materializing complete exact source PR #${task.repair_pr_number} before paid repair`
|
|
16223
16659
|
));
|
|
16224
|
-
|
|
16660
|
+
repairPlan = await planRepairPublication(wt.worktreeDir, {
|
|
16225
16661
|
repo: task.repo,
|
|
16226
16662
|
prNumber: task.repair_pr_number,
|
|
16227
16663
|
headSha: task.repair_head_sha,
|
|
16228
|
-
githubToken
|
|
16664
|
+
githubToken,
|
|
16665
|
+
log: (message) => log2(`task ${id}: ${message}`)
|
|
16229
16666
|
});
|
|
16230
|
-
log2(`task ${id}: materialized full repair source ${task.repo}#${task.repair_pr_number}@${
|
|
16667
|
+
log2(repairPlan.strategy === "in-place" ? `task ${id}: repairing ${task.repo}#${task.repair_pr_number}@${repairPlan.headSha} IN PLACE on ${repairPlan.remoteBranch}` : `task ${id}: materialized full repair source ${task.repo}#${task.repair_pr_number}@${repairPlan.headSha} for supersede (${repairPlan.reason})${repairPlan.conflicted ? " with conflicts for the agent to resolve" : ""}`);
|
|
16231
16668
|
}
|
|
16232
|
-
return { wt, githubToken, agentGithubReadToken, continuationRestore };
|
|
16669
|
+
return { wt, githubToken, agentGithubReadToken, continuationRestore, repairPlan };
|
|
16233
16670
|
}
|
|
16234
16671
|
var init_task_worktree_preparation = __esm({
|
|
16235
16672
|
"../../scripts/virtual-office/code-runner/task-worktree-preparation.mjs"() {
|
|
16236
16673
|
"use strict";
|
|
16237
16674
|
init_worktree_helper();
|
|
16238
16675
|
init_resume_branch();
|
|
16239
|
-
|
|
16676
|
+
init_repair_publication_strategy();
|
|
16240
16677
|
init_task_helpers();
|
|
16241
16678
|
}
|
|
16242
16679
|
});
|
|
@@ -16265,7 +16702,8 @@ async function processOneTask(client, task, cfg, runnerInstanceId, swarmAdmissio
|
|
|
16265
16702
|
wt,
|
|
16266
16703
|
githubToken,
|
|
16267
16704
|
agentGithubReadToken,
|
|
16268
|
-
continuationRestore
|
|
16705
|
+
continuationRestore,
|
|
16706
|
+
repairPlan
|
|
16269
16707
|
} = await prepareTaskWorktree({ client, task, cfg, safeProgress, log });
|
|
16270
16708
|
worktreeName = wt.worktreeName;
|
|
16271
16709
|
const canonicalBaseline = await captureCanonicalBaseline(wt.worktreeDir);
|
|
@@ -16400,6 +16838,7 @@ async function processOneTask(client, task, cfg, runnerInstanceId, swarmAdmissio
|
|
|
16400
16838
|
partial,
|
|
16401
16839
|
run,
|
|
16402
16840
|
maxTurns: effectiveMaxTurns,
|
|
16841
|
+
repairPlan,
|
|
16403
16842
|
worktreeDir: wt.worktreeDir,
|
|
16404
16843
|
githubToken,
|
|
16405
16844
|
safeProgress,
|
|
@@ -16410,7 +16849,7 @@ async function processOneTask(client, task, cfg, runnerInstanceId, swarmAdmissio
|
|
|
16410
16849
|
if (await taskWasCancelled({ client, id, run, safeProgress, log })) return;
|
|
16411
16850
|
if (await gateTestGenTaskOrFail({ client, id, task, files, worktreeDir: wt.worktreeDir, log })) return;
|
|
16412
16851
|
if (await enforceCompletionGateOrFail({ client, id, task, worktreeDir: wt.worktreeDir, run, truncated: partial, log })) return;
|
|
16413
|
-
const publicationTarget = await resolvePublicationTarget({ task, continuationRestore, worktreeDir: wt.worktreeDir, githubToken, allowAmbientGithubFallback: cfg.allowAmbientGithub });
|
|
16852
|
+
const publicationTarget = await resolvePublicationTarget({ task, continuationRestore, repairPlan, worktreeDir: wt.worktreeDir, githubToken, allowAmbientGithubFallback: cfg.allowAmbientGithub });
|
|
16414
16853
|
const localBranch = await resolveOrCreateBranchAsync(wt.worktreeDir, "vo/code-task");
|
|
16415
16854
|
const publicationBranch = publicationTarget.targetBranch || localBranch;
|
|
16416
16855
|
if (!await recordPublicationIntent({ client, id, branch: publicationBranch, safeProgress, log })) {
|
|
@@ -16419,7 +16858,7 @@ async function processOneTask(client, task, cfg, runnerInstanceId, swarmAdmissio
|
|
|
16419
16858
|
}
|
|
16420
16859
|
await safeProgress(client, id, runnerStagePatch("opening_pr", `opening PR for ${files.length} changed file(s)`));
|
|
16421
16860
|
log(`task ${id}: publishing with merge-owner=${mergeOwner(cfg)}`);
|
|
16422
|
-
const closesSource = publicationTarget.supersedesPrNumber ? `
|
|
16861
|
+
const closesSource = publicationTarget.supersedesPrNumber && publicationTarget.supersedesPrNumber !== publicationTarget.targetPrNumber ? `
|
|
16423
16862
|
|
|
16424
16863
|
Closes #${publicationTarget.supersedesPrNumber}` : "";
|
|
16425
16864
|
const pr = await openCodeTaskPrAsync(wt.worktreeDir, files, {
|
|
@@ -16431,8 +16870,8 @@ Closes #${publicationTarget.supersedesPrNumber}` : "";
|
|
|
16431
16870
|
draft: partial,
|
|
16432
16871
|
armAutoMerge: cfg.armGhAutoMerge,
|
|
16433
16872
|
// default OFF: the watcher waits for green CI, then uses the exact-SHA receipt-gated merge
|
|
16434
|
-
...publicationTarget
|
|
16435
|
-
deferSupersededPrCleanup:
|
|
16873
|
+
...publicationTarget
|
|
16874
|
+
// carries deferSupersededPrCleanup: a supersede now closes its predecessor at PUBLISH time (guarded by head SHA), never only at merge
|
|
16436
16875
|
});
|
|
16437
16876
|
if (await finalizePublishedPr({
|
|
16438
16877
|
client,
|