@odla-ai/harness 0.11.9 → 0.11.10

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.
@@ -7,7 +7,7 @@ import {
7
7
  createCodeRuntimeControlClient,
8
8
  createCodeRuntimeSessionSkillLoader,
9
9
  runCodeRuntimeHeartbeatLoop
10
- } from "./chunk-X5KT7NHM.js";
10
+ } from "./chunk-4IGSN53G.js";
11
11
  import "./chunk-INL642J5.js";
12
12
  import {
13
13
  assertPinnedImage,
package/dist/node.cjs CHANGED
@@ -25,6 +25,7 @@ __export(node_exports, {
25
25
  CodeRuntimeCheckpointManager: () => CodeRuntimeCheckpointManager,
26
26
  CodeRuntimeControlError: () => CodeRuntimeControlError,
27
27
  CodeRuntimeReconciler: () => CodeRuntimeReconciler,
28
+ DEPENDENCY_INSTALL_TIMEOUT_MS: () => DEPENDENCY_INSTALL_TIMEOUT_MS,
28
29
  DecompositionError: () => DecompositionError,
29
30
  MAX_MEMORY_BODY: () => MAX_MEMORY_BODY,
30
31
  MEASURED_PREMIUM: () => MEASURED_PREMIUM,
@@ -51,6 +52,8 @@ __export(node_exports, {
51
52
  createCodeToolBroker: () => createCodeToolBroker,
52
53
  createCodeWorkspaceCheckpoint: () => createCodeWorkspaceCheckpoint,
53
54
  createContainerRecipeExecutor: () => createContainerRecipeExecutor,
55
+ dependencyCacheKey: () => dependencyCacheKey,
56
+ dependencyInstallArgs: () => dependencyInstallArgs,
54
57
  describeCodeRecipes: () => describeCodeRecipes,
55
58
  describeGateFailure: () => describeGateFailure,
56
59
  describePatchFailure: () => describePatchFailure,
@@ -70,6 +73,7 @@ __export(node_exports, {
70
73
  parseRepositoryRecipes: () => parseRepositoryRecipes,
71
74
  patchPaths: () => patchPaths,
72
75
  planReachCollisions: () => planReachCollisions,
76
+ prepareContainerDependencies: () => prepareContainerDependencies,
73
77
  prepareRuntimeCheckpoint: () => prepareRuntimeCheckpoint,
74
78
  racedAttempt: () => racedAttempt,
75
79
  readOnlyNotice: () => readOnlyNotice,
@@ -85,6 +89,7 @@ __export(node_exports, {
85
89
  runCodeAgentAttempt: () => runCodeAgentAttempt,
86
90
  runCodeRuntimeHeartbeatLoop: () => runCodeRuntimeHeartbeatLoop,
87
91
  runContainerAttempt: () => runContainerAttempt,
92
+ runContainerCommand: () => runContainerCommand,
88
93
  runGoal: () => runGoal,
89
94
  runHarnessRunner: () => runHarnessRunner,
90
95
  runLeasedAttempt: () => runLeasedAttempt,
@@ -94,6 +99,7 @@ __export(node_exports, {
94
99
  selectWinner: () => selectWinner,
95
100
  sessionRecipesFor: () => sessionRecipesFor,
96
101
  sessionSkillsFor: () => sessionSkillsFor,
102
+ stageManifests: () => stageManifests,
97
103
  stageWorkspace: () => stageWorkspace,
98
104
  stageWorkspacePair: () => stageWorkspacePair,
99
105
  straySubGoalFiles: () => straySubGoalFiles,
@@ -1793,6 +1799,9 @@ function parseMemory(value) {
1793
1799
  return Number(match[1]) * scale;
1794
1800
  }
1795
1801
  function execute(engine, args, name, recipe2, signal) {
1802
+ return runContainerCommand(engine, args, name, { timeoutMs: recipe2.timeoutMs, maxOutputBytes: recipe2.maxOutputBytes }, signal);
1803
+ }
1804
+ function runContainerCommand(engine, args, name, recipe2, signal) {
1796
1805
  return new Promise((accept, reject) => {
1797
1806
  const started = Date.now();
1798
1807
  const child = (0, import_node_child_process5.spawn)(engine, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
@@ -2893,7 +2902,7 @@ function codeSkill(opts) {
2893
2902
  },
2894
2903
  handler: (input, ctx) => call("sandbox.edit", input, ctx.signal)
2895
2904
  };
2896
- const writeFile6 = {
2905
+ const writeFile7 = {
2897
2906
  name: "odla_write_file",
2898
2907
  description: "Create a file or replace its entire contents with exactly the text given. Use this for a NEW file (a test, a module, a fixture) and for a rewrite where most lines change; use odla_edit_file for a small change inside an existing file. Paths are repository-relative; parent directories are created. The content must be the complete file.",
2899
2908
  inputSchema: {
@@ -2995,7 +3004,7 @@ function codeSkill(opts) {
2995
3004
  ];
2996
3005
  const runsRecipes = opts.recipes ?? !opts.readOnly;
2997
3006
  const effects = [...opts.readOnly ? [] : [applyPatch], ...runsRecipes ? [runRecipe] : []];
2998
- const tools = opts.surface === "v3" ? [...orientation, searchFiles, read2, ...opts.readOnly ? [] : [editFile, writeFile6], ...effects] : opts.surface === "v2" ? [listFiles, searchFiles, read2, ...effects] : [read2, ...effects];
3007
+ const tools = opts.surface === "v3" ? [...orientation, searchFiles, read2, ...opts.readOnly ? [] : [editFile, writeFile7], ...effects] : opts.surface === "v2" ? [listFiles, searchFiles, read2, ...effects] : [read2, ...effects];
2999
3008
  return { name: "code", tools };
3000
3009
  }
3001
3010
 
@@ -3826,8 +3835,8 @@ async function write(context, request, options, policy, registry) {
3826
3835
  if (options.readOnlyPrefixes?.some((prefix) => path === prefix || path.startsWith(`${prefix}/`))) {
3827
3836
  throw new TypeError("write targets a read-only reference source");
3828
3837
  }
3829
- const exists = (await registry.files(context.workspaceDir)).includes(path);
3830
- const current = exists ? await (0, import_promises13.readFile)(resolveCodePath(context.workspaceDir, path), "utf8") : null;
3838
+ const exists2 = (await registry.files(context.workspaceDir)).includes(path);
3839
+ const current = exists2 ? await (0, import_promises13.readFile)(resolveCodePath(context.workspaceDir, path), "utf8") : null;
3831
3840
  const patch2 = writeAsDiff(path, current, content);
3832
3841
  const allowed = await policy.write(policyContext(context, request, options, { patch: patch2 }));
3833
3842
  if (!allowed) return response(request, false, "tool denied by CaMeL policy");
@@ -5359,6 +5368,95 @@ async function installedDependencies(repoRoot) {
5359
5368
  const info = await (0, import_promises17.lstat)(source).catch(() => null);
5360
5369
  return info?.isDirectory() ? { source } : null;
5361
5370
  }
5371
+
5372
+ // src/code-recipe-dependency-cache.ts
5373
+ var import_node_crypto5 = require("crypto");
5374
+ var import_promises18 = require("fs/promises");
5375
+ var import_node_process3 = require("process");
5376
+ var import_node_path16 = require("path");
5377
+ var DEPENDENCY_INSTALL_TIMEOUT_MS = 20 * 6e4;
5378
+ var OUTPUT_CAP = 4 * 1024 * 1024;
5379
+ var exists = async (path) => Boolean(await (0, import_promises18.stat)(path).catch(() => null));
5380
+ async function dependencyCacheKey(repoRoot, image, arch = process.arch) {
5381
+ const lock = await (0, import_promises18.readFile)((0, import_node_path16.join)(repoRoot, "package-lock.json")).catch(() => null);
5382
+ if (!lock) return null;
5383
+ return (0, import_node_crypto5.createHash)("sha256").update(lock).update("\n").update(image).update("\n").update(arch).digest("hex").slice(0, 32);
5384
+ }
5385
+ async function stageManifests(repoRoot, stageDir) {
5386
+ await (0, import_promises18.mkdir)(stageDir, { recursive: true });
5387
+ const staged = [];
5388
+ for (const name of ["package.json", "package-lock.json", ".npmrc"]) {
5389
+ if (!await exists((0, import_node_path16.join)(repoRoot, name))) continue;
5390
+ await (0, import_promises18.copyFile)((0, import_node_path16.join)(repoRoot, name), (0, import_node_path16.join)(stageDir, name));
5391
+ staged.push(name);
5392
+ }
5393
+ const root = JSON.parse(await (0, import_promises18.readFile)((0, import_node_path16.join)(repoRoot, "package.json"), "utf8"));
5394
+ const globs = Array.isArray(root.workspaces) ? root.workspaces : root.workspaces?.packages ?? [];
5395
+ for (const glob of globs) {
5396
+ const dirs = glob.endsWith("/*") ? (await (0, import_promises18.readdir)((0, import_node_path16.join)(repoRoot, glob.slice(0, -2)), { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map((entry) => `${glob.slice(0, -2)}/${entry.name}`) : [glob];
5397
+ for (const dir of dirs) {
5398
+ const manifest = (0, import_node_path16.join)(repoRoot, dir, "package.json");
5399
+ if (!await exists(manifest)) continue;
5400
+ await (0, import_promises18.mkdir)((0, import_node_path16.join)(stageDir, dir), { recursive: true });
5401
+ await (0, import_promises18.copyFile)(manifest, (0, import_node_path16.join)(stageDir, dir, "package.json"));
5402
+ staged.push(`${dir}/package.json`);
5403
+ }
5404
+ }
5405
+ return staged;
5406
+ }
5407
+ function dependencyInstallArgs(engine, image, stageDir, name) {
5408
+ if (/[,\r\n]/.test(stageDir)) throw new TypeError("stage path contains unsupported mount characters");
5409
+ const uid = typeof import_node_process3.getuid === "function" ? (0, import_node_process3.getuid)() : 1e3;
5410
+ const gid = typeof import_node_process3.getgid === "function" ? (0, import_node_process3.getgid)() : 1e3;
5411
+ const mount = engine === "container" ? `--mount=type=bind,source=${stageDir},target=/workspace` : `--mount=type=bind,src=${stageDir},dst=/workspace`;
5412
+ const npm = ["npm", "ci", "--no-audit", "--no-fund", "--ignore-scripts"];
5413
+ return [
5414
+ "run",
5415
+ "--rm",
5416
+ `--name=${name}`,
5417
+ "--cap-drop=ALL",
5418
+ "--memory=2g",
5419
+ "--cpus=2",
5420
+ `--user=${uid}:${gid}`,
5421
+ "--tmpfs=/tmp",
5422
+ mount,
5423
+ "--workdir=/workspace",
5424
+ "--env=CI=1",
5425
+ "--env=npm_config_cache=/tmp/npm-cache",
5426
+ "--env=HOME=/tmp",
5427
+ image,
5428
+ ...npm
5429
+ ];
5430
+ }
5431
+ async function prepareContainerDependencies(input) {
5432
+ const key = await dependencyCacheKey(input.repoRoot, input.image);
5433
+ if (!key) return null;
5434
+ const dir = (0, import_node_path16.join)(input.cacheRoot, key);
5435
+ const tree = (0, import_node_path16.join)(dir, "node_modules");
5436
+ const stage = (0, import_node_path16.join)(dir, "stage");
5437
+ if (await exists(tree)) return { source: tree };
5438
+ const run = input.run ?? runContainerCommand;
5439
+ await (0, import_promises18.rm)(stage, { recursive: true, force: true });
5440
+ const staged = await stageManifests(input.repoRoot, stage);
5441
+ input.log?.(`installing ${key.slice(0, 12)}: ${staged.length} manifests, npm ci in ${input.image}`);
5442
+ const name = `odla-deps-${key.slice(0, 12)}`;
5443
+ const result = await run(
5444
+ input.engine,
5445
+ dependencyInstallArgs(input.engine, input.image, stage, name),
5446
+ name,
5447
+ { timeoutMs: DEPENDENCY_INSTALL_TIMEOUT_MS, maxOutputBytes: OUTPUT_CAP }
5448
+ );
5449
+ if (result.exitCode !== 0 || result.timedOut || !await exists((0, import_node_path16.join)(stage, "node_modules"))) {
5450
+ input.log?.(`install failed (exit ${result.exitCode}${result.timedOut ? ", timed out" : ""}): ${result.stderr.trim().split("\n").slice(-3).join(" | ").slice(0, 400)}`);
5451
+ await (0, import_promises18.rm)(stage, { recursive: true, force: true });
5452
+ return null;
5453
+ }
5454
+ await (0, import_promises18.rename)((0, import_node_path16.join)(stage, "node_modules"), tree);
5455
+ await (0, import_promises18.rm)(stage, { recursive: true, force: true });
5456
+ await (0, import_promises18.writeFile)((0, import_node_path16.join)(dir, "manifest.json"), JSON.stringify({ image: input.image, key, createdAt: Date.now() }, null, 2));
5457
+ input.log?.(`installed ${key.slice(0, 12)} in ${Math.round(result.durationMs / 1e3)}s`);
5458
+ return { source: tree };
5459
+ }
5362
5460
  // Annotate the CommonJS export names for ESM import in node:
5363
5461
  0 && (module.exports = {
5364
5462
  CODE_PATCH_PATH,
@@ -5366,6 +5464,7 @@ async function installedDependencies(repoRoot) {
5366
5464
  CodeRuntimeCheckpointManager,
5367
5465
  CodeRuntimeControlError,
5368
5466
  CodeRuntimeReconciler,
5467
+ DEPENDENCY_INSTALL_TIMEOUT_MS,
5369
5468
  DecompositionError,
5370
5469
  MAX_MEMORY_BODY,
5371
5470
  MEASURED_PREMIUM,
@@ -5392,6 +5491,8 @@ async function installedDependencies(repoRoot) {
5392
5491
  createCodeToolBroker,
5393
5492
  createCodeWorkspaceCheckpoint,
5394
5493
  createContainerRecipeExecutor,
5494
+ dependencyCacheKey,
5495
+ dependencyInstallArgs,
5395
5496
  describeCodeRecipes,
5396
5497
  describeGateFailure,
5397
5498
  describePatchFailure,
@@ -5411,6 +5512,7 @@ async function installedDependencies(repoRoot) {
5411
5512
  parseRepositoryRecipes,
5412
5513
  patchPaths,
5413
5514
  planReachCollisions,
5515
+ prepareContainerDependencies,
5414
5516
  prepareRuntimeCheckpoint,
5415
5517
  racedAttempt,
5416
5518
  readOnlyNotice,
@@ -5426,6 +5528,7 @@ async function installedDependencies(repoRoot) {
5426
5528
  runCodeAgentAttempt,
5427
5529
  runCodeRuntimeHeartbeatLoop,
5428
5530
  runContainerAttempt,
5531
+ runContainerCommand,
5429
5532
  runGoal,
5430
5533
  runHarnessRunner,
5431
5534
  runLeasedAttempt,
@@ -5435,6 +5538,7 @@ async function installedDependencies(repoRoot) {
5435
5538
  selectWinner,
5436
5539
  sessionRecipesFor,
5437
5540
  sessionSkillsFor,
5541
+ stageManifests,
5438
5542
  stageWorkspace,
5439
5543
  stageWorkspacePair,
5440
5544
  straySubGoalFiles,