@odla-ai/harness 0.11.11 → 0.11.13

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.
@@ -543,9 +543,11 @@ export {
543
543
  buildContainerRunArgs,
544
544
  runContainerAttempt,
545
545
  SKIP_WORKSPACE_DIRS,
546
+ SECRET_WORKSPACE_FILE,
547
+ allowedWorkspacePath,
546
548
  materializeGitTree,
547
549
  stageWorkspace,
548
550
  stageWorkspacePair,
549
551
  safeWorkspaceLabel
550
552
  };
551
- //# sourceMappingURL=chunk-ZM6AITC2.js.map
553
+ //# sourceMappingURL=chunk-3ON6UAOV.js.map
@@ -1,13 +1,14 @@
1
1
  import {
2
2
  observedBroker
3
- } from "./chunk-INL642J5.js";
3
+ } from "./chunk-OXWLPB7P.js";
4
4
  import {
5
+ SECRET_WORKSPACE_FILE,
5
6
  SKIP_WORKSPACE_DIRS,
6
7
  assertPinnedImage,
7
8
  stageWorkspace,
8
9
  stageWorkspacePair,
9
10
  verifyContainerEngineBoundary
10
- } from "./chunk-ZM6AITC2.js";
11
+ } from "./chunk-3ON6UAOV.js";
11
12
  import {
12
13
  HARNESS_PROTOCOL_VERSION
13
14
  } from "./chunk-ONYW2VSB.js";
@@ -290,7 +291,7 @@ async function withOverloadRetry(call, wait2, onRetry = () => void 0, retryable
290
291
  }
291
292
 
292
293
  // src/code-runtime-reconciler.ts
293
- var sleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
294
+ var sleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
294
295
  var POST_ACK_RETRY_TICKS = 40;
295
296
  var CodeRuntimeReconciler = class {
296
297
  constructor(control, engine, onDiagnostic, options = {}) {
@@ -392,12 +393,12 @@ function retryableControlFailure(value) {
392
393
  return failure.status === 408 || failure.status === 425 || failure.status === 429 || failure.status >= 500;
393
394
  }
394
395
  function wait(ms, signal) {
395
- return new Promise((resolve6) => {
396
- if (signal?.aborted) return resolve6();
397
- const timer = setTimeout(resolve6, ms);
396
+ return new Promise((resolve7) => {
397
+ if (signal?.aborted) return resolve7();
398
+ const timer = setTimeout(resolve7, ms);
398
399
  signal?.addEventListener("abort", () => {
399
400
  clearTimeout(timer);
400
- resolve6();
401
+ resolve7();
401
402
  }, { once: true });
402
403
  });
403
404
  }
@@ -827,15 +828,121 @@ function isCheckpointEffectCompleted(checkpoint, effectId, actionDigest) {
827
828
  return true;
828
829
  }
829
830
 
830
- // src/recipe-container.ts
831
+ // src/code-recipe-dependencies.ts
832
+ import { lstat as lstat2, rm, symlink } from "fs/promises";
833
+ import { isAbsolute, join } from "path";
834
+ var RESERVED_MOUNTS = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage"]);
835
+ function assertLentPath(mountAs) {
836
+ const parts = mountAs.split("/");
837
+ if (isAbsolute(mountAs) || mountAs.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
838
+ throw new TypeError(`recipe dependencies must mount inside the workspace, not at "${mountAs}"`);
839
+ }
840
+ }
841
+ function assertReservedMount(mountAs) {
842
+ assertLentPath(mountAs);
843
+ if (!RESERVED_MOUNTS.has(mountAs.split("/").at(-1))) {
844
+ throw new TypeError(`recipe dependencies must mount at a reserved name, not "${mountAs}"`);
845
+ }
846
+ }
847
+ function lentDirectories(dependencies) {
848
+ return [{ source: dependencies.source, mountAs: dependencies.mountAs ?? "node_modules" }, ...dependencies.nested ?? []];
849
+ }
850
+ function withRecipeDependencies(executor, dependencies) {
851
+ const lent = lentDirectories(dependencies);
852
+ for (const entry of lent) {
853
+ if (!isAbsolute(entry.source)) {
854
+ throw new TypeError("recipe dependency source must be an absolute path");
855
+ }
856
+ assertReservedMount(entry.mountAs);
857
+ }
858
+ return {
859
+ run: async (input) => {
860
+ const linked = [];
861
+ try {
862
+ for (const entry of lent) {
863
+ const target = join(input.workspaceDir, entry.mountAs);
864
+ if (await lstat2(target).catch(() => null)) continue;
865
+ await symlink(entry.source, target, "dir").then(() => linked.push(target), () => void 0);
866
+ }
867
+ return await executor.run(input);
868
+ } finally {
869
+ for (const target of linked) await rm(target, { force: true, recursive: false }).catch(() => void 0);
870
+ }
871
+ }
872
+ };
873
+ }
874
+ async function installedDependencies(repoRoot) {
875
+ const source = join(repoRoot, "node_modules");
876
+ const info = await lstat2(source).catch(() => null);
877
+ return info?.isDirectory() ? { source } : null;
878
+ }
879
+
880
+ // src/recipe-container-lending.ts
831
881
  import { spawn as spawn2 } from "child_process";
882
+ import { mkdir, rm as rm2, stat } from "fs/promises";
883
+ import { dirname, join as join2, resolve as resolve3, sep as sep2 } from "path";
884
+ var RUN_ID = /^[A-Za-z0-9_.-]{1,64}$/;
885
+ function copy(args) {
886
+ return new Promise((accept, reject) => {
887
+ const child = spawn2("cp", args, { shell: false, stdio: "ignore" });
888
+ child.once("error", reject);
889
+ child.once("exit", (code) => accept(code ?? 1));
890
+ });
891
+ }
892
+ async function cloneTree(source, target, platform = process.platform) {
893
+ await mkdir(dirname(target), { recursive: true });
894
+ const fast = platform === "darwin" ? ["-Rc", source, target] : platform === "linux" ? ["-R", "--reflink=auto", source, target] : null;
895
+ if (fast && await copy(fast) === 0) return;
896
+ await rm2(target, { recursive: true, force: true });
897
+ if (await copy(["-R", source, target]) !== 0) throw new Error(`could not clone ${source}`);
898
+ }
899
+ async function lendDependencies(dependencies, runId) {
900
+ if (!dependencies?.runsDir) return { dependencies, release: async () => void 0 };
901
+ if (!RUN_ID.test(runId)) throw new TypeError("run id is not a safe directory name");
902
+ const treeRoot = dirname(dependencies.source);
903
+ for (const entry of lentDirectories(dependencies)) {
904
+ if (entry.source !== join2(treeRoot, entry.mountAs)) {
905
+ throw new TypeError("every directory of a cloned set must lie under its tree root at its mount path");
906
+ }
907
+ }
908
+ const runDir = join2(dependencies.runsDir, runId);
909
+ await cloneTree(treeRoot, runDir);
910
+ const rebase = (entry) => ({ source: join2(runDir, entry.mountAs), mountAs: entry.mountAs });
911
+ return {
912
+ dependencies: {
913
+ ...dependencies,
914
+ source: join2(runDir, dependencies.mountAs ?? "node_modules"),
915
+ nested: (dependencies.nested ?? []).map(rebase),
916
+ writable: true
917
+ },
918
+ release: () => rm2(runDir, { recursive: true, force: true })
919
+ };
920
+ }
921
+ async function lendBuildProducts(products, workspaceDir) {
922
+ const root = resolve3(workspaceDir);
923
+ const lent = [];
924
+ for (const product of products) {
925
+ assertLentPath(product.mountAs);
926
+ if (SECRET_WORKSPACE_FILE.test(product.mountAs.split("/").at(-1))) throw new TypeError("a secret is never a build product");
927
+ const target = resolve3(root, product.mountAs);
928
+ if (!target.startsWith(`${root}${sep2}`)) throw new TypeError("build product escapes the workspace");
929
+ if (await stat(target).catch(() => null)) continue;
930
+ if (!await stat(join2(root, product.within ?? dirname(product.mountAs))).catch(() => null)) continue;
931
+ await cloneTree(product.source, target);
932
+ lent.push(product.mountAs);
933
+ }
934
+ return lent;
935
+ }
936
+
937
+ // src/recipe-container.ts
938
+ import { spawn as spawn3 } from "child_process";
832
939
  import { getgid, getuid } from "process";
833
940
  import { randomUUID } from "crypto";
834
941
  var ARTIFACT_PATH = /^[A-Za-z0-9_@+.,-]+(?:\/[A-Za-z0-9_@+.,-]+)*$/;
835
942
  var PRIVATE_ARTIFACT_PART = /^(?:\.git|\.odla|\.wrangler|\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json)$/i;
836
943
  function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-recipe-${randomUUID().slice(0, 12)}`, dependencies = null) {
837
944
  assertCodeBuildRecipe(recipe2);
838
- const mounts = dependencies ? [dependencyMount(engine, dependencies)] : [];
945
+ const mounts = dependencies ? dependencyMounts(engine, dependencies) : [];
839
946
  if (/[,\r\n]/.test(workspaceDir)) throw new TypeError("workspace path contains unsupported mount characters");
840
947
  const uid = typeof getuid === "function" ? getuid() : 1e3;
841
948
  const gid = typeof getgid === "function" ? getgid() : 1e3;
@@ -861,6 +968,7 @@ function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-re
861
968
  ...mounts,
862
969
  "--workdir=/workspace",
863
970
  "--env=CI=1",
971
+ "--env=HOME=/tmp",
864
972
  recipe2.image,
865
973
  ...recipe2.command
866
974
  ];
@@ -883,26 +991,34 @@ function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-re
883
991
  ...mounts,
884
992
  "--workdir=/workspace",
885
993
  "--env=CI=1",
994
+ "--env=HOME=/tmp",
886
995
  recipe2.image,
887
996
  ...recipe2.command
888
997
  ];
889
998
  }
890
- var LENDABLE = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage"]);
891
- function dependencyMount(engine, dependencies) {
892
- const mountAs = dependencies.mountAs ?? "node_modules";
893
- if (!dependencies.source.startsWith("/") || /[,\r\n]/.test(dependencies.source)) {
894
- throw new TypeError("recipe dependency source must be an absolute path without mount separators");
895
- }
896
- if (!LENDABLE.has(mountAs)) throw new TypeError(`recipe dependencies must mount at a reserved name, not "${mountAs}"`);
897
- return engine === "container" ? `--mount=type=bind,source=${dependencies.source},target=/workspace/${mountAs},readonly` : `--mount=type=bind,src=${dependencies.source},dst=/workspace/${mountAs},readonly`;
999
+ function dependencyMounts(engine, dependencies) {
1000
+ const readonly = dependencies.writable ? "" : ",readonly";
1001
+ return lentDirectories(dependencies).map(({ source, mountAs }) => {
1002
+ if (!source.startsWith("/") || /[,\r\n]/.test(source)) {
1003
+ throw new TypeError("recipe dependency source must be an absolute path without mount separators");
1004
+ }
1005
+ assertReservedMount(mountAs);
1006
+ return engine === "container" ? `--mount=type=bind,source=${source},target=/workspace/${mountAs}${readonly}` : `--mount=type=bind,src=${source},dst=/workspace/${mountAs}${readonly}`;
1007
+ });
898
1008
  }
899
1009
  function createContainerRecipeExecutor(engine, options = {}) {
900
1010
  return {
901
1011
  async run(input) {
902
1012
  await verifyContainerEngineBoundary(engine);
903
1013
  const name = `odla-recipe-${randomUUID().slice(0, 12)}`;
904
- const args = buildRecipeContainerArgs(engine, input.workspaceDir, input.recipe, name, options.dependencies ?? null);
905
- return execute(engine, args, name, input.recipe, input.signal);
1014
+ const lent = await lendDependencies(options.dependencies ?? null, name);
1015
+ try {
1016
+ if (lent.dependencies?.products?.length) await lendBuildProducts(lent.dependencies.products, input.workspaceDir);
1017
+ const args = buildRecipeContainerArgs(engine, input.workspaceDir, input.recipe, name, lent.dependencies);
1018
+ return await execute(engine, args, name, input.recipe, input.signal);
1019
+ } finally {
1020
+ await lent.release();
1021
+ }
906
1022
  }
907
1023
  };
908
1024
  }
@@ -924,10 +1040,14 @@ function parseMemory(value) {
924
1040
  function execute(engine, args, name, recipe2, signal) {
925
1041
  return runContainerCommand(engine, args, name, { timeoutMs: recipe2.timeoutMs, maxOutputBytes: recipe2.maxOutputBytes }, signal);
926
1042
  }
1043
+ var CONTAINER_PROGRESS = /^\[\d+\/\d+\] .*$/gm;
1044
+ function recipeOutput(engine, stderr) {
1045
+ return engine === "container" ? stderr.replace(CONTAINER_PROGRESS, "").replace(/^\n+/, "") : stderr;
1046
+ }
927
1047
  function runContainerCommand(engine, args, name, recipe2, signal) {
928
1048
  return new Promise((accept, reject) => {
929
1049
  const started = Date.now();
930
- const child = spawn2(engine, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
1050
+ const child = spawn3(engine, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
931
1051
  const stdout = [];
932
1052
  const stderr = [];
933
1053
  let bytes = 0;
@@ -940,7 +1060,7 @@ function runContainerCommand(engine, args, name, recipe2, signal) {
940
1060
  timedOut = reason === "timeout";
941
1061
  outputLimitExceeded = reason === "output";
942
1062
  const remove = engine === "container" ? ["delete", "--force", name] : ["rm", "-f", name];
943
- const killer = spawn2(engine, remove, { shell: false, stdio: "ignore" });
1063
+ const killer = spawn3(engine, remove, { shell: false, stdio: "ignore" });
944
1064
  killer.unref();
945
1065
  child.kill("SIGTERM");
946
1066
  };
@@ -966,7 +1086,7 @@ function runContainerCommand(engine, args, name, recipe2, signal) {
966
1086
  accept({
967
1087
  exitCode: code ?? 1,
968
1088
  stdout: Buffer.concat(stdout).toString("utf8"),
969
- stderr: Buffer.concat(stderr).toString("utf8"),
1089
+ stderr: recipeOutput(engine, Buffer.concat(stderr).toString("utf8")),
970
1090
  durationMs: Date.now() - started,
971
1091
  outputLimitExceeded,
972
1092
  timedOut
@@ -978,8 +1098,8 @@ function runContainerCommand(engine, args, name, recipe2, signal) {
978
1098
  // src/code-verifier.ts
979
1099
  import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
980
1100
  import { createReadStream } from "fs";
981
- import { lstat as lstat2 } from "fs/promises";
982
- import { join } from "path";
1101
+ import { lstat as lstat3 } from "fs/promises";
1102
+ import { join as join3 } from "path";
983
1103
  import {
984
1104
  digestCodeVerificationReceipt
985
1105
  } from "@odla-ai/camel/code";
@@ -1091,8 +1211,8 @@ async function inspectArtifacts(workspaceDir, recipe2) {
1091
1211
  const receipts = [];
1092
1212
  for (const artifact of recipe2.expectedArtifacts ?? []) {
1093
1213
  try {
1094
- const path = join(workspaceDir, artifact.path);
1095
- const info = await lstat2(path);
1214
+ const path = join3(workspaceDir, artifact.path);
1215
+ const info = await lstat3(path);
1096
1216
  if (!info.isFile() || info.isSymbolicLink()) {
1097
1217
  receipts.push({ artifactId: artifact.id, status: "invalid", bytes: null, digest: null });
1098
1218
  } else if (info.size > artifact.maximumBytes) {
@@ -1316,9 +1436,9 @@ var CodeRuntimeCheckpointManager = class {
1316
1436
 
1317
1437
  // src/code-runtime-archive.ts
1318
1438
  import { gunzipSync } from "zlib";
1319
- import { mkdir, mkdtemp, rm, writeFile } from "fs/promises";
1439
+ import { mkdir as mkdir2, mkdtemp, rm as rm3, writeFile } from "fs/promises";
1320
1440
  import { tmpdir } from "os";
1321
- import { dirname, join as join2, resolve as resolve3, sep as sep2 } from "path";
1441
+ import { dirname as dirname2, join as join4, resolve as resolve4, sep as sep3 } from "path";
1322
1442
  var RESERVED2 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
1323
1443
  var SECRET2 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
1324
1444
  var MAX_NUL_SHARE = 0.1;
@@ -1336,9 +1456,9 @@ async function materializeCodeRuntimeArchive(archive, visiblePaths, tempRoot = t
1336
1456
  throw new TypeError("Code source archive exceeds its decompressed byte bound");
1337
1457
  }
1338
1458
  const entries = parseTar(bytes, archive.limits);
1339
- const root = await mkdtemp(join2(tempRoot, "odla-code-archive-"));
1340
- const sourceDir = join2(root, "source");
1341
- await mkdir(sourceDir);
1459
+ const root = await mkdtemp(join4(tempRoot, "odla-code-archive-"));
1460
+ const sourceDir = join4(root, "source");
1461
+ await mkdir2(sourceDir);
1342
1462
  let visible = 0;
1343
1463
  try {
1344
1464
  for (const entry of entries) {
@@ -1351,16 +1471,16 @@ async function materializeCodeRuntimeArchive(archive, visiblePaths, tempRoot = t
1351
1471
  continue;
1352
1472
  }
1353
1473
  if (nulShare(content) > MAX_NUL_SHARE) continue;
1354
- const target = resolve3(sourceDir, entry.path);
1355
- if (!target.startsWith(`${resolve3(sourceDir)}${sep2}`)) throw new TypeError("Code source path escapes its root");
1356
- await mkdir(dirname(target), { recursive: true });
1474
+ const target = resolve4(sourceDir, entry.path);
1475
+ if (!target.startsWith(`${resolve4(sourceDir)}${sep3}`)) throw new TypeError("Code source path escapes its root");
1476
+ await mkdir2(dirname2(target), { recursive: true });
1357
1477
  await writeFile(target, content, { flag: "wx", mode: 420 });
1358
1478
  visible += 1;
1359
1479
  }
1360
1480
  if (!visible && !visiblePaths) throw new TypeError("GitHub commit has no Code-visible text source");
1361
- return { sourceDir, cleanup: () => rm(root, { recursive: true, force: true }) };
1481
+ return { sourceDir, cleanup: () => rm3(root, { recursive: true, force: true }) };
1362
1482
  } catch (cause) {
1363
- await rm(root, { recursive: true, force: true });
1483
+ await rm3(root, { recursive: true, force: true });
1364
1484
  throw cause;
1365
1485
  }
1366
1486
  }
@@ -1663,13 +1783,13 @@ async function prepareRuntimeLocalSource(input) {
1663
1783
  }
1664
1784
 
1665
1785
  // src/code-runtime-source.ts
1666
- import { mkdir as mkdir3, mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
1786
+ import { mkdir as mkdir4, mkdtemp as mkdtemp2, rm as rm4, writeFile as writeFile2 } from "fs/promises";
1667
1787
  import { tmpdir as tmpdir2 } from "os";
1668
- import { dirname as dirname3, join as join4, resolve as resolve4, sep as sep3 } from "path";
1788
+ import { dirname as dirname4, join as join6, resolve as resolve5, sep as sep4 } from "path";
1669
1789
 
1670
1790
  // src/code-runtime-selected-source.ts
1671
- import { chmod, cp, mkdir as mkdir2, readdir as readdir2 } from "fs/promises";
1672
- import { dirname as dirname2, join as join3 } from "path";
1791
+ import { chmod, cp, mkdir as mkdir3, readdir as readdir2 } from "fs/promises";
1792
+ import { dirname as dirname3, join as join5 } from "path";
1673
1793
  function selectedSourceSet(payload) {
1674
1794
  if (!payload.sourceSet) return null;
1675
1795
  const set = payload.sourceSet && typeof payload.sourceSet === "object" && !Array.isArray(payload.sourceSet) ? payload.sourceSet : null;
@@ -1706,8 +1826,8 @@ async function attachReferenceDirectories(workspace, references) {
1706
1826
  for (const reference of references) {
1707
1827
  validateAlias(reference.alias);
1708
1828
  for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
1709
- const target = join3(root, ".odla-references", reference.alias);
1710
- await mkdir2(dirname2(target), { recursive: true });
1829
+ const target = join5(root, ".odla-references", reference.alias);
1830
+ await mkdir3(dirname3(target), { recursive: true });
1711
1831
  await cp(reference.sourceDir, target, { recursive: true, errorOnExist: true, force: false });
1712
1832
  await makeTreeReadOnly(target);
1713
1833
  }
@@ -1720,7 +1840,7 @@ function validateAlias(alias) {
1720
1840
  }
1721
1841
  async function makeTreeReadOnly(root) {
1722
1842
  for (const entry of await readdir2(root, { withFileTypes: true })) {
1723
- const target = join3(root, entry.name);
1843
+ const target = join5(root, entry.name);
1724
1844
  if (entry.isDirectory()) await makeTreeReadOnly(target);
1725
1845
  else if (entry.isFile()) await chmod(target, 292);
1726
1846
  }
@@ -1734,9 +1854,9 @@ var SOURCE_MAX_BYTES = 80 * 1024 * 1024;
1734
1854
  var SOURCE_SET_MAX_BYTES = 480 * 1024 * 1024;
1735
1855
  async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir2()) {
1736
1856
  if (!snapshot.files.length || snapshot.files.length > SOURCE_MAX_FILES) throw new TypeError("Code source file count is invalid");
1737
- const root = await mkdtemp2(join4(tempRoot, "odla-code-source-"));
1738
- const sourceDir = join4(root, "source");
1739
- await mkdir3(sourceDir);
1857
+ const root = await mkdtemp2(join6(tempRoot, "odla-code-source-"));
1858
+ const sourceDir = join6(root, "source");
1859
+ await mkdir4(sourceDir);
1740
1860
  const seen = /* @__PURE__ */ new Set();
1741
1861
  let bytes = 0;
1742
1862
  try {
@@ -1746,9 +1866,9 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir2()) {
1746
1866
  seen.add(file.path);
1747
1867
  bytes += Buffer.byteLength(file.path) + Buffer.byteLength(file.content);
1748
1868
  if (bytes > SOURCE_MAX_BYTES) throw new TypeError("Code source exceeds its byte bound");
1749
- const target = resolve4(sourceDir, file.path);
1750
- if (!target.startsWith(`${resolve4(sourceDir)}${sep3}`)) throw new TypeError("Code source path escapes its root");
1751
- await mkdir3(dirname3(target), { recursive: true });
1869
+ const target = resolve5(sourceDir, file.path);
1870
+ if (!target.startsWith(`${resolve5(sourceDir)}${sep4}`)) throw new TypeError("Code source path escapes its root");
1871
+ await mkdir4(dirname4(target), { recursive: true });
1752
1872
  await writeFile2(target, file.content, { flag: "wx", mode: 420 });
1753
1873
  }
1754
1874
  for (const reference of snapshot.references ?? []) {
@@ -1761,15 +1881,15 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir2()) {
1761
1881
  seen.add(path);
1762
1882
  bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
1763
1883
  if (bytes > SOURCE_SET_MAX_BYTES) throw new TypeError("Code source set exceeds its byte bound");
1764
- const target = resolve4(sourceDir, path);
1765
- if (!target.startsWith(`${resolve4(sourceDir)}${sep3}`)) throw new TypeError("Code reference path escapes its root");
1766
- await mkdir3(dirname3(target), { recursive: true });
1884
+ const target = resolve5(sourceDir, path);
1885
+ if (!target.startsWith(`${resolve5(sourceDir)}${sep4}`)) throw new TypeError("Code reference path escapes its root");
1886
+ await mkdir4(dirname4(target), { recursive: true });
1767
1887
  await writeFile2(target, file.content, { flag: "wx", mode: 292 });
1768
1888
  }
1769
1889
  }
1770
- return { sourceDir, cleanup: () => rm2(root, { recursive: true, force: true }) };
1890
+ return { sourceDir, cleanup: () => rm4(root, { recursive: true, force: true }) };
1771
1891
  } catch (cause) {
1772
- await rm2(root, { recursive: true, force: true });
1892
+ await rm4(root, { recursive: true, force: true });
1773
1893
  throw cause;
1774
1894
  }
1775
1895
  }
@@ -1788,9 +1908,9 @@ async function attachCodeRuntimeReferences(workspace, references) {
1788
1908
  bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
1789
1909
  if (bytes > SOURCE_SET_MAX_BYTES - SOURCE_MAX_BYTES) throw new TypeError("Code reference set exceeds its byte bound");
1790
1910
  for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
1791
- const target = resolve4(root, path);
1792
- if (!target.startsWith(`${resolve4(root)}${sep3}`)) throw new TypeError("Code reference path escapes its root");
1793
- await mkdir3(dirname3(target), { recursive: true });
1911
+ const target = resolve5(root, path);
1912
+ if (!target.startsWith(`${resolve5(root)}${sep4}`)) throw new TypeError("Code reference path escapes its root");
1913
+ await mkdir4(dirname4(target), { recursive: true });
1794
1914
  await writeFile2(target, file.content, { flag: "wx", mode: 292 });
1795
1915
  }
1796
1916
  }
@@ -2236,7 +2356,7 @@ Finish with a concise, non-empty answer to the owner. Do not call tools or promi
2236
2356
  }
2237
2357
 
2238
2358
  // src/code-runtime-session-skills.ts
2239
- var sleep2 = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
2359
+ var sleep2 = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
2240
2360
  function createCodeRuntimeSessionSkillLoader(control, options = {}) {
2241
2361
  const wait2 = options.wait ?? sleep2;
2242
2362
  const load = control.collaborationSkills?.bind(control);
@@ -2295,7 +2415,7 @@ var inferWithBackoff = (infer, wait2, onRetry) => withOverloadRetry(infer, wait2
2295
2415
  async function handleCodeRuntimeInference(input) {
2296
2416
  const { command, request, state } = input;
2297
2417
  const startedAt = Date.now();
2298
- const wait2 = input.wait ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
2418
+ const wait2 = input.wait ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
2299
2419
  const response2 = await inferWithBackoff(
2300
2420
  () => input.control.infer(command.sessionId, {
2301
2421
  requestId: request.requestId,
@@ -2363,9 +2483,9 @@ function createCodeRuntimeInference(options) {
2363
2483
  }
2364
2484
 
2365
2485
  // src/code-tool-discovery.ts
2366
- import { spawn as spawn3 } from "child_process";
2486
+ import { spawn as spawn4 } from "child_process";
2367
2487
  import { readFile as readFile2, readdir as readdir3 } from "fs/promises";
2368
- import { relative as relative2, resolve as resolve5 } from "path";
2488
+ import { relative as relative2, resolve as resolve6 } from "path";
2369
2489
  var DEFAULT_MAX_FILES = 2e4;
2370
2490
  var DEFAULT_MAX_RESULTS = 100;
2371
2491
  var DEFAULT_MAX_FILE_BYTES = 512 * 1024;
@@ -2393,7 +2513,7 @@ async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
2393
2513
  for (const entry of await readdir3(directory, { withFileTypes: true })) {
2394
2514
  if (SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
2395
2515
  if (entry.isSymbolicLink()) throw new TypeError("workspace contains a symbolic link");
2396
- const target = resolve5(directory, entry.name);
2516
+ const target = resolve6(directory, entry.name);
2397
2517
  if (entry.isDirectory()) await walk(target);
2398
2518
  else if (entry.isFile()) {
2399
2519
  const path = relative2(root, target).split("\\").join("/");
@@ -2407,7 +2527,7 @@ async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
2407
2527
  }
2408
2528
  }
2409
2529
  };
2410
- await walk(resolve5(root));
2530
+ await walk(resolve6(root));
2411
2531
  return paths.sort();
2412
2532
  }
2413
2533
  function listWorkspace(paths, options = {}) {
@@ -2469,7 +2589,7 @@ function nativeSearchBatch(root, paths, options, remaining) {
2469
2589
  options.query,
2470
2590
  ...paths
2471
2591
  ];
2472
- const child = spawn3("rg", args, {
2592
+ const child = spawn4("rg", args, {
2473
2593
  cwd: root,
2474
2594
  stdio: ["ignore", "pipe", "ignore"],
2475
2595
  ...options.signal ? { signal: options.signal } : {}
@@ -2521,7 +2641,7 @@ async function fallbackSearch(root, scoped, options) {
2521
2641
  if (matches.length >= options.maxResults) break;
2522
2642
  let source;
2523
2643
  try {
2524
- source = await readFile2(resolve5(root, path));
2644
+ source = await readFile2(resolve6(root, path));
2525
2645
  } catch {
2526
2646
  continue;
2527
2647
  }
@@ -2825,7 +2945,7 @@ import { readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
2825
2945
 
2826
2946
  // src/code-tool-graph.ts
2827
2947
  import { readFile as readFile3 } from "fs/promises";
2828
- import { join as join5 } from "path";
2948
+ import { join as join7 } from "path";
2829
2949
  import {
2830
2950
  hubs,
2831
2951
  incident,
@@ -2839,7 +2959,7 @@ var cache = /* @__PURE__ */ new Map();
2839
2959
  function workspaceGraphs(workspaceDir, paths) {
2840
2960
  const existing = cache.get(workspaceDir);
2841
2961
  if (existing) return existing;
2842
- const read2 = (path) => readFile3(join5(workspaceDir, path), "utf8");
2962
+ const read2 = (path) => readFile3(join7(workspaceDir, path), "utf8");
2843
2963
  const built = (async () => ({
2844
2964
  // No knownTables: a staged workspace may not carry migrations, and a filter
2845
2965
  // that silently drops every table is worse than an unfiltered one. Callers
@@ -2949,8 +3069,8 @@ async function edit(context, request, options, policy, registry) {
2949
3069
  }
2950
3070
 
2951
3071
  // src/code-tool-write.ts
2952
- import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
2953
- import { dirname as dirname4 } from "path";
3072
+ import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
3073
+ import { dirname as dirname5 } from "path";
2954
3074
  var MAX_WRITE_BYTES = 256 * 1024;
2955
3075
  var lines = (text) => text === "" ? [] : text.replace(/\n$/, "").split("\n");
2956
3076
  function writeAsDiff(path, current, content) {
@@ -2975,7 +3095,7 @@ async function writeCodeFile(workspaceDir, path, content, current) {
2975
3095
  }
2976
3096
  if (content.includes("\0")) throw new TypeError("content contains NUL bytes; use plain text");
2977
3097
  const target = resolveCodePath(workspaceDir, path);
2978
- await mkdir4(dirname4(target), { recursive: true });
3098
+ await mkdir5(dirname5(target), { recursive: true });
2979
3099
  await writeFile4(target, content, "utf8");
2980
3100
  return { created: current === null, deletions: current === null ? 0 : lines(current).length, additions: lines(content).length };
2981
3101
  }
@@ -3003,7 +3123,7 @@ async function write(context, request, options, policy, registry) {
3003
3123
  }
3004
3124
 
3005
3125
  // src/code-tool-reads.ts
3006
- import { readFile as readFile6, stat } from "fs/promises";
3126
+ import { readFile as readFile6, stat as stat2 } from "fs/promises";
3007
3127
  var GRAPH_TOOLS = /* @__PURE__ */ new Set([
3008
3128
  "sandbox.overview",
3009
3129
  "sandbox.where_is",
@@ -3025,7 +3145,7 @@ async function read(context, request, options, policy, registry) {
3025
3145
  const allowed = await policy.read(policyContext(context, request, options, { paths, path, startLine, endLine }));
3026
3146
  if (!allowed) return response(request, false, "tool denied by CaMeL policy");
3027
3147
  const target = resolveCodePath(context.workspaceDir, path);
3028
- const info = await stat(target);
3148
+ const info = await stat2(target);
3029
3149
  if (!info.isFile() || info.size > Math.max(options.maxReadBytes ?? 128 * 1024, 2 * 1024 * 1024)) {
3030
3150
  throw new TypeError("file is not a bounded regular source file");
3031
3151
  }
@@ -3382,7 +3502,7 @@ function assertBudget(budget) {
3382
3502
 
3383
3503
  // src/code-repository-recipes.ts
3384
3504
  import { readFile as readFile7 } from "fs/promises";
3385
- import { join as join6 } from "path";
3505
+ import { join as join8 } from "path";
3386
3506
 
3387
3507
  // src/code-runtime-events.ts
3388
3508
  import { createHash as createHash3 } from "crypto";
@@ -3457,7 +3577,7 @@ function parseRepositoryRecipes(text, envelope) {
3457
3577
  async function readRepositoryRecipes(baselineDir, envelope) {
3458
3578
  let text;
3459
3579
  try {
3460
- text = await readFile7(join6(baselineDir, REPOSITORY_RECIPES_FILE), "utf8");
3580
+ text = await readFile7(join8(baselineDir, REPOSITORY_RECIPES_FILE), "utf8");
3461
3581
  } catch (cause) {
3462
3582
  if (cause.code === "ENOENT") return null;
3463
3583
  throw cause;
@@ -3690,8 +3810,8 @@ async function startGoalPursuit(input) {
3690
3810
  function codeRuntimeAcknowledgementGate(signal) {
3691
3811
  let settle;
3692
3812
  let settled = false;
3693
- const ready = new Promise((resolve6) => {
3694
- settle = resolve6;
3813
+ const ready = new Promise((resolve7) => {
3814
+ settle = resolve7;
3695
3815
  });
3696
3816
  const release = (run) => {
3697
3817
  if (settled) return;
@@ -4062,9 +4182,18 @@ export {
4062
4182
  createCodeWorkspaceCheckpoint,
4063
4183
  restoreCodeWorkspaceCheckpoint,
4064
4184
  isCheckpointEffectCompleted,
4185
+ assertLentPath,
4186
+ assertReservedMount,
4187
+ lentDirectories,
4188
+ withRecipeDependencies,
4189
+ installedDependencies,
4190
+ cloneTree,
4191
+ lendDependencies,
4192
+ lendBuildProducts,
4065
4193
  buildRecipeContainerArgs,
4066
4194
  createContainerRecipeExecutor,
4067
4195
  assertCodeBuildRecipe,
4196
+ recipeOutput,
4068
4197
  runContainerCommand,
4069
4198
  verifyCodeCandidate,
4070
4199
  prepareRuntimeCheckpoint,
@@ -4103,4 +4232,4 @@ export {
4103
4232
  withProofRecipes,
4104
4233
  TheseusRuntimeEngine
4105
4234
  };
4106
- //# sourceMappingURL=chunk-4IGSN53G.js.map
4235
+ //# sourceMappingURL=chunk-B2PQAORN.js.map