@odla-ai/cli 0.15.0 → 0.16.0

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/bin.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  exitCodeFor,
4
4
  runCli
5
- } from "./chunk-E3CCYDIT.js";
5
+ } from "./chunk-S3OYXHDG.js";
6
6
 
7
7
  // src/bin.ts
8
8
  runCli().catch((err) => {
@@ -2398,7 +2398,7 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
2398
2398
  }
2399
2399
  }
2400
2400
 
2401
- // ../harness/dist/chunk-QEW6NNAO.js
2401
+ // ../harness/dist/chunk-4EM6NMET.js
2402
2402
  import { createHash } from "crypto";
2403
2403
  import { readFile, readdir as readdir2 } from "fs/promises";
2404
2404
  import { relative as relative3, resolve as resolve4 } from "path";
@@ -2735,7 +2735,7 @@ function validateSnapshot(snapshot, limits) {
2735
2735
  }
2736
2736
  }
2737
2737
 
2738
- // ../harness/dist/chunk-QEW6NNAO.js
2738
+ // ../harness/dist/chunk-4EM6NMET.js
2739
2739
  import { spawn as spawn4 } from "child_process";
2740
2740
  import { lstat as lstat2 } from "fs/promises";
2741
2741
  import { resolve as resolve23, sep as sep3 } from "path";
@@ -3028,7 +3028,7 @@ function looksLikeDestination(value) {
3028
3028
  return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text);
3029
3029
  }
3030
3030
 
3031
- // ../harness/dist/chunk-QEW6NNAO.js
3031
+ // ../harness/dist/chunk-4EM6NMET.js
3032
3032
  import { createHash as createHash3 } from "crypto";
3033
3033
  async function digestStagedWorkspace(root, limits) {
3034
3034
  const files = [];
@@ -3276,10 +3276,28 @@ async function parseSource(value) {
3276
3276
  if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("source file");
3277
3277
  return { path: file.path, content: file.content };
3278
3278
  });
3279
+ const referencesValue = snapshot.references === void 0 ? [] : snapshot.references;
3280
+ if (!Array.isArray(referencesValue) || referencesValue.length > 5) throw invalid("reference sources");
3281
+ const aliases = /* @__PURE__ */ new Set();
3282
+ const references = [];
3283
+ for (const item of referencesValue) {
3284
+ const reference = record3(item);
3285
+ if (!reference || typeof reference.alias !== "string" || !/^[a-z][a-z0-9-]{0,39}$/.test(reference.alias) || aliases.has(reference.alias) || reference.alias === "primary" || typeof reference.repository !== "string" || typeof reference.commitSha !== "string" || typeof reference.treeDigest !== "string" || !Array.isArray(reference.files)) throw invalid("reference source");
3286
+ aliases.add(reference.alias);
3287
+ const referenceFiles = reference.files.map((entry) => {
3288
+ const file = record3(entry);
3289
+ if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("reference source file");
3290
+ return { path: file.path, content: file.content };
3291
+ });
3292
+ const source2 = { repository: reference.repository, commitSha: reference.commitSha, files: referenceFiles };
3293
+ const referenceDigest = await digestCodeRepositorySnapshot(source2, { maximumFiles: 1e4, maximumBytes: 16 * 1024 * 1024 });
3294
+ if (referenceDigest !== reference.treeDigest) throw invalid("reference source digest");
3295
+ references.push({ alias: reference.alias, ...source2, treeDigest: referenceDigest });
3296
+ }
3279
3297
  const source = { repository: snapshot.repository, commitSha: snapshot.commitSha, files };
3280
3298
  const digest = await digestCodeRepositorySnapshot(source, { maximumFiles: 1e4, maximumBytes: 16 * 1024 * 1024 });
3281
3299
  if (digest !== snapshot.treeDigest) throw invalid("source digest");
3282
- return { ...source, treeDigest: digest };
3300
+ return { ...source, treeDigest: digest, ...references.length ? { references } : {} };
3283
3301
  }
