spacecode 2.14.4 → 2.14.6
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/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/repl.mjs +71 -10
- package/package.json +1 -1
package/dist/repl.mjs
CHANGED
|
@@ -4088,7 +4088,7 @@ var init_defaults_core_loop = __esm({
|
|
|
4088
4088
|
body: `You are the core of a coding run. You direct; you never read files, search, run commands or write code. Workers do that.
|
|
4089
4089
|
|
|
4090
4090
|
## Tools (the only ones that exist)
|
|
4091
|
-
- edit_plan {tasks}: add tasks. Each task has id, role, goal (what the worker must do, concrete and self-contained: the worker sees only this, the files and the acceptance), files (a file belongs to one open task at a time), acceptance {kind: checks|rubric|none, detail} (a code task that the repo can check, with tests or a typecheck, uses kind checks so the repo's own typecheck and tests must pass; rubric and none skip them), budgetTokens, deadlineMs (milliseconds; a worker needs minutes to clone, work and check, so use 600000 for a code task; values are clamped to 600000-840000).
|
|
4091
|
+
- edit_plan {tasks}: add tasks. Each task has id, role, goal (what the worker must do, concrete and self-contained: the worker sees only this, the files and the acceptance), files (a file belongs to one open task at a time), dependsOn (optional list of task ids that must be accepted first; set it for any task that builds on another's work), acceptance {kind: checks|rubric|none, detail} (a code task that the repo can check, with tests or a typecheck, uses kind checks so the repo's own typecheck and tests must pass; rubric and none skip them), budgetTokens, deadlineMs (milliseconds; a worker needs minutes to clone, work and check, so use 600000 for a code task; values are clamped to 600000-840000).
|
|
4092
4092
|
- dispatch_task {taskId}: start a pending task (at most 2 attempts per task).
|
|
4093
4093
|
- accept_task {taskId}: accept a task in review. A checks task needs a passing verifier first.
|
|
4094
4094
|
- drop_task {taskId, reason}: drop a task you no longer need.
|
|
@@ -4101,7 +4101,7 @@ var init_defaults_core_loop = __esm({
|
|
|
4101
4101
|
- When the token budget is exhausted, package what is accepted and send the result.
|
|
4102
4102
|
- Prefer small tasks with one owner per file. Stay inside the budget.
|
|
4103
4103
|
- You are only woken by worker results and user messages, never by your own actions. So in the same answer as edit_plan, dispatch_task every task that can start now; a task left pending with nothing running stalls the run.
|
|
4104
|
-
- Tasks that must follow another (a test after its implementation) are dispatched after the earlier one is accepted; the later worker starts from the accepted work, so give it only its own files and do not repeat the earlier task's work.
|
|
4104
|
+
- Tasks that must follow another (a test after its implementation) name it in dependsOn and are dispatched only after the earlier one is accepted (a dispatch before that is refused, including while the earlier one is being retried); the later worker starts from the accepted work, so give it only its own files and do not repeat the earlier task's work. If a task it depends on is dropped or failed, the dependent can never start: drop it or replan it without that dependency.
|
|
4105
4105
|
- Worker digests are already included in what you are shown; there is nothing more to read. Decide from them (accept_task, drop_task, or dispatch a retry).
|
|
4106
4106
|
- Make every tool call of your answer at once; you do not see results between calls. Reply with tool calls only.`
|
|
4107
4107
|
},
|
|
@@ -104840,6 +104840,32 @@ var init_worker_contract = __esm({
|
|
|
104840
104840
|
}
|
|
104841
104841
|
});
|
|
104842
104842
|
|
|
104843
|
+
// ../shared/src/core/check-baseline.ts
|
|
104844
|
+
function judgeAgainstBaseline(checks, baseline) {
|
|
104845
|
+
const newChecks = {};
|
|
104846
|
+
const baselineFailed = [];
|
|
104847
|
+
for (const key of Object.keys(checks)) {
|
|
104848
|
+
const code = checks[key];
|
|
104849
|
+
if (code === 0) {
|
|
104850
|
+
newChecks[key] = 0;
|
|
104851
|
+
continue;
|
|
104852
|
+
}
|
|
104853
|
+
const baseCode = baseline[key];
|
|
104854
|
+
if (typeof baseCode === "number" && baseCode !== 0) {
|
|
104855
|
+
newChecks[key] = 0;
|
|
104856
|
+
baselineFailed.push(key);
|
|
104857
|
+
} else {
|
|
104858
|
+
newChecks[key] = code;
|
|
104859
|
+
}
|
|
104860
|
+
}
|
|
104861
|
+
return { checks: newChecks, baselineFailed };
|
|
104862
|
+
}
|
|
104863
|
+
var init_check_baseline = __esm({
|
|
104864
|
+
"../shared/src/core/check-baseline.ts"() {
|
|
104865
|
+
"use strict";
|
|
104866
|
+
}
|
|
104867
|
+
});
|
|
104868
|
+
|
|
104843
104869
|
// ../shared/src/core/worker-pipeline.ts
|
|
104844
104870
|
function agentFailure(run) {
|
|
104845
104871
|
const tail = (run.stderr.trim() || run.stdout.trim()).slice(-300).replace(/\s+/g, " ");
|
|
@@ -104910,6 +104936,7 @@ async function runWorkerJob(deps, job) {
|
|
|
104910
104936
|
const changedFiles = names.stdout.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
104911
104937
|
const diffLines = patch.stdout === "" ? 0 : patch.stdout.split("\n").length;
|
|
104912
104938
|
const checks = {};
|
|
104939
|
+
const baselineFailed = [];
|
|
104913
104940
|
if (job.task.acceptance.kind === "checks") {
|
|
104914
104941
|
const pkg = await deps.sandbox.exec(`cd ${shq(dir)} && cat package.json 2>/dev/null`);
|
|
104915
104942
|
let scripts;
|
|
@@ -104931,6 +104958,31 @@ async function runWorkerJob(deps, job) {
|
|
|
104931
104958
|
if (r.exitCode !== 0) deps.log("worker check failed", { ...ids, check: c.name, exitCode: r.exitCode, tail: redactSecrets(`${r.stdout}
|
|
104932
104959
|
${r.stderr}`.trim().slice(-300).replace(/\s+/g, " ")) });
|
|
104933
104960
|
}
|
|
104961
|
+
const failing = wanted.filter((c) => checks[c.name] !== 0);
|
|
104962
|
+
if (deps.baseline && failing.length > 0) {
|
|
104963
|
+
const bdir = `${dir}-baseline`;
|
|
104964
|
+
try {
|
|
104965
|
+
const added = await deps.sandbox.exec(`cd ${shq(dir)} && git worktree add --detach ${shq(bdir)} ${base}`);
|
|
104966
|
+
if (added.exitCode !== 0) {
|
|
104967
|
+
deps.log("baseline worktree failed", { ...ids, exitCode: added.exitCode });
|
|
104968
|
+
} else {
|
|
104969
|
+
await deps.baseline.prepare(bdir);
|
|
104970
|
+
const baselineExit = {};
|
|
104971
|
+
for (const c of failing) baselineExit[c.name] = (await deps.sandbox.exec(`cd ${shq(bdir)} && timeout ${slice()} ${c.cmd}`)).exitCode;
|
|
104972
|
+
const judged = judgeAgainstBaseline(checks, baselineExit);
|
|
104973
|
+
Object.assign(checks, judged.checks);
|
|
104974
|
+
for (const name of judged.baselineFailed) {
|
|
104975
|
+
baselineFailed.push(name);
|
|
104976
|
+
deps.log("worker check also fails on the base revision", { ...ids, check: name, exitCode: baselineExit[name] });
|
|
104977
|
+
}
|
|
104978
|
+
}
|
|
104979
|
+
} catch (e) {
|
|
104980
|
+
if (e instanceof TypeError || e instanceof ReferenceError) throw e;
|
|
104981
|
+
deps.log("baseline comparison failed", { ...ids, error: e instanceof Error ? e.message.slice(0, 120) : "unknown" });
|
|
104982
|
+
} finally {
|
|
104983
|
+
await deps.sandbox.exec(`cd ${shq(dir)} && git worktree remove --force ${shq(bdir)}`).catch((e) => deps.log("baseline worktree cleanup failed", { ...ids, error: e instanceof Error ? e.message.slice(0, 100) : "unknown" }));
|
|
104984
|
+
}
|
|
104985
|
+
}
|
|
104934
104986
|
}
|
|
104935
104987
|
}
|
|
104936
104988
|
if (await deps.cancelled()) return "cancelled";
|
|
@@ -104947,7 +104999,7 @@ ${r.stderr}`.trim().slice(-300).replace(/\s+/g, " ")) });
|
|
|
104947
104999
|
...patch.stdout !== "" && !tooBig ? { patch: patch.stdout } : {},
|
|
104948
105000
|
...tooBig ? { reason: "patch exceeds the size limit" } : {},
|
|
104949
105001
|
...digest === "" ? { reason: run.exitCode !== 0 ? agentFailure(run) : noDigestReason(result?.text) } : {},
|
|
104950
|
-
verify: { checks, changedFiles, diffLines }
|
|
105002
|
+
verify: { checks, changedFiles, diffLines, ...baselineFailed.length > 0 ? { baselineFailed } : {} }
|
|
104951
105003
|
});
|
|
104952
105004
|
deps.log("worker reported", { ...ids, files: changedFiles.length, diffLines });
|
|
104953
105005
|
return "reported";
|
|
@@ -104963,6 +105015,7 @@ var MAX_PATCH_BYTES, shq, clip;
|
|
|
104963
105015
|
var init_worker_pipeline = __esm({
|
|
104964
105016
|
"../shared/src/core/worker-pipeline.ts"() {
|
|
104965
105017
|
"use strict";
|
|
105018
|
+
init_check_baseline();
|
|
104966
105019
|
init_worker_contract();
|
|
104967
105020
|
MAX_PATCH_BYTES = 5e6;
|
|
104968
105021
|
shq = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
|
|
@@ -104982,6 +105035,7 @@ var init_core = __esm({
|
|
|
104982
105035
|
init_verify();
|
|
104983
105036
|
init_worker_contract();
|
|
104984
105037
|
init_worker_pipeline();
|
|
105038
|
+
init_check_baseline();
|
|
104985
105039
|
}
|
|
104986
105040
|
});
|
|
104987
105041
|
|
|
@@ -104991,6 +105045,15 @@ async function must(shell, cmd, what) {
|
|
|
104991
105045
|
if (r.exitCode !== 0) throw new Error(`${what} failed: ${r.stderr.trim() || `exit ${r.exitCode}`}`);
|
|
104992
105046
|
return r.stdout.trim();
|
|
104993
105047
|
}
|
|
105048
|
+
async function linkDependencyFolders(shell, top, dir) {
|
|
105049
|
+
const found = await must(shell, `cd ${q2(top)} && find . -maxdepth 4 -name node_modules -type d -prune -not -path './.git/*'`, "listing dependency folders");
|
|
105050
|
+
for (const line of found.split("\n")) {
|
|
105051
|
+
const rel = line.trim().replace(/^\.\//, "");
|
|
105052
|
+
if (rel === "" || rel === ".") continue;
|
|
105053
|
+
const parent = rel.includes("/") ? rel.slice(0, rel.lastIndexOf("/")) : ".";
|
|
105054
|
+
await must(shell, `mkdir -p ${q2(`${dir}/${parent}`)} && { [ -e ${q2(`${dir}/${rel}`)} ] || ln -s ${q2(`${top}/${rel}`)} ${q2(`${dir}/${rel}`)}; }`, `linking ${rel}`);
|
|
105055
|
+
}
|
|
105056
|
+
}
|
|
104994
105057
|
async function createTaskWorktree(shell, cwd2, base, runId, taskId, attempt, warn = () => {
|
|
104995
105058
|
}) {
|
|
104996
105059
|
const top = await must(shell, `cd ${q2(cwd2)} && git rev-parse --show-toplevel`, "finding the repository root");
|
|
@@ -105000,13 +105063,7 @@ async function createTaskWorktree(shell, cwd2, base, runId, taskId, attempt, war
|
|
|
105000
105063
|
if (sweep.exitCode !== 0) warn(`stale worktree sweep failed: ${sweep.stderr.trim().slice(0, 200) || `exit ${sweep.exitCode}`}`);
|
|
105001
105064
|
const dir = `${root}/${safe(runId)}/wt-${safe(taskId)}-${attempt}`;
|
|
105002
105065
|
await must(shell, `git -C ${q2(top)} worktree add --detach ${q2(dir)} ${q2(commit)}`, "creating the task worktree");
|
|
105003
|
-
|
|
105004
|
-
for (const line of found.split("\n")) {
|
|
105005
|
-
const rel = line.trim().replace(/^\.\//, "");
|
|
105006
|
-
if (rel === "" || rel === ".") continue;
|
|
105007
|
-
const parent = rel.includes("/") ? rel.slice(0, rel.lastIndexOf("/")) : ".";
|
|
105008
|
-
await must(shell, `mkdir -p ${q2(`${dir}/${parent}`)} && { [ -e ${q2(`${dir}/${rel}`)} ] || ln -s ${q2(`${top}/${rel}`)} ${q2(`${dir}/${rel}`)}; }`, `linking ${rel}`);
|
|
105009
|
-
}
|
|
105066
|
+
await linkDependencyFolders(shell, top, dir);
|
|
105010
105067
|
return { top, dir };
|
|
105011
105068
|
}
|
|
105012
105069
|
async function removeTaskWorktree(shell, top, dir) {
|
|
@@ -105057,6 +105114,10 @@ function localPipelineDeps(d, job) {
|
|
|
105057
105114
|
chown: () => "true",
|
|
105058
105115
|
installDependencies: false,
|
|
105059
105116
|
excludeFromDiff: ["**/node_modules"],
|
|
105117
|
+
// A check that already fails on the base revision (this repo's `lint`: eslint is not installed) is re-run there, so it is not blamed on the task.
|
|
105118
|
+
baseline: { prepare: async (bdir) => {
|
|
105119
|
+
if (created) await linkDependencyFolders(shell, created.top, bdir);
|
|
105120
|
+
} },
|
|
105060
105121
|
basePatches: async () => {
|
|
105061
105122
|
const res = await d.fetch(`${base}/base-patches`, { headers: auth, signal: d.signal });
|
|
105062
105123
|
if (!res.ok) throw new Error(`base patches request failed (${res.status})`);
|