@algosuite/vo-mcp 0.2.0-beta.68 → 0.2.0-beta.69
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/agent-auth-probe-cli.mjs +42 -29
- package/dist/autostart-cli.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/install-cli.js.map +0 -1
- package/dist/login-cli.js.map +0 -1
- package/dist/pair-cli.js.map +0 -1
- package/dist/runner-cli.js +280 -63
- package/dist/runner-cli.js.map +3 -4
- package/dist/runner-supervisor.js +5 -1
- package/dist/runner-supervisor.js.map +1 -2
- package/dist/set-key-cli.js.map +0 -1
- package/dist/supervisor-credential-helper.js.map +0 -1
- package/dist/update-cli.js.map +0 -1
- package/package.json +1 -1
package/dist/runner-cli.js
CHANGED
|
@@ -1699,14 +1699,18 @@ async function addWorktreeWithRetry({
|
|
|
1699
1699
|
sleep: sleep3 = sleepMs,
|
|
1700
1700
|
removeDir = async (target) => {
|
|
1701
1701
|
await fsp2.rm(target, { recursive: true, force: true });
|
|
1702
|
-
}
|
|
1702
|
+
},
|
|
1703
|
+
baseRef = "origin/main"
|
|
1703
1704
|
}) {
|
|
1705
|
+
if (baseRef !== "origin/main" && !/^[0-9a-f]{40}$/u.test(baseRef)) {
|
|
1706
|
+
throw new Error("worktree base ref must be origin/main or an exact lowercase SHA");
|
|
1707
|
+
}
|
|
1704
1708
|
const maxAttempts = Math.max(1, Math.floor(attempts));
|
|
1705
1709
|
let result = null;
|
|
1706
1710
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
1707
1711
|
result = await runner(
|
|
1708
1712
|
"git",
|
|
1709
|
-
["worktree", "add", "-b", branchName, worktreeDir,
|
|
1713
|
+
["worktree", "add", "-b", branchName, worktreeDir, baseRef],
|
|
1710
1714
|
{ cwd: root, timeoutMs }
|
|
1711
1715
|
);
|
|
1712
1716
|
if (result.status === 0 && await pathExistsFn(worktreeDir)) {
|
|
@@ -2892,6 +2896,18 @@ async function alignCanonicalClone(root, options = {}) {
|
|
|
2892
2896
|
}
|
|
2893
2897
|
return { updated: true, headSha, originSha };
|
|
2894
2898
|
}
|
|
2899
|
+
async function fetchExactTaskBase(root, baseSha, options = {}) {
|
|
2900
|
+
if (!/^[0-9a-f]{40}$/u.test(baseSha)) {
|
|
2901
|
+
throw new Error("comparative base SHA must be exact lowercase hex");
|
|
2902
|
+
}
|
|
2903
|
+
const fetched = await git(root, ["fetch", "origin", baseSha], options);
|
|
2904
|
+
if (fetched.status !== 0) {
|
|
2905
|
+
throw new Error(`exact comparative source is unavailable: ${summarizeProcessFailure(fetched)}`);
|
|
2906
|
+
}
|
|
2907
|
+
const resolved = await gitText(root, ["rev-parse", `${baseSha}^{commit}`], options);
|
|
2908
|
+
if (resolved !== baseSha) throw new Error("exact comparative source did not resolve to the requested commit");
|
|
2909
|
+
return resolved;
|
|
2910
|
+
}
|
|
2895
2911
|
function shouldIgnoreManagedEntry(entryName) {
|
|
2896
2912
|
return IGNORED_MANAGED_ENTRIES.has(entryName) || entryName.endsWith(".lock") || entryName.startsWith("runner-");
|
|
2897
2913
|
}
|
|
@@ -2932,6 +2948,9 @@ async function prepareTaskRoot(root, options = {}) {
|
|
|
2932
2948
|
try {
|
|
2933
2949
|
await reportLegacyResiduals(root, options);
|
|
2934
2950
|
const alignment = await alignCanonicalClone(root, options);
|
|
2951
|
+
if (options.baseSha) {
|
|
2952
|
+
await fetchExactTaskBase(root, options.baseSha, options);
|
|
2953
|
+
}
|
|
2935
2954
|
const hydration = await ensurePnpmHydration(root, {
|
|
2936
2955
|
logger: options.logger,
|
|
2937
2956
|
runner: options.runner || runProcess
|
|
@@ -3206,10 +3225,16 @@ async function createFixWorktree(kind, error = {}, options = {}) {
|
|
|
3206
3225
|
const dependencyOwnership = createDependencyOwnershipTracker(worktreeDir);
|
|
3207
3226
|
const prep = await prepare(root, {
|
|
3208
3227
|
recoverDirtyCanonical: multiRepo,
|
|
3209
|
-
managedPool: path11.dirname(worktreeDir)
|
|
3228
|
+
managedPool: path11.dirname(worktreeDir),
|
|
3229
|
+
baseSha: options.baseSha || null
|
|
3210
3230
|
});
|
|
3211
3231
|
await processRunner("git", ["config", "core.longpaths", "true"], { cwd: root, timeoutMs: 3e4 });
|
|
3212
|
-
const add = await addWorktree({
|
|
3232
|
+
const add = await addWorktree({
|
|
3233
|
+
root,
|
|
3234
|
+
branchName,
|
|
3235
|
+
worktreeDir,
|
|
3236
|
+
baseRef: options.baseSha || "origin/main"
|
|
3237
|
+
});
|
|
3213
3238
|
if (!add.ok) {
|
|
3214
3239
|
const detail = describeGitFailure(add.result);
|
|
3215
3240
|
if (multiRepo) {
|
|
@@ -4694,6 +4719,7 @@ function finalizeAgentTaskResult({
|
|
|
4694
4719
|
result,
|
|
4695
4720
|
bin,
|
|
4696
4721
|
stderrTail,
|
|
4722
|
+
classifyStderr = null,
|
|
4697
4723
|
timedOut,
|
|
4698
4724
|
killed,
|
|
4699
4725
|
cancelReason,
|
|
@@ -4724,8 +4750,16 @@ function finalizeAgentTaskResult({
|
|
|
4724
4750
|
if (code !== 0 || signal) summary = fallback || `${bin} exited ${signal || code}`;
|
|
4725
4751
|
else summary = fallback || `${bin} exited without terminal result`;
|
|
4726
4752
|
}
|
|
4753
|
+
let failure = result.failure ?? null;
|
|
4754
|
+
if (!failure && !sawTerminalResult && (code !== 0 || signal) && typeof classifyStderr === "function") {
|
|
4755
|
+
try {
|
|
4756
|
+
failure = classifyStderr(stderrTail);
|
|
4757
|
+
} catch {
|
|
4758
|
+
}
|
|
4759
|
+
}
|
|
4727
4760
|
return {
|
|
4728
4761
|
...result,
|
|
4762
|
+
...failure ? { failure } : {},
|
|
4729
4763
|
ok: sawTerminalResult && result.ok && (code === 0 || forcedAfterResult),
|
|
4730
4764
|
summary: augmentSummary(summary)
|
|
4731
4765
|
};
|
|
@@ -5650,7 +5684,8 @@ function runAgentTask({
|
|
|
5650
5684
|
// parser would work, the runner would report null, and the feature
|
|
5651
5685
|
// would measure nothing while every test passed.
|
|
5652
5686
|
tokenUsage: evt.tokenUsage ?? result.tokenUsage ?? null,
|
|
5653
|
-
modelUsage: evt.modelUsage ?? result.modelUsage ?? null
|
|
5687
|
+
modelUsage: evt.modelUsage ?? result.modelUsage ?? null,
|
|
5688
|
+
failure: evt.failure ?? result.failure ?? null
|
|
5654
5689
|
};
|
|
5655
5690
|
terminalCleanupTimer ??= armTerminalCleanup({
|
|
5656
5691
|
child,
|
|
@@ -5679,6 +5714,7 @@ function runAgentTask({
|
|
|
5679
5714
|
result,
|
|
5680
5715
|
bin,
|
|
5681
5716
|
stderrTail,
|
|
5717
|
+
classifyStderr: runner.classifyStderr,
|
|
5682
5718
|
timedOut,
|
|
5683
5719
|
killed,
|
|
5684
5720
|
cancelReason,
|
|
@@ -5803,26 +5839,15 @@ var init_claude_runner = __esm({
|
|
|
5803
5839
|
if (process.platform !== "win32") return { bin, args, spawnOptions: spawnOptions ?? this.getSpawnOptions() };
|
|
5804
5840
|
return buildWindowsClaudeLaunch({ bin, args, env: env2 });
|
|
5805
5841
|
}
|
|
5806
|
-
/**
|
|
5807
|
-
* Fill ANTHROPIC_API_KEY from the OS keychain when not already set (M4 BYO),
|
|
5808
|
-
* so a friend who ran `vo-mcp set-key` authenticates without an env var.
|
|
5809
|
-
* Explicit env wins; no key stored → unchanged (Claude Code login as before).
|
|
5810
|
-
*/
|
|
5811
5842
|
applyAuthEnv(env2 = process.env) {
|
|
5812
5843
|
return withAnthropicKey(env2);
|
|
5813
5844
|
}
|
|
5814
5845
|
costBasis(env2 = process.env) {
|
|
5815
5846
|
return claudeCostBasis(env2);
|
|
5816
5847
|
}
|
|
5817
|
-
/** Describe which Anthropic auth source the spawn will use (for runner logs). */
|
|
5818
5848
|
describeAuth(env2 = process.env) {
|
|
5819
5849
|
return describeAnthropicAuthSource(env2);
|
|
5820
5850
|
}
|
|
5821
|
-
/**
|
|
5822
|
-
* Best-effort auth check: is `claude` on PATH and can we verify login?
|
|
5823
|
-
* Never throws. If we can't cheaply detect auth, we return installed:true
|
|
5824
|
-
* and let the real spawn fail with a clearer error from the CLI itself.
|
|
5825
|
-
*/
|
|
5826
5851
|
async checkAuth() {
|
|
5827
5852
|
return checkClaudeAuth();
|
|
5828
5853
|
}
|
|
@@ -5948,6 +5973,66 @@ var init_flat_token_usage = __esm({
|
|
|
5948
5973
|
}
|
|
5949
5974
|
});
|
|
5950
5975
|
|
|
5976
|
+
// ../../scripts/virtual-office/code-runner/error-message.mjs
|
|
5977
|
+
function boundedErrorMessage(error, maxLength = 240) {
|
|
5978
|
+
try {
|
|
5979
|
+
return String(error?.message ?? error ?? "unknown error").slice(0, maxLength);
|
|
5980
|
+
} catch {
|
|
5981
|
+
return "unknown error";
|
|
5982
|
+
}
|
|
5983
|
+
}
|
|
5984
|
+
function codexFailure(code, fields = {}) {
|
|
5985
|
+
const definition = FAILURE_DEFINITIONS[code];
|
|
5986
|
+
return {
|
|
5987
|
+
schema: FAILURE_SCHEMA,
|
|
5988
|
+
agent: "codex",
|
|
5989
|
+
source: definition.source,
|
|
5990
|
+
code,
|
|
5991
|
+
...fields,
|
|
5992
|
+
operator_next_action: definition.operator_next_action
|
|
5993
|
+
};
|
|
5994
|
+
}
|
|
5995
|
+
function classifyCodexTerminalFailure(evt = {}) {
|
|
5996
|
+
const error = evt && typeof evt.error === "object" && evt.error !== null ? evt.error : null;
|
|
5997
|
+
if (!/^The ['"][^'"]+['"] model is not supported when using Codex with a ChatGPT account\.$/u.test(
|
|
5998
|
+
typeof error?.message === "string" ? error.message : ""
|
|
5999
|
+
)) return null;
|
|
6000
|
+
const status = Number(evt.status ?? error.status);
|
|
6001
|
+
return codexFailure("model_unsupported_for_account", Number.isInteger(status) ? { provider_status: status } : {});
|
|
6002
|
+
}
|
|
6003
|
+
function classifyCodexStderr(stderr = "") {
|
|
6004
|
+
if (!/(?:^|\n)\s*windows sandbox(?: failed)?: runner error:.*\bCreateProcessAsUserW failed:\s*1312\b/imu.test(String(stderr))) return null;
|
|
6005
|
+
return codexFailure("codex_runtime_launch_logon_session", { win32_error: 1312 });
|
|
6006
|
+
}
|
|
6007
|
+
function serializeRunnerFailure(record, maxLength = 2e3) {
|
|
6008
|
+
const definition = record && FAILURE_DEFINITIONS[record.code];
|
|
6009
|
+
if (!definition || record.schema !== FAILURE_SCHEMA || record.source !== definition.source) return null;
|
|
6010
|
+
const value = codexFailure(record.code, {
|
|
6011
|
+
...record.code === "model_unsupported_for_account" && Number.isInteger(record.provider_status) ? { provider_status: record.provider_status } : {},
|
|
6012
|
+
...definition.win32_error ? { win32_error: definition.win32_error } : {}
|
|
6013
|
+
});
|
|
6014
|
+
const serialized2 = JSON.stringify(value);
|
|
6015
|
+
return serialized2.length <= Math.max(0, maxLength) ? serialized2 : null;
|
|
6016
|
+
}
|
|
6017
|
+
var FAILURE_SCHEMA, FAILURE_DEFINITIONS;
|
|
6018
|
+
var init_error_message = __esm({
|
|
6019
|
+
"../../scripts/virtual-office/code-runner/error-message.mjs"() {
|
|
6020
|
+
"use strict";
|
|
6021
|
+
FAILURE_SCHEMA = "vo.runner_failure.v1";
|
|
6022
|
+
FAILURE_DEFINITIONS = {
|
|
6023
|
+
model_unsupported_for_account: {
|
|
6024
|
+
source: "terminal_event",
|
|
6025
|
+
operator_next_action: "Create a new owner-reviewed task with an account-supported Codex model; keep the preserved draft PR for review rather than resuming this task."
|
|
6026
|
+
},
|
|
6027
|
+
codex_runtime_launch_logon_session: {
|
|
6028
|
+
source: "stderr",
|
|
6029
|
+
win32_error: 1312,
|
|
6030
|
+
operator_next_action: "Repair the Codex Windows logon/session configuration on the serving host, then rerun the runner."
|
|
6031
|
+
}
|
|
6032
|
+
};
|
|
6033
|
+
}
|
|
6034
|
+
});
|
|
6035
|
+
|
|
5951
6036
|
// ../../scripts/virtual-office/code-runner/codex-runner.mjs
|
|
5952
6037
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
5953
6038
|
import { existsSync as existsSync10 } from "node:fs";
|
|
@@ -6048,7 +6133,8 @@ function parseCodexEvent(line) {
|
|
|
6048
6133
|
}
|
|
6049
6134
|
if (type === "turn.failed" || type === "error") {
|
|
6050
6135
|
const msg = evt.error && (evt.error.message || evt.error) || evt.message || "codex run failed";
|
|
6051
|
-
|
|
6136
|
+
const failure = classifyCodexTerminalFailure(evt);
|
|
6137
|
+
return { kind: "result", isError: true, costUsd: null, summary: String(msg), numTurns: null, ...failure ? { failure } : {} };
|
|
6052
6138
|
}
|
|
6053
6139
|
return null;
|
|
6054
6140
|
}
|
|
@@ -6059,6 +6145,7 @@ var init_codex_runner = __esm({
|
|
|
6059
6145
|
init_agent_key_store();
|
|
6060
6146
|
init_agent_auth_tier();
|
|
6061
6147
|
init_flat_token_usage();
|
|
6148
|
+
init_error_message();
|
|
6062
6149
|
CODEX_PREFER_LOGIN_ENV = "VO_RUNNER_CODEX_PREFER_LOGIN";
|
|
6063
6150
|
LEGACY_PREFER_LOGIN_ENV = "VO_RUNNER_PREFER_LOGIN";
|
|
6064
6151
|
CodexRunner = class {
|
|
@@ -6076,6 +6163,9 @@ var init_codex_runner = __esm({
|
|
|
6076
6163
|
parseEvent(line) {
|
|
6077
6164
|
return parseCodexEvent(line);
|
|
6078
6165
|
}
|
|
6166
|
+
classifyStderr(stderr) {
|
|
6167
|
+
return classifyCodexStderr(stderr);
|
|
6168
|
+
}
|
|
6079
6169
|
/**
|
|
6080
6170
|
* SECURITY: never `shell: true` — same RCE class as cursor-runner. The old
|
|
6081
6171
|
* `shell: win32 && !/\.exe$/` fell back to shell mode whenever
|
|
@@ -6097,12 +6187,6 @@ var init_codex_runner = __esm({
|
|
|
6097
6187
|
windowsVerbatimArguments: false
|
|
6098
6188
|
};
|
|
6099
6189
|
}
|
|
6100
|
-
/**
|
|
6101
|
-
* Fill the OpenAI credential env var(s) (OPENAI_API_KEY / CODEX_API_KEY) from
|
|
6102
|
-
* the OS keychain when not already set, so a BYO friend who ran
|
|
6103
|
-
* `vo-mcp set-key --provider codex` authenticates without an env var. Explicit
|
|
6104
|
-
* env wins; no key stored → unchanged (a prior `codex login` still works).
|
|
6105
|
-
*/
|
|
6106
6190
|
applyAuthEnv(env2 = process.env) {
|
|
6107
6191
|
if (isTruthyFlag2(env2[CODEX_PREFER_LOGIN_ENV]) || isTruthyFlag2(env2[LEGACY_PREFER_LOGIN_ENV])) {
|
|
6108
6192
|
const out = { ...env2 };
|
|
@@ -6115,20 +6199,9 @@ var init_codex_runner = __esm({
|
|
|
6115
6199
|
costBasis(env2 = process.env) {
|
|
6116
6200
|
return String(env2.OPENAI_API_KEY || env2.CODEX_API_KEY || "").trim() ? "vendor_billed" : "subscription_api_equivalent";
|
|
6117
6201
|
}
|
|
6118
|
-
/**
|
|
6119
|
-
* Dispatch-time billing tier for the NEXT codex spawn, from the SAME facts
|
|
6120
|
-
* checkAuth() already gathered — no extra subprocess. applyAuthEnv() is a
|
|
6121
|
-
* keychain read in this process (@napi-rs/keyring), not a spawn.
|
|
6122
|
-
*
|
|
6123
|
-
* Codex is the agent where this signal was already computed and then thrown
|
|
6124
|
-
* away: checkAuth() distinguished "API key available (no persisted ChatGPT
|
|
6125
|
-
* login)" from a real login, but only inside a `message` string that the
|
|
6126
|
-
* heartbeat schema strips before storage. This gives that fact a typed home.
|
|
6127
|
-
*/
|
|
6128
6202
|
authTier(env2 = this.env) {
|
|
6129
6203
|
return safeAuthTier(() => authTierFromCostBasis(this.costBasis(this.applyAuthEnv(env2))));
|
|
6130
6204
|
}
|
|
6131
|
-
/** Best-effort binary + persisted-login probe. Never throws or spends tokens. */
|
|
6132
6205
|
async checkAuth() {
|
|
6133
6206
|
try {
|
|
6134
6207
|
const bin = this.binary;
|
|
@@ -7228,6 +7301,17 @@ async function classifyFailureForResume({
|
|
|
7228
7301
|
record = recordRateLimited,
|
|
7229
7302
|
deferRecord = false
|
|
7230
7303
|
} = {}) {
|
|
7304
|
+
const structuredFailure = serializeRunnerFailure(run.failure);
|
|
7305
|
+
if (structuredFailure) {
|
|
7306
|
+
return {
|
|
7307
|
+
rateLimited: false,
|
|
7308
|
+
progress: {
|
|
7309
|
+
status: "failed",
|
|
7310
|
+
message: `runner blocker: ${run.failure.code}; operator review required`,
|
|
7311
|
+
result: structuredFailure
|
|
7312
|
+
}
|
|
7313
|
+
};
|
|
7314
|
+
}
|
|
7231
7315
|
if (enabled) {
|
|
7232
7316
|
const rl = detect(run.summary, { now });
|
|
7233
7317
|
if (rl.rateLimited) {
|
|
@@ -7302,6 +7386,7 @@ var init_rate_limit_resume = __esm({
|
|
|
7302
7386
|
"use strict";
|
|
7303
7387
|
init_rate_limit_detector_core();
|
|
7304
7388
|
init_rate_limit_resume_state();
|
|
7389
|
+
init_error_message();
|
|
7305
7390
|
}
|
|
7306
7391
|
});
|
|
7307
7392
|
|
|
@@ -8648,16 +8733,28 @@ var init_repair_publication_strategy = __esm({
|
|
|
8648
8733
|
|
|
8649
8734
|
// ../../scripts/virtual-office/code-runner/partial-pr-continuation.mjs
|
|
8650
8735
|
function partialPrContinuationResult(run = {}, maxLength = 2e3, reason = null) {
|
|
8736
|
+
const limit = Math.max(0, Number(maxLength) || 0);
|
|
8651
8737
|
const summary = String(run.summary || "agent stopped before completing the task").trim();
|
|
8652
8738
|
const reasonMarker = reason === "rate_limited" ? `
|
|
8653
8739
|
${RATE_LIMITED_CONTINUATION_MARKER}` : "";
|
|
8654
|
-
|
|
8655
|
-
${
|
|
8740
|
+
const blockedMarker = "ALGOSUITE_TASK_OUTCOME: BLOCKED";
|
|
8741
|
+
const blockedPrefix = `${blockedMarker}${reasonMarker}
|
|
8742
|
+
`;
|
|
8743
|
+
const serializedFailure = serializeRunnerFailure(run.failure, Math.max(0, limit - blockedPrefix.length - 1));
|
|
8744
|
+
const outcomeMarker = serializedFailure ? blockedMarker : PARTIAL_PR_CONTINUATION_MARKER;
|
|
8745
|
+
const prefix = `${outcomeMarker}${reasonMarker}
|
|
8746
|
+
`;
|
|
8747
|
+
const failure = serializedFailure && outcomeMarker === blockedMarker ? serializedFailure : null;
|
|
8748
|
+
const suffix = failure ? `
|
|
8749
|
+
${failure}` : "";
|
|
8750
|
+
const summaryRoom = Math.max(0, limit - prefix.length - suffix.length);
|
|
8751
|
+
return `${prefix}${summary.slice(0, summaryRoom)}${suffix}`.slice(0, limit);
|
|
8656
8752
|
}
|
|
8657
8753
|
var PARTIAL_PR_CONTINUATION_MARKER, RATE_LIMITED_CONTINUATION_MARKER;
|
|
8658
8754
|
var init_partial_pr_continuation = __esm({
|
|
8659
8755
|
"../../scripts/virtual-office/code-runner/partial-pr-continuation.mjs"() {
|
|
8660
8756
|
"use strict";
|
|
8757
|
+
init_error_message();
|
|
8661
8758
|
PARTIAL_PR_CONTINUATION_MARKER = "ALGOSUITE_TASK_OUTCOME: NEEDS_CONTINUATION";
|
|
8662
8759
|
RATE_LIMITED_CONTINUATION_MARKER = "ALGOSUITE_CONTINUATION_REASON: RATE_LIMITED";
|
|
8663
8760
|
}
|
|
@@ -12015,20 +12112,6 @@ var init_pr_watcher_failure_confirmation = __esm({
|
|
|
12015
12112
|
}
|
|
12016
12113
|
});
|
|
12017
12114
|
|
|
12018
|
-
// ../../scripts/virtual-office/code-runner/error-message.mjs
|
|
12019
|
-
function boundedErrorMessage(error, maxLength = 240) {
|
|
12020
|
-
try {
|
|
12021
|
-
return String(error?.message ?? error ?? "unknown error").slice(0, maxLength);
|
|
12022
|
-
} catch {
|
|
12023
|
-
return "unknown error";
|
|
12024
|
-
}
|
|
12025
|
-
}
|
|
12026
|
-
var init_error_message = __esm({
|
|
12027
|
-
"../../scripts/virtual-office/code-runner/error-message.mjs"() {
|
|
12028
|
-
"use strict";
|
|
12029
|
-
}
|
|
12030
|
-
});
|
|
12031
|
-
|
|
12032
12115
|
// ../../scripts/virtual-office/code-runner/watcher-coordination.mjs
|
|
12033
12116
|
import { createHash as createHash7 } from "node:crypto";
|
|
12034
12117
|
function ciFixOccurrenceKey({ repo, prNumber, headSha, repairAttempt }) {
|
|
@@ -12524,7 +12607,49 @@ var init_superseded_pr_source = __esm({
|
|
|
12524
12607
|
});
|
|
12525
12608
|
|
|
12526
12609
|
// ../../scripts/virtual-office/code-runner/ci-fix-prompt.mjs
|
|
12527
|
-
function
|
|
12610
|
+
function fairSplit(aLen, bLen, budget) {
|
|
12611
|
+
if (aLen + bLen <= budget) return { a: aLen, b: bLen };
|
|
12612
|
+
const half = Math.floor(budget / 2);
|
|
12613
|
+
if (aLen <= half) return { a: aLen, b: budget - aLen };
|
|
12614
|
+
if (bLen <= half) return { a: budget - bLen, b: bLen };
|
|
12615
|
+
return { a: half, b: budget - half };
|
|
12616
|
+
}
|
|
12617
|
+
function boundHead(content, allocated, label) {
|
|
12618
|
+
if (content.length <= allocated) return content;
|
|
12619
|
+
const marker = `
|
|
12620
|
+
[${label} truncated to the first ${allocated} characters available in this dispatch]`;
|
|
12621
|
+
if (marker.length >= allocated) return marker.slice(0, Math.max(0, allocated));
|
|
12622
|
+
return `${content.slice(0, allocated - marker.length)}${marker}`;
|
|
12623
|
+
}
|
|
12624
|
+
function boundTail(content, allocated, label) {
|
|
12625
|
+
if (content.length <= allocated) return content;
|
|
12626
|
+
const marker = `[${label} truncated to the final ${allocated} characters available in this dispatch]
|
|
12627
|
+
`;
|
|
12628
|
+
if (marker.length >= allocated) return marker.slice(0, Math.max(0, allocated));
|
|
12629
|
+
return `${marker}${content.slice(-(allocated - marker.length))}`;
|
|
12630
|
+
}
|
|
12631
|
+
function boundedEvidenceShares({
|
|
12632
|
+
prPatch,
|
|
12633
|
+
failedLogs,
|
|
12634
|
+
fixedOverheadChars,
|
|
12635
|
+
prNumber,
|
|
12636
|
+
maxTotalChars = CI_FIX_PROMPT_MAX_CHARS
|
|
12637
|
+
}) {
|
|
12638
|
+
const patchWanted = String(prPatch || "").trim() || NO_PATCH_EVIDENCE;
|
|
12639
|
+
const logsWanted = String(failedLogs || "").trim() || NO_LOG_EVIDENCE;
|
|
12640
|
+
const available = maxTotalChars - fixedOverheadChars;
|
|
12641
|
+
if (available < NO_PATCH_EVIDENCE.length + NO_LOG_EVIDENCE.length) {
|
|
12642
|
+
throw new Error(
|
|
12643
|
+
`CI-fix prompt for PR #${prNumber ?? "?"} cannot fit its mandatory instructions and identity (${fixedOverheadChars} chars) plus even an empty-evidence notice inside the ${maxTotalChars}-character dispatch limit \u2014 refusing to send a malformed or instructions-truncated request`
|
|
12644
|
+
);
|
|
12645
|
+
}
|
|
12646
|
+
const { a: patchShare, b: logsShare } = fairSplit(patchWanted.length, logsWanted.length, available);
|
|
12647
|
+
return {
|
|
12648
|
+
patchBody: boundHead(patchWanted, patchShare, "PR patch"),
|
|
12649
|
+
logsBody: boundTail(logsWanted, logsShare, "failed-job evidence")
|
|
12650
|
+
};
|
|
12651
|
+
}
|
|
12652
|
+
function renderHeader({ prNumber, repo, branch, headSha, failedChecks }) {
|
|
12528
12653
|
return [
|
|
12529
12654
|
`${CI_FIX_MARKER} An AlgoHQ-dispatched pull request has FAILING CI and needs a fix.`,
|
|
12530
12655
|
"",
|
|
@@ -12543,24 +12668,45 @@ function buildCiFixPrompt({ prNumber, repo, branch, headSha, failedChecks, prPat
|
|
|
12543
12668
|
"VO-ALLOW-PR-OVERLAP marker. Your work ships: finish the fix completely, leave UNCOMMITTED",
|
|
12544
12669
|
"edits, and the runner publishes for you. A denied git/gh is EXPECTED; do NOT retry it.",
|
|
12545
12670
|
"",
|
|
12546
|
-
HEADLESS_EXECUTION_CONTRACT
|
|
12671
|
+
HEADLESS_EXECUTION_CONTRACT
|
|
12672
|
+
].join("\n");
|
|
12673
|
+
}
|
|
12674
|
+
function assemblePrompt(header, patchBody, logsBody) {
|
|
12675
|
+
return [
|
|
12676
|
+
header,
|
|
12547
12677
|
"",
|
|
12548
12678
|
"## Bounded source PR patch excerpt",
|
|
12549
12679
|
"```diff",
|
|
12550
|
-
|
|
12680
|
+
patchBody,
|
|
12551
12681
|
"```",
|
|
12552
12682
|
"",
|
|
12553
12683
|
"## Failed-job evidence",
|
|
12554
12684
|
"```text",
|
|
12555
|
-
|
|
12685
|
+
logsBody,
|
|
12556
12686
|
"```"
|
|
12557
12687
|
].join("\n");
|
|
12558
12688
|
}
|
|
12689
|
+
function buildCiFixPrompt({ prNumber, repo, branch, headSha, failedChecks, prPatch, failedLogs }) {
|
|
12690
|
+
const header = renderHeader({ prNumber, repo, branch, headSha, failedChecks });
|
|
12691
|
+
const fixedOverheadChars = assemblePrompt(header, "", "").length;
|
|
12692
|
+
const { patchBody, logsBody } = boundedEvidenceShares({
|
|
12693
|
+
prPatch,
|
|
12694
|
+
failedLogs,
|
|
12695
|
+
fixedOverheadChars,
|
|
12696
|
+
prNumber,
|
|
12697
|
+
maxTotalChars: CI_FIX_PROMPT_MAX_CHARS
|
|
12698
|
+
});
|
|
12699
|
+
return assemblePrompt(header, patchBody, logsBody);
|
|
12700
|
+
}
|
|
12701
|
+
var CI_FIX_PROMPT_MAX_CHARS, NO_PATCH_EVIDENCE, NO_LOG_EVIDENCE;
|
|
12559
12702
|
var init_ci_fix_prompt = __esm({
|
|
12560
12703
|
"../../scripts/virtual-office/code-runner/ci-fix-prompt.mjs"() {
|
|
12561
12704
|
"use strict";
|
|
12562
12705
|
init_superseded_pr_source();
|
|
12563
12706
|
init_headless_execution_contract();
|
|
12707
|
+
CI_FIX_PROMPT_MAX_CHARS = 9900;
|
|
12708
|
+
NO_PATCH_EVIDENCE = "[No excerpt available; the complete exact source is still materialized in the worktree.]";
|
|
12709
|
+
NO_LOG_EVIDENCE = "[No failed-job log was available; use the named checks and patch.]";
|
|
12564
12710
|
}
|
|
12565
12711
|
});
|
|
12566
12712
|
|
|
@@ -16067,7 +16213,11 @@ async function finalizePublishedPr({
|
|
|
16067
16213
|
const overlapBlocked = pr.overlapDraft === true;
|
|
16068
16214
|
const blockedRefs = Array.isArray(pr.overlapBlockedBy) && pr.overlapBlockedBy.length > 0 ? pr.overlapBlockedBy.map((n) => `#${n}`).join(", ") : "unresolved (see PR body for the gate report)";
|
|
16069
16215
|
const overlapNote = overlapBlocked ? ` as DRAFT \u2014 overlap-blocked by ${blockedRefs} (finished work preserved; mark ready after the overlap is resolved)` : "";
|
|
16070
|
-
const
|
|
16216
|
+
const serializedRunnerFailure = serializeRunnerFailure(run?.failure);
|
|
16217
|
+
const runnerFailure = Boolean(serializedRunnerFailure);
|
|
16218
|
+
const runnerFailureRecord = serializedRunnerFailure ? JSON.parse(serializedRunnerFailure) : null;
|
|
16219
|
+
const runnerFailureMessage = runnerFailureRecord ? `blocked by ${runnerFailureRecord.code}; operator review required: ${runnerFailureRecord.operator_next_action}` : "";
|
|
16220
|
+
const fixDispatchGuard = overlapBlocked || runnerFailure ? { allowFixDispatch: false } : {};
|
|
16071
16221
|
let resumeQueued = false;
|
|
16072
16222
|
if (cfg.watchEnabled) {
|
|
16073
16223
|
try {
|
|
@@ -16078,7 +16228,7 @@ async function finalizePublishedPr({
|
|
|
16078
16228
|
taskId: id,
|
|
16079
16229
|
operatorId: task.operator_id,
|
|
16080
16230
|
tenantId: task.tenant_id,
|
|
16081
|
-
needsContinuation: partial && !rateLimitResume && (task.continuation_attempt ?? 0) < (task.continuation_max_attempts ?? 3),
|
|
16231
|
+
needsContinuation: partial && !rateLimitResume && !runnerFailure && (task.continuation_attempt ?? 0) < (task.continuation_max_attempts ?? 3),
|
|
16082
16232
|
continuationExhausted: partial && (task.continuation_attempt ?? 0) >= (task.continuation_max_attempts ?? 3),
|
|
16083
16233
|
repairChain: task.repair_chain ?? {
|
|
16084
16234
|
root_pr_number: pr.prNumber,
|
|
@@ -16135,15 +16285,15 @@ async function finalizePublishedPr({
|
|
|
16135
16285
|
safeProgress: safeProgress2,
|
|
16136
16286
|
log: log2,
|
|
16137
16287
|
patch: {
|
|
16138
|
-
status: "pr_opened",
|
|
16139
|
-
message: `opened ${pr.prUrl}${overlapNote}${pr.newCommit === false ? " (no new commits \u2014 the branch already held every change)" : ""}`,
|
|
16288
|
+
status: runnerFailure ? "failed" : "pr_opened",
|
|
16289
|
+
message: runnerFailure ? `${runnerFailureMessage}; preserved PR ${pr.prUrl} for owner review` : `opened ${pr.prUrl}${overlapNote}${pr.newCommit === false ? " (no new commits \u2014 the branch already held every change)" : ""}`,
|
|
16140
16290
|
pr_url: pr.prUrl,
|
|
16141
16291
|
pr_number: pr.prNumber,
|
|
16142
16292
|
pr_branch: pr.branch,
|
|
16143
16293
|
result: (() => {
|
|
16144
16294
|
const prefix = overlapBlocked ? `[VO-PUBLISH-OVERLAP-BLOCKED: ${blockedRefs}] ` : "";
|
|
16145
16295
|
const room = 2e3 - prefix.length;
|
|
16146
|
-
return `${prefix}${partial ? partialPrContinuationResult(run, room, rateLimitResume ? "rate_limited" : null) : String(run.summary).slice(0, room)}`;
|
|
16296
|
+
return `${prefix}${partial || runnerFailure ? partialPrContinuationResult(run, room, rateLimitResume ? "rate_limited" : null) : String(run.summary).slice(0, room)}`;
|
|
16147
16297
|
})(),
|
|
16148
16298
|
...runOutcomePatch(run),
|
|
16149
16299
|
...terminalLedgerPatch(run)
|
|
@@ -16187,6 +16337,7 @@ var init_publication_outcome = __esm({
|
|
|
16187
16337
|
init_outcome_commit();
|
|
16188
16338
|
init_terminal_delivery();
|
|
16189
16339
|
init_rate_limit_resume();
|
|
16340
|
+
init_error_message();
|
|
16190
16341
|
defaultRunCommand4 = (cmd, args, cwd, opts = {}) => runProcess2(cmd, args, { cwd, ...opts });
|
|
16191
16342
|
}
|
|
16192
16343
|
});
|
|
@@ -16524,7 +16675,16 @@ function isMaxTurnExhaustion(run = {}, maxTurns) {
|
|
|
16524
16675
|
return Number.isInteger(maxTurns) && maxTurns > 0 && Number.isInteger(run.numTurns) && run.numTurns > maxTurns;
|
|
16525
16676
|
}
|
|
16526
16677
|
function decideNoChangesTerminalStatus({ partial, run = {}, maxTurns } = {}) {
|
|
16678
|
+
const structuredFailure = serializeRunnerFailure(run.failure);
|
|
16527
16679
|
if (!partial) {
|
|
16680
|
+
if (structuredFailure) {
|
|
16681
|
+
const failure = JSON.parse(structuredFailure);
|
|
16682
|
+
return {
|
|
16683
|
+
status: "failed",
|
|
16684
|
+
message: `agent reported a runner blocker with no file changes \u2014 ${failure.code}; ${failure.operator_next_action}`,
|
|
16685
|
+
result: partialPrContinuationResult(run, RESULT_LIMIT)
|
|
16686
|
+
};
|
|
16687
|
+
}
|
|
16528
16688
|
if (reportsBlocker(run.summary)) {
|
|
16529
16689
|
return {
|
|
16530
16690
|
status: "failed",
|
|
@@ -16548,8 +16708,8 @@ function decideNoChangesTerminalStatus({ partial, run = {}, maxTurns } = {}) {
|
|
|
16548
16708
|
const cause = String(run.summary || "").trim();
|
|
16549
16709
|
return {
|
|
16550
16710
|
status: "failed",
|
|
16551
|
-
message: cause ? `agent made no file changes \u2014 ${cause.slice(0, 200)}` : "agent made no file changes",
|
|
16552
|
-
result: (cause || "no_changes").slice(0, RESULT_LIMIT)
|
|
16711
|
+
message: structuredFailure ? `agent made no file changes \u2014 ${JSON.parse(structuredFailure).code}; ${JSON.parse(structuredFailure).operator_next_action}` : cause ? `agent made no file changes \u2014 ${cause.slice(0, 200)}` : "agent made no file changes",
|
|
16712
|
+
result: structuredFailure ? partialPrContinuationResult(run, RESULT_LIMIT) : (cause || "no_changes").slice(0, RESULT_LIMIT)
|
|
16553
16713
|
};
|
|
16554
16714
|
}
|
|
16555
16715
|
async function closeSupersededSourceOnNoChanges({
|
|
@@ -16682,6 +16842,8 @@ var init_no_changes_terminal_status = __esm({
|
|
|
16682
16842
|
init_terminal_ledger_patch();
|
|
16683
16843
|
init_outcome_commit();
|
|
16684
16844
|
init_terminal_delivery();
|
|
16845
|
+
init_error_message();
|
|
16846
|
+
init_partial_pr_continuation();
|
|
16685
16847
|
RESULT_LIMIT = 2e3;
|
|
16686
16848
|
}
|
|
16687
16849
|
});
|
|
@@ -16999,6 +17161,50 @@ var init_daemon_config = __esm({
|
|
|
16999
17161
|
});
|
|
17000
17162
|
|
|
17001
17163
|
// ../../scripts/virtual-office/code-runner/task-worktree-preparation.mjs
|
|
17164
|
+
async function git3(worktreeDir, args, runCommand = runProcess2) {
|
|
17165
|
+
const result = await runCommand("git", args, { cwd: worktreeDir, timeoutMs: 6e4 });
|
|
17166
|
+
if (result.status !== 0) throw new Error(`source binding git ${args[0]} failed before model execution`);
|
|
17167
|
+
return String(result.stdout || "").trim();
|
|
17168
|
+
}
|
|
17169
|
+
function hasAffirmativePreSpawnNoSpendEvidence(parentTask) {
|
|
17170
|
+
return Boolean(parentTask && parentTask.status === "failed" && parentTask.completed_at && parentTask.execution_started_at === null && parentTask.usage_reported_at && parentTask.cost_usd === 0 && parentTask.cost_basis === "no_agent_spawned" && typeof parentTask.claimed_by === "string" && parentTask.claimed_by.length > 0 && RUNNER_INSTANCE_ID.test(parentTask.runner_instance_id ?? "") && parentTask.usage_runner_instance_id === parentTask.runner_instance_id && !parentTask.token_usage && !parentTask.model_usage && !parentTask.detached_run_economics?.length && !parentTask.resumed_from && !parentTask.pr_branch && !parentTask.pr_url && !parentTask.pr_number);
|
|
17171
|
+
}
|
|
17172
|
+
async function bindTaskExecutionSource({
|
|
17173
|
+
client,
|
|
17174
|
+
task,
|
|
17175
|
+
parentTask,
|
|
17176
|
+
worktreeDir,
|
|
17177
|
+
continuationRestore,
|
|
17178
|
+
runCommand = runProcess2
|
|
17179
|
+
}) {
|
|
17180
|
+
const baseSha = task.comparative_base_sha;
|
|
17181
|
+
if (!baseSha) return null;
|
|
17182
|
+
if (!EXACT_SHA.test(baseSha)) throw new Error("comparative base SHA is invalid");
|
|
17183
|
+
const actualSha = await git3(worktreeDir, ["rev-parse", "HEAD"], runCommand);
|
|
17184
|
+
if (!EXACT_SHA.test(actualSha)) throw new Error("worktree HEAD is not an exact commit SHA");
|
|
17185
|
+
if (!task.resumed_from) {
|
|
17186
|
+
if (actualSha !== baseSha) throw new Error("initial comparative worktree HEAD does not match requested base");
|
|
17187
|
+
} else if (continuationRestore) {
|
|
17188
|
+
if (task.pr_head_sha_at_enqueue && actualSha !== task.pr_head_sha_at_enqueue) {
|
|
17189
|
+
throw new Error("continuation worktree HEAD does not match its expected continuation head");
|
|
17190
|
+
}
|
|
17191
|
+
await git3(worktreeDir, ["merge-base", "--is-ancestor", baseSha, actualSha], runCommand);
|
|
17192
|
+
} else {
|
|
17193
|
+
if (!hasAffirmativePreSpawnNoSpendEvidence(parentTask)) {
|
|
17194
|
+
throw new Error("continuation branch is missing and prior execution state is unknown; recovery review required");
|
|
17195
|
+
}
|
|
17196
|
+
if (actualSha !== baseSha) throw new Error("safe pre-spawn continuation did not rematerialize the original base");
|
|
17197
|
+
}
|
|
17198
|
+
const acknowledgement = await client.postProgress(task.code_task_id, {
|
|
17199
|
+
execution_start_sha: actualSha,
|
|
17200
|
+
stage: "preparing_worktree",
|
|
17201
|
+
message: `Exact execution source ${actualSha} verified before model start`
|
|
17202
|
+
});
|
|
17203
|
+
if (acknowledgement?.terminal || acknowledgement?.task?.execution_start_sha !== actualSha) {
|
|
17204
|
+
throw new Error("control plane did not durably acknowledge execution_start_sha");
|
|
17205
|
+
}
|
|
17206
|
+
return { baseSha, executionStartSha: actualSha };
|
|
17207
|
+
}
|
|
17002
17208
|
async function prepareTaskWorktree({ client, task, cfg, safeProgress: safeProgress2, log: log2 }) {
|
|
17003
17209
|
const id = task.code_task_id;
|
|
17004
17210
|
await safeProgress2(client, id, runnerStagePatch(
|
|
@@ -17015,7 +17221,7 @@ async function prepareTaskWorktree({ client, task, cfg, safeProgress: safeProgre
|
|
|
17015
17221
|
const wt = await createFixWorktree(
|
|
17016
17222
|
"code-task",
|
|
17017
17223
|
{ source: id.slice(0, 8), repo: task.repo },
|
|
17018
|
-
{ githubToken }
|
|
17224
|
+
{ githubToken, baseSha: task.comparative_base_sha ?? null }
|
|
17019
17225
|
);
|
|
17020
17226
|
if (!wt.worktreeName || !wt.worktreeDir) {
|
|
17021
17227
|
throw new Error("worktree isolation failure \u2014 refusing to run in the main tree");
|
|
@@ -17045,8 +17251,16 @@ async function prepareTaskWorktree({ client, task, cfg, safeProgress: safeProgre
|
|
|
17045
17251
|
});
|
|
17046
17252
|
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" : ""}`);
|
|
17047
17253
|
}
|
|
17048
|
-
|
|
17254
|
+
const sourceBinding = await bindTaskExecutionSource({
|
|
17255
|
+
client,
|
|
17256
|
+
task,
|
|
17257
|
+
parentTask,
|
|
17258
|
+
worktreeDir: wt.worktreeDir,
|
|
17259
|
+
continuationRestore
|
|
17260
|
+
});
|
|
17261
|
+
return { wt, githubToken, agentGithubReadToken, continuationRestore, repairPlan, sourceBinding };
|
|
17049
17262
|
}
|
|
17263
|
+
var EXACT_SHA, RUNNER_INSTANCE_ID;
|
|
17050
17264
|
var init_task_worktree_preparation = __esm({
|
|
17051
17265
|
"../../scripts/virtual-office/code-runner/task-worktree-preparation.mjs"() {
|
|
17052
17266
|
"use strict";
|
|
@@ -17054,6 +17268,9 @@ var init_task_worktree_preparation = __esm({
|
|
|
17054
17268
|
init_resume_branch();
|
|
17055
17269
|
init_repair_publication_strategy();
|
|
17056
17270
|
init_task_helpers();
|
|
17271
|
+
init_process_runner2();
|
|
17272
|
+
EXACT_SHA = /^[0-9a-f]{40}$/u;
|
|
17273
|
+
RUNNER_INSTANCE_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
|
17057
17274
|
}
|
|
17058
17275
|
});
|
|
17059
17276
|
|