3284
3302
  function parseReview(value) {
3285
3303
  const review = record3(record3(value)?.review);
@@ -3810,7 +3828,7 @@ var CodeRuntimeCheckpointManager = class {
3810
3828
  await this.options.event(command, {
3811
3829
  type: "message",
3812
3830
  actor: "system",
3813
- body: `Candidate ${candidate.candidateId} is ready for exact owner publication approval`
3831
+ body: command.payload.sourceSet ? `Candidate ${candidate.candidateId} was verified and delivered to the session PR branch` : `Candidate ${candidate.candidateId} is ready for legacy owner publication approval`
3814
3832
  }, pending.refs);
3815
3833
  this.#pending.delete(command.commandId);
3816
3834
  return true;
@@ -3837,12 +3855,51 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir3()) {
3837
3855
  await mkdir3(dirname4(target), { recursive: true });
3838
3856
  await writeFile2(target, file.content, { flag: "wx", mode: 420 });
3839
3857
  }
3858
+ for (const reference of snapshot.references ?? []) {
3859
+ validateAlias(reference.alias);
3860
+ if (!reference.files.length || reference.files.length > 1e4) throw new TypeError("Code reference file count is invalid");
3861
+ for (const file of reference.files) {
3862
+ validatePath(file.path);
3863
+ const path = `.odla-references/${reference.alias}/${file.path}`;
3864
+ if (seen.has(path)) throw new TypeError("Code reference repeats a path");
3865
+ seen.add(path);
3866
+ bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
3867
+ if (bytes > 80 * 1024 * 1024) throw new TypeError("Code source set exceeds its byte bound");
3868
+ const target = resolve32(sourceDir, path);
3869
+ if (!target.startsWith(`${resolve32(sourceDir)}${sep22}`)) throw new TypeError("Code reference path escapes its root");
3870
+ await mkdir3(dirname4(target), { recursive: true });
3871
+ await writeFile2(target, file.content, { flag: "wx", mode: 292 });
3872
+ }
3873
+ }
3840
3874
  return { sourceDir, cleanup: () => rm3(root, { recursive: true, force: true }) };
3841
3875
  } catch (cause) {
3842
3876
  await rm3(root, { recursive: true, force: true });
3843
3877
  throw cause;
3844
3878
  }
3845
3879
  }
3880
+ function validateAlias(alias) {
3881
+ if (!/^[a-z][a-z0-9-]{0,39}$/.test(alias) || alias === "primary") {
3882
+ throw new TypeError("Code reference alias is invalid");
3883
+ }
3884
+ }
3885
+ async function attachCodeRuntimeReferences(workspace, references) {
3886
+ let bytes = 0;
3887
+ for (const reference of references) {
3888
+ validateAlias(reference.alias);
3889
+ for (const file of reference.files) {
3890
+ validatePath(file.path);
3891
+ const path = `.odla-references/${reference.alias}/${file.path}`;
3892
+ bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
3893
+ if (bytes > 64 * 1024 * 1024) throw new TypeError("Code reference set exceeds its byte bound");
3894
+ for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
3895
+ const target = resolve32(root, path);
3896
+ if (!target.startsWith(`${resolve32(root)}${sep22}`)) throw new TypeError("Code reference path escapes its root");
3897
+ await mkdir3(dirname4(target), { recursive: true });
3898
+ await writeFile2(target, file.content, { flag: "wx", mode: 292 });
3899
+ }
3900
+ }
3901
+ }
3902
+ }
3846
3903
  function validatePath(path) {
3847
3904
  const parts = path.split("/");
3848
3905
  if (!path || path.startsWith("/") || path.includes("\\") || path.includes("\0") || parts.some((part) => !part || part === "." || part === ".." || RESERVED2.has(part) || SECRET2.test(part))) {
@@ -4041,6 +4098,9 @@ async function patch(context, request, options, policy) {
4041
4098
  exactKeys(request.input, ["patch"]);
4042
4099
  const value = stringField(request.input, "patch");
4043
4100
  const paths = validateCodePatch(value, options.maxPatchBytes ?? 256 * 1024);
4101
+ if (paths.some((path) => options.readOnlyPrefixes?.some((prefix) => path === prefix || path.startsWith(`${prefix}/`)))) {
4102
+ throw new TypeError("patch targets a read-only reference source");
4103
+ }
4044
4104
  const allowed = await policy.patch(policyContext(context, request, options, { patch: value }));
4045
4105
  if (!allowed) return response(request, false, "tool denied by CaMeL policy");
4046
4106
  await applyCodePatch(context.workspaceDir, value, paths);
@@ -4126,6 +4186,9 @@ function validateOptions(options) {
4126
4186
  throw new TypeError("Code tool broker requires a reader and unique registered recipes");
4127
4187
  }
4128
4188
  for (const recipe2 of options.recipes) assertCodeBuildRecipe(recipe2);
4189
+ if (options.readOnlyPrefixes?.some((prefix) => !/^[A-Za-z0-9_.-]+$/.test(prefix) || prefix === "." || prefix === "..")) {
4190
+ throw new TypeError("Code tool broker read-only prefix is invalid");
4191
+ }
4129
4192
  }
4130
4193
  function exactKeys(input, allowed) {
4131
4194
  if (Object.keys(input).some((key) => !allowed.includes(key))) throw new TypeError("tool input contains an unsupported field");
@@ -4233,7 +4296,8 @@ function createCodeRuntimeToolBroker(input, lease, role) {
4233
4296
  recipes: input.recipes,
4234
4297
  recipeExecutor: createContainerRecipeExecutor(input.engine),
4235
4298
  recipeAuthorization: input.recipeAuthorization ?? "registered_recipe",
4236
- readerId: `code-session:${lease.task.taskId}`
4299
+ readerId: `code-session:${lease.task.taskId}`,
4300
+ readOnlyPrefixes: [".odla-references"]
4237
4301
  });
4238
4302
  return role === "coding" ? broker : { execute: (context, request) => request.tool === "sandbox.read" ? broker.execute(context, request) : Promise.resolve({ requestId: request.requestId, ok: false, content: "review sessions are read-only" }) };
4239
4303
  }
@@ -4311,6 +4375,14 @@ var CodePiRuntimeEngine = class {
4311
4375
  resume
4312
4376
  });
4313
4377
  ({ workspace, sourceDigest, trustedBaseDigest: localTrustedBaseDigest } = prepared);
4378
+ if (command.payload.sourceSet) {
4379
+ const selected = await this.options.control.source(command.sessionId);
4380
+ if (selected.repository !== metadata2.repository || selected.commitSha !== metadata2.baseCommitSha || selected.treeDigest !== metadata2.sourceTreeDigest) {
4381
+ await workspace.cleanup();
4382
+ throw new TypeError("Code local source does not match the selected GitHub primary source");
4383
+ }
4384
+ await attachCodeRuntimeReferences(workspace, selected.references ?? []);
4385
+ }
4314
4386
  } else {
4315
4387
  const source = await this.options.control.source(command.sessionId);
4316
4388
  const materialized = await materializeCodeRuntimeSource(source);
@@ -4338,8 +4410,8 @@ var CodePiRuntimeEngine = class {
4338
4410
  repository: metadata2.repository,
4339
4411
  sourceTreeDigest: metadata2.sourceTreeDigest,
4340
4412
  trustedBaseDigest: requestedLocal ? localTrustedBaseDigest : await digestStagedWorkspace(workspace.baselineDir, {
4341
- maxFiles: 1e4,
4342
- maxBytes: 16 * 1024 * 1024
4413
+ maxFiles: 2e4,
4414
+ maxBytes: 512 * 1024 * 1024
4343
4415
  }),
4344
4416
  planningInputDigest: metadata2.planningInputDigest ?? digestRuntimeValue(
4345
4417
  JSON.stringify({ attestation: metadata2.attestationDigest, prompt: metadata2.prompt, tree: sourceDigest })
@@ -7674,4 +7746,4 @@ export {
7674
7746
  exitCodeFor,
7675
7747
  runCli
7676
7748
  };
7677
- //# sourceMappingURL=chunk-E3CCYDIT.js.map
7749
+ //# sourceMappingURL=chunk-S3OYXHDG.js.map