spacecode 2.14.3 → 2.14.5

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/repl.mjs CHANGED
@@ -6375,9 +6375,9 @@ var init_trust = __esm({
6375
6375
  });
6376
6376
 
6377
6377
  // ../agent/src/import/source-changes.ts
6378
- function fingerprintSources(files, hash) {
6378
+ function fingerprintSources(files, hash2) {
6379
6379
  const fingerprint = {};
6380
- for (const [path13, content] of Object.entries(files)) fingerprint[path13] = hash(content);
6380
+ for (const [path13, content] of Object.entries(files)) fingerprint[path13] = hash2(content);
6381
6381
  return fingerprint;
6382
6382
  }
6383
6383
  function diffSources(before, after) {
@@ -6397,8 +6397,8 @@ var init_source_changes = __esm({
6397
6397
  });
6398
6398
 
6399
6399
  // ../agent/src/import/mcp-tool-cache.ts
6400
- function serverCacheKey(def, hash) {
6401
- return hash(JSON.stringify([def.name, def.command, def.args, [...def.envNames].sort()]));
6400
+ function serverCacheKey(def, hash2) {
6401
+ return hash2(JSON.stringify([def.name, def.command, def.args, [...def.envNames].sort()]));
6402
6402
  }
6403
6403
  function cleanTool(raw) {
6404
6404
  if (!isPlainObject(raw) || typeof raw.name !== "string" || raw.name === "" || typeof raw.description !== "string" || !isPlainObject(raw.inputSchema)) return null;
@@ -7817,9 +7817,9 @@ function formatAdaptReport(r, before, replaced) {
7817
7817
  }
7818
7818
  async function adaptInstructions(deps, original) {
7819
7819
  const { client, log } = deps;
7820
- const hash = await sha12(original);
7820
+ const hash2 = await sha12(original);
7821
7821
  const current = await client.get("instructions", "global");
7822
- const marker = adaptedMarker(hash);
7822
+ const marker = adaptedMarker(hash2);
7823
7823
  const existingOrig = await client.get("instructions", "global.orig");
7824
7824
  if (current && existingOrig && existingOrig.content === original && current.description === marker) {
7825
7825
  log(" unchanged instructions/global (already adapted from this source)");
@@ -7886,7 +7886,7 @@ var init_import_adapt = __esm({
7886
7886
  "use strict";
7887
7887
  init_src();
7888
7888
  EXCERPT_CHARS = 1e3;
7889
- adaptedMarker = (hash) => `adapted-from:${hash}`;
7889
+ adaptedMarker = (hash2) => `adapted-from:${hash2}`;
7890
7890
  }
7891
7891
  });
7892
7892
 
@@ -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,14 +105045,7 @@ 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
  }
104994
- async function createTaskWorktree(shell, cwd2, runId, taskId, attempt) {
104995
- const top = await must(shell, `cd ${q2(cwd2)} && git rev-parse --show-toplevel`, "finding the repository root");
104996
- const gitDir = await must(shell, `cd ${q2(cwd2)} && git rev-parse --absolute-git-dir`, "finding the git directory");
104997
- const base = await must(shell, `cd ${q2(top)} && git stash create`, "snapshotting the working tree") || "HEAD";
104998
- const root = `${gitDir}/spacecode-core`;
104999
- await shell(`find ${q2(root)} -mindepth 2 -maxdepth 2 -type d -name 'wt-*' -mmin +1440 2>/dev/null | while read -r w; do git -C ${q2(top)} worktree remove --force "$w" >/dev/null 2>&1; done; git -C ${q2(top)} worktree prune`);
105000
- const dir = `${root}/${safe(runId)}/wt-${safe(taskId)}-${attempt}`;
105001
- await must(shell, `git -C ${q2(top)} worktree add --detach ${q2(dir)} ${q2(base)}`, "creating the task worktree");
105048
+ async function linkDependencyFolders(shell, top, dir) {
105002
105049
  const found = await must(shell, `cd ${q2(top)} && find . -maxdepth 4 -name node_modules -type d -prune -not -path './.git/*'`, "listing dependency folders");
105003
105050
  for (const line of found.split("\n")) {
105004
105051
  const rel = line.trim().replace(/^\.\//, "");
@@ -105006,6 +105053,17 @@ async function createTaskWorktree(shell, cwd2, runId, taskId, attempt) {
105006
105053
  const parent = rel.includes("/") ? rel.slice(0, rel.lastIndexOf("/")) : ".";
105007
105054
  await must(shell, `mkdir -p ${q2(`${dir}/${parent}`)} && { [ -e ${q2(`${dir}/${rel}`)} ] || ln -s ${q2(`${top}/${rel}`)} ${q2(`${dir}/${rel}`)}; }`, `linking ${rel}`);
105008
105055
  }
105056
+ }
105057
+ async function createTaskWorktree(shell, cwd2, base, runId, taskId, attempt, warn = () => {
105058
+ }) {
105059
+ const top = await must(shell, `cd ${q2(cwd2)} && git rev-parse --show-toplevel`, "finding the repository root");
105060
+ const commit = await must(shell, `cd ${q2(top)} && git stash create`, "snapshotting the working tree") || "HEAD";
105061
+ const root = `${base}/${safe(top.slice(top.lastIndexOf("/") + 1))}-${hash(top)}`;
105062
+ const sweep = await shell(`find ${q2(root)} -mindepth 2 -maxdepth 2 -type d -name 'wt-*' -mmin +1440 2>/dev/null | while read -r w; do git -C ${q2(top)} worktree remove --force "$w" >/dev/null 2>&1; done; git -C ${q2(top)} worktree prune`);
105063
+ if (sweep.exitCode !== 0) warn(`stale worktree sweep failed: ${sweep.stderr.trim().slice(0, 200) || `exit ${sweep.exitCode}`}`);
105064
+ const dir = `${root}/${safe(runId)}/wt-${safe(taskId)}-${attempt}`;
105065
+ await must(shell, `git -C ${q2(top)} worktree add --detach ${q2(dir)} ${q2(commit)}`, "creating the task worktree");
105066
+ await linkDependencyFolders(shell, top, dir);
105009
105067
  return { top, dir };
105010
105068
  }
105011
105069
  async function removeTaskWorktree(shell, top, dir) {
@@ -105016,12 +105074,17 @@ async function removeTaskWorktree(shell, top, dir) {
105016
105074
  return err instanceof Error ? err.message : String(err);
105017
105075
  }
105018
105076
  }
105019
- var q2, safe;
105077
+ var q2, safe, hash;
105020
105078
  var init_worktree = __esm({
105021
105079
  "src/core/worktree.ts"() {
105022
105080
  "use strict";
105023
105081
  q2 = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
105024
105082
  safe = (s) => s.replace(/[^A-Za-z0-9_.-]/g, "_");
105083
+ hash = (s) => {
105084
+ let h = 5381;
105085
+ for (let i = 0; i < s.length; i++) h = (h << 5) + h + s.charCodeAt(i) >>> 0;
105086
+ return h.toString(16);
105087
+ };
105025
105088
  }
105026
105089
  });
105027
105090
 
@@ -105034,7 +105097,7 @@ function localPipelineDeps(d, job) {
105034
105097
  const deps = {
105035
105098
  sandbox: { exec: shell, writeFile: (path13, content) => d.writeFile(path13, content) },
105036
105099
  setup: async () => {
105037
- const wt = await createTaskWorktree(shell, d.cwd, d.runId, job.taskId, job.attempt);
105100
+ const wt = await createTaskWorktree(shell, d.cwd, d.worktreeBase, d.runId, job.taskId, job.attempt, (message) => d.log(message, { runId: d.runId, taskId: job.taskId }));
105038
105101
  created = wt;
105039
105102
  return wt.dir;
105040
105103
  },
@@ -105051,6 +105114,10 @@ function localPipelineDeps(d, job) {
105051
105114
  chown: () => "true",
105052
105115
  installDependencies: false,
105053
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
+ } },
105054
105121
  basePatches: async () => {
105055
105122
  const res = await d.fetch(`${base}/base-patches`, { headers: auth, signal: d.signal });
105056
105123
  if (!res.ok) throw new Error(`base patches request failed (${res.status})`);
@@ -105228,6 +105295,7 @@ var init_core_turn = __esm({
105228
105295
  import { execFile as execFile4 } from "node:child_process";
105229
105296
  import { mkdir as mkdir6, writeFile as writeFile6 } from "node:fs/promises";
105230
105297
  import { join as join15 } from "node:path";
105298
+ import { tmpdir } from "node:os";
105231
105299
  async function repoIsPublic(url, fetchFn = fetch) {
105232
105300
  const hit = visibility.get(url);
105233
105301
  if (hit !== void 0) return hit;
@@ -105305,6 +105373,7 @@ async function runCoreForRepl(o) {
105305
105373
  const { deps, cleanup } = localPipelineDeps(
105306
105374
  {
105307
105375
  cwd: o.cwd,
105376
+ worktreeBase: join15(tmpdir(), "spacecode-core"),
105308
105377
  runId,
105309
105378
  backendUrl: backendUrl(o.env),
105310
105379
  apiKey: o.apiKey,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spacecode",
3
- "version": "2.14.3",
3
+ "version": "2.14.5",
4
4
  "description": "SpaceCode - launch cloud dev environments with AI coding agents",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",