@odla-ai/cli 0.16.2 → 0.16.4

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.
@@ -1775,12 +1775,16 @@ async function defaultReadOrigin(cwd) {
1775
1775
  }
1776
1776
 
1777
1777
  // src/code-runtime-config.ts
1778
- var CODE_PI_IMAGE = "ghcr.io/cory/odla-pi-agent@sha256:e7858b4f0291543171b2659b47c5d00f97acc4767cafd9b74ae5228899e0cde4";
1778
+ var CODE_PI_IMAGE = "odla-ai/pi-agent:embedded";
1779
+ var CODE_NODE_IMAGE = "node:24-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd";
1779
1780
  var CODE_BUILD_RECIPES = Object.freeze([{
1780
1781
  id: "odla-code-contracts",
1781
- image: "node:24-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd",
1782
+ image: CODE_NODE_IMAGE,
1782
1783
  command: [
1783
1784
  "node",
1785
+ // The clean source mount deliberately has no node_modules. Reuse the
1786
+ // semver copy inside this digest-pinned npm image without network access.
1787
+ "--import=data:text/javascript,import%20Module%20from%20%22node%3Amodule%22%3Bprocess.env.NODE_PATH%3D%22%2Fusr%2Flocal%2Flib%2Fnode_modules%2Fnpm%2Fnode_modules%22%3BModule._initPaths%28%29",
1784
1788
  "--test",
1785
1789
  "scripts/lib/directories.test.mjs",
1786
1790
  "scripts/lib/internal-deps.test.mjs",
@@ -1795,15 +1799,95 @@ var CODE_BUILD_RECIPES = Object.freeze([{
1795
1799
  pids: 128
1796
1800
  }]);
1797
1801
 
1802
+ // src/code-images.ts
1803
+ import { spawn as spawn2 } from "child_process";
1804
+ import { createHash } from "crypto";
1805
+ import { copyFile, mkdtemp, readFile, rm, writeFile } from "fs/promises";
1806
+ import { tmpdir } from "os";
1807
+ import { join as join2 } from "path";
1808
+ import { fileURLToPath } from "url";
1809
+ var runCodeImageCommand = (command, args, stdio) => new Promise((accept, reject) => {
1810
+ const child = spawn2(command, [...args], { shell: false, stdio });
1811
+ child.once("error", reject);
1812
+ child.once("exit", (code, signal) => {
1813
+ if (code === 0) accept();
1814
+ else reject(new Error(`${command} ${args.join(" ")} exited ${code ?? signal ?? "without a status"}`));
1815
+ });
1816
+ });
1817
+ async function prepareCodeImages(engine, images, run = runCodeImageCommand, buildEmbedded = buildEmbeddedPiImage, nameEmbedded = embeddedPiImageName) {
1818
+ if (engine === "container") {
1819
+ try {
1820
+ await run(engine, ["system", "start"], "inherit");
1821
+ } catch {
1822
+ throw new Error("Apple container could not start; run `container system start` once to complete its lightweight VM setup, then retry");
1823
+ }
1824
+ }
1825
+ const prepared = [];
1826
+ for (const image of images) {
1827
+ const runtimeImage = image === CODE_PI_IMAGE ? await nameEmbedded() : image;
1828
+ const inspectArgs = ["image", "inspect", runtimeImage];
1829
+ try {
1830
+ await run(engine, inspectArgs, "ignore");
1831
+ prepared.push(runtimeImage);
1832
+ continue;
1833
+ } catch {
1834
+ }
1835
+ if (image === CODE_PI_IMAGE) {
1836
+ try {
1837
+ await buildEmbedded(engine, runtimeImage, run);
1838
+ } catch (error) {
1839
+ const detail = error instanceof Error && error.message ? `: ${error.message}` : "";
1840
+ throw new Error(`could not prepare CLI-embedded Code image${detail}`);
1841
+ }
1842
+ prepared.push(runtimeImage);
1843
+ continue;
1844
+ }
1845
+ const args = engine === "container" ? ["image", "pull", image] : ["pull", image];
1846
+ try {
1847
+ await run(engine, args, "inherit");
1848
+ } catch (error) {
1849
+ const detail = error instanceof Error && error.message ? `: ${error.message}` : "";
1850
+ throw new Error(`could not prepare pinned Code image ${image}${detail}`);
1851
+ }
1852
+ prepared.push(image);
1853
+ }
1854
+ return prepared;
1855
+ }
1856
+ function embeddedPiAssetPath() {
1857
+ return fileURLToPath(new URL("./runtime/pi-agent.js", import.meta.url));
1858
+ }
1859
+ async function embeddedPiImageName() {
1860
+ const bundle = await readFile(embeddedPiAssetPath()).catch(() => {
1861
+ throw new Error("CLI-embedded Pi runtime is missing; reinstall this exact @odla-ai/cli version");
1862
+ });
1863
+ return `odla-ai/pi-agent:embedded-sha256-${createHash("sha256").update(bundle).digest("hex")}`;
1864
+ }
1865
+ async function buildEmbeddedPiImage(engine, image, run) {
1866
+ const context = await mkdtemp(join2(tmpdir(), "odla-code-pi-"));
1867
+ try {
1868
+ await copyFile(embeddedPiAssetPath(), join2(context, "pi-agent.js"));
1869
+ await writeFile(join2(context, "Dockerfile"), [
1870
+ `FROM ${CODE_NODE_IMAGE}`,
1871
+ "COPY pi-agent.js /opt/odla/pi-agent.js",
1872
+ "WORKDIR /workspace",
1873
+ 'ENTRYPOINT ["node", "/opt/odla/pi-agent.js"]',
1874
+ ""
1875
+ ].join("\n"), { mode: 384 });
1876
+ await run(engine, ["build", "--tag", image, context], "inherit");
1877
+ } finally {
1878
+ await rm(context, { recursive: true, force: true });
1879
+ }
1880
+ }
1881
+
1798
1882
  // src/code-connect.ts
1799
1883
  import { existsSync as existsSync4 } from "fs";
1800
1884
  import { cpus, hostname, totalmem } from "os";
1801
1885
  import { resolve as resolve5 } from "path";
1802
1886
 
1803
- // ../harness/dist/chunk-7SN5SG4N.js
1887
+ // ../harness/dist/chunk-QTUEF2HZ.js
1804
1888
  var HARNESS_PROTOCOL_VERSION = 1;
1805
1889
 
1806
- // ../harness/dist/chunk-HX4QYGWE.js
1890
+ // ../harness/dist/chunk-GE6CCN7W.js
1807
1891
  var CONTROL = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/;
1808
1892
  var HarnessProtocolError = class extends Error {
1809
1893
  name = "HarnessProtocolError";
@@ -1881,21 +1965,21 @@ function encodeAgentInput(message2) {
1881
1965
  `;
1882
1966
  }
1883
1967
 
1884
- // ../harness/dist/chunk-QWWRL7M4.js
1885
- import { execFile as execFile2, spawn as spawn2 } from "child_process";
1968
+ // ../harness/dist/chunk-NOBK6PCT.js
1969
+ import { execFile as execFile2, spawn as spawn3 } from "child_process";
1886
1970
  import { constants } from "fs";
1887
1971
  import { access } from "fs/promises";
1888
- import { delimiter, join as join2 } from "path";
1972
+ import { delimiter, join as join3 } from "path";
1889
1973
  import { getgid, getuid } from "process";
1890
- import { mkdir, mkdtemp, realpath, rm, writeFile } from "fs/promises";
1891
- import { tmpdir } from "os";
1974
+ import { mkdir, mkdtemp as mkdtemp2, realpath, rm as rm2, writeFile as writeFile2 } from "fs/promises";
1975
+ import { tmpdir as tmpdir2 } from "os";
1892
1976
  import { join as join22, resolve as resolve3, sep } from "path";
1893
1977
  import { spawn as spawn22 } from "child_process";
1894
1978
  import { isAbsolute as isAbsolute3 } from "path";
1895
- import { chmod, copyFile, lstat, mkdir as mkdir2, mkdtemp as mkdtemp2, readdir, realpath as realpath2, rm as rm2, stat } from "fs/promises";
1896
- import { tmpdir as tmpdir2 } from "os";
1897
- import { basename, join as join3, relative as relative2, resolve as resolve22, sep as sep2 } from "path";
1898
- import { spawn as spawn3 } from "child_process";
1979
+ import { chmod, copyFile as copyFile2, lstat, mkdir as mkdir2, mkdtemp as mkdtemp22, readdir, realpath as realpath2, rm as rm22, stat } from "fs/promises";
1980
+ import { tmpdir as tmpdir22 } from "os";
1981
+ import { basename, join as join32, relative as relative2, resolve as resolve22, sep as sep2 } from "path";
1982
+ import { spawn as spawn32 } from "child_process";
1899
1983
  var DIGEST_IMAGE = /^[a-z0-9][a-z0-9._/-]*(?::[a-zA-Z0-9._-]+)?@sha256:[0-9a-f]{64}$/;
1900
1984
  function assertPinnedImage(image) {
1901
1985
  if (!DIGEST_IMAGE.test(image)) throw new TypeError("container image must be pinned by sha256 digest");
@@ -1903,7 +1987,7 @@ function assertPinnedImage(image) {
1903
1987
  async function commandAvailable(engine) {
1904
1988
  for (const directory of (process.env.PATH ?? "").split(delimiter).filter(Boolean)) {
1905
1989
  try {
1906
- await access(join2(directory, engine), constants.X_OK);
1990
+ await access(join3(directory, engine), constants.X_OK);
1907
1991
  return true;
1908
1992
  } catch {
1909
1993
  }
@@ -1934,7 +2018,7 @@ async function selectContainerEngine(requested = "auto", options = {}) {
1934
2018
  throw new TypeError("no rootless Podman found; install Podman or explicitly choose --engine docker after reviewing its daemon boundary");
1935
2019
  }
1936
2020
  if (platform === "darwin") {
1937
- throw new TypeError("no Apple container or Podman Machine found; install one or explicitly choose --engine docker");
2021
+ throw new TypeError("Apple container is not installed; on Apple Silicon macOS 26 run `brew install container`, then retry (Podman Machine is the fallback)");
1938
2022
  }
1939
2023
  throw new TypeError("no supported container engine found");
1940
2024
  }
@@ -2027,7 +2111,7 @@ async function runContainerAttempt(options) {
2027
2111
  await verifyContainerEngineBoundary(options.engine);
2028
2112
  const args = buildContainerRunArgs(options);
2029
2113
  const name = containerName(args);
2030
- const child = spawn2(options.engine, args, { stdio: ["pipe", "pipe", "pipe"], shell: false });
2114
+ const child = spawn3(options.engine, args, { stdio: ["pipe", "pipe", "pipe"], shell: false });
2031
2115
  let stderr = "";
2032
2116
  let outputBytes = 0;
2033
2117
  let complete = null;
@@ -2045,7 +2129,7 @@ async function runContainerAttempt(options) {
2045
2129
  child.stdin.write(encodeAgentInput(cancel));
2046
2130
  }
2047
2131
  const removeArgs = options.engine === "container" ? ["delete", "--force", name] : ["rm", "-f", name];
2048
- const killer = spawn2(options.engine, removeArgs, { stdio: "ignore", shell: false });
2132
+ const killer = spawn3(options.engine, removeArgs, { stdio: "ignore", shell: false });
2049
2133
  killer.unref();
2050
2134
  };
2051
2135
  const abort = () => stop("runner_cancelled");
@@ -2198,7 +2282,7 @@ async function materializeGitTree(source, commitSha, options = {}) {
2198
2282
  return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
2199
2283
  });
2200
2284
  if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
2201
- const root = await mkdtemp(join22(options.tempRoot ?? tmpdir(), "odla-git-tree-"));
2285
+ const root = await mkdtemp2(join22(options.tempRoot ?? tmpdir2(), "odla-git-tree-"));
2202
2286
  const targetRoot = join22(root, "source");
2203
2287
  await mkdir(targetRoot);
2204
2288
  let byteCount = 0;
@@ -2211,17 +2295,17 @@ async function materializeGitTree(source, commitSha, options = {}) {
2211
2295
  const target = resolve3(targetRoot, entry.path);
2212
2296
  if (!target.startsWith(`${resolve3(targetRoot)}${sep}`)) throw new TypeError("Git tree path escapes workspace");
2213
2297
  await mkdir(resolve3(target, ".."), { recursive: true });
2214
- await writeFile(target, content, { flag: "wx", mode: entry.mode === "100755" ? 493 : 420 });
2298
+ await writeFile2(target, content, { flag: "wx", mode: entry.mode === "100755" ? 493 : 420 });
2215
2299
  }
2216
2300
  return {
2217
2301
  root,
2218
2302
  sourceDir: targetRoot,
2219
2303
  fileCount: entries.length,
2220
2304
  byteCount,
2221
- cleanup: () => rm(root, { recursive: true, force: true })
2305
+ cleanup: () => rm2(root, { recursive: true, force: true })
2222
2306
  };
2223
2307
  } catch (error) {
2224
- await rm(root, { recursive: true, force: true });
2308
+ await rm2(root, { recursive: true, force: true });
2225
2309
  throw error;
2226
2310
  }
2227
2311
  }
@@ -2232,7 +2316,7 @@ async function sourceFiles(sourceDir, maxFiles, maxBytes) {
2232
2316
  for (const entry of await readdir(dir, { withFileTypes: true })) {
2233
2317
  if (entry.isDirectory() && SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
2234
2318
  if (!entry.isDirectory() && SECRET_WORKSPACE_FILE.test(entry.name)) continue;
2235
- const path = join3(dir, entry.name);
2319
+ const path = join32(dir, entry.name);
2236
2320
  if (entry.isSymbolicLink()) continue;
2237
2321
  if (entry.isDirectory()) {
2238
2322
  await walk(path);
@@ -2255,7 +2339,7 @@ async function sourceFiles(sourceDir, maxFiles, maxBytes) {
2255
2339
  return files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
2256
2340
  }
2257
2341
  async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
2258
- const child = spawn3("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
2342
+ const child = spawn32("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
2259
2343
  cwd: sourceDir,
2260
2344
  stdio: ["ignore", "pipe", "pipe"],
2261
2345
  shell: false
@@ -2302,14 +2386,14 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
2302
2386
  }
2303
2387
  async function copyTree(files, destination) {
2304
2388
  for (const file of files) {
2305
- const target = join3(destination, file.relativePath);
2389
+ const target = join32(destination, file.relativePath);
2306
2390
  await mkdir2(resolve22(target, ".."), { recursive: true });
2307
- await copyFile(file.source, target);
2391
+ await copyFile2(file.source, target);
2308
2392
  await chmod(target, file.mode);
2309
2393
  }
2310
2394
  }
2311
2395
  async function captureGitDiff(root, maxBytes) {
2312
- const child = spawn3("git", [
2396
+ const child = spawn32("git", [
2313
2397
  "diff",
2314
2398
  "--no-index",
2315
2399
  "--binary",
@@ -2345,9 +2429,9 @@ async function stageWorkspace(source, options = {}) {
2345
2429
  const sourceDir = await realpath2(resolve22(source));
2346
2430
  const sourceStat = await stat(sourceDir);
2347
2431
  if (!sourceStat.isDirectory()) throw new TypeError("workspace source must be a directory");
2348
- const root = await mkdtemp2(join3(options.tempRoot ?? tmpdir2(), "odla-harness-"));
2349
- const baselineDir = join3(root, "baseline");
2350
- const workspaceDir = join3(root, "workspace");
2432
+ const root = await mkdtemp22(join32(options.tempRoot ?? tmpdir22(), "odla-harness-"));
2433
+ const baselineDir = join32(root, "baseline");
2434
+ const workspaceDir = join32(root, "workspace");
2351
2435
  await Promise.all([mkdir2(baselineDir), mkdir2(workspaceDir)]);
2352
2436
  try {
2353
2437
  const maxFiles = options.maxFiles ?? 2e4;
@@ -2361,10 +2445,10 @@ async function stageWorkspace(source, options = {}) {
2361
2445
  fileCount: files.length,
2362
2446
  byteCount: files.reduce((sum, file) => sum + file.bytes, 0),
2363
2447
  patch: (maxBytes2) => captureGitDiff(root, maxBytes2),
2364
- cleanup: () => rm2(root, { recursive: true, force: true })
2448
+ cleanup: () => rm22(root, { recursive: true, force: true })
2365
2449
  };
2366
2450
  } catch (error) {
2367
- await rm2(root, { recursive: true, force: true });
2451
+ await rm22(root, { recursive: true, force: true });
2368
2452
  throw error;
2369
2453
  }
2370
2454
  }
@@ -2377,9 +2461,9 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
2377
2461
  sourceFiles(baselineDirSource, maxFiles, maxBytes),
2378
2462
  sourceFiles(workspaceDirSource, maxFiles, maxBytes)
2379
2463
  ]);
2380
- const root = await mkdtemp2(join3(options.tempRoot ?? tmpdir2(), "odla-harness-"));
2381
- const baselineDir = join3(root, "baseline");
2382
- const workspaceDir = join3(root, "workspace");
2464
+ const root = await mkdtemp22(join32(options.tempRoot ?? tmpdir22(), "odla-harness-"));
2465
+ const baselineDir = join32(root, "baseline");
2466
+ const workspaceDir = join32(root, "workspace");
2383
2467
  await Promise.all([mkdir2(baselineDir), mkdir2(workspaceDir)]);
2384
2468
  try {
2385
2469
  await Promise.all([copyTree(baselineFiles, baselineDir), copyTree(workspaceFiles, workspaceDir)]);
@@ -2390,17 +2474,17 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
2390
2474
  fileCount: workspaceFiles.length,
2391
2475
  byteCount: workspaceFiles.reduce((sum, file) => sum + file.bytes, 0),
2392
2476
  patch: (maxPatchBytes) => captureGitDiff(root, maxPatchBytes),
2393
- cleanup: () => rm2(root, { recursive: true, force: true })
2477
+ cleanup: () => rm22(root, { recursive: true, force: true })
2394
2478
  };
2395
2479
  } catch (error) {
2396
- await rm2(root, { recursive: true, force: true });
2480
+ await rm22(root, { recursive: true, force: true });
2397
2481
  throw error;
2398
2482
  }
2399
2483
  }
2400
2484
 
2401
- // ../harness/dist/chunk-T2RD6ZAW.js
2402
- import { createHash } from "crypto";
2403
- import { readFile, readdir as readdir2 } from "fs/promises";
2485
+ // ../harness/dist/chunk-6J2ZETST.js
2486
+ import { createHash as createHash2 } from "crypto";
2487
+ import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
2404
2488
  import { relative as relative3, resolve as resolve4 } from "path";
2405
2489
 
2406
2490
  // ../camel/dist/chunk-7FHPOQVP.js
@@ -2735,21 +2819,21 @@ function validateSnapshot(snapshot, limits) {
2735
2819
  }
2736
2820
  }
2737
2821
 
2738
- // ../harness/dist/chunk-T2RD6ZAW.js
2822
+ // ../harness/dist/chunk-6J2ZETST.js
2739
2823
  import { spawn as spawn4 } from "child_process";
2740
2824
  import { lstat as lstat2 } from "fs/promises";
2741
2825
  import { resolve as resolve23, sep as sep3 } from "path";
2742
2826
  import { spawn as spawn23 } from "child_process";
2743
2827
  import { getgid as getgid2, getuid as getuid2 } from "process";
2744
2828
  import { randomUUID } from "crypto";
2745
- import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
2829
+ import { createHash as createHash22, randomUUID as randomUUID2 } from "crypto";
2746
2830
  import { createReadStream } from "fs";
2747
2831
  import { lstat as lstat22 } from "fs/promises";
2748
2832
  import { join as join4 } from "path";
2749
- import { mkdir as mkdir3, mkdtemp as mkdtemp3, rm as rm3, writeFile as writeFile2 } from "fs/promises";
2833
+ import { mkdir as mkdir3, mkdtemp as mkdtemp3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
2750
2834
  import { tmpdir as tmpdir3 } from "os";
2751
2835
  import { dirname as dirname4, join as join23, resolve as resolve32, sep as sep22 } from "path";
2752
- import { readFile as readFile2, readdir as readdir22, stat as stat2 } from "fs/promises";
2836
+ import { readFile as readFile22, readdir as readdir22, stat as stat2 } from "fs/promises";
2753
2837
  import { relative as relative22, resolve as resolve42 } from "path";
2754
2838
 
2755
2839
  // ../camel/dist/chunk-LAXU2AVK.js
@@ -3028,7 +3112,7 @@ function looksLikeDestination(value) {
3028
3112
  return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text);
3029
3113
  }
3030
3114
 
3031
- // ../harness/dist/chunk-T2RD6ZAW.js
3115
+ // ../harness/dist/chunk-6J2ZETST.js
3032
3116
  import { createHash as createHash3 } from "crypto";
3033
3117
  async function digestStagedWorkspace(root, limits) {
3034
3118
  const files = [];
@@ -3045,10 +3129,10 @@ async function digestStagedWorkspace(root, limits) {
3045
3129
  }
3046
3130
  };
3047
3131
  await walk(resolve4(root));
3048
- const hash = createHash("sha256");
3132
+ const hash = createHash2("sha256");
3049
3133
  let bytes = 0;
3050
3134
  for (const file of files.sort((left, right) => left.path.localeCompare(right.path))) {
3051
- const content = await readFile(file.target);
3135
+ const content = await readFile2(file.target);
3052
3136
  bytes += Buffer.byteLength(file.path) + content.byteLength;
3053
3137
  if (bytes > limits.maxBytes) throw new TypeError("workspace digest exceeds its byte bound");
3054
3138
  hash.update(`${Buffer.byteLength(file.path)}:${file.path}:${content.byteLength}:`);
@@ -3682,7 +3766,7 @@ async function inspectArtifacts(workspaceDir, recipe2) {
3682
3766
  }
3683
3767
  function hashFile(path) {
3684
3768
  return new Promise((accept, reject) => {
3685
- const hash = createHash2("sha256");
3769
+ const hash = createHash22("sha256");
3686
3770
  const stream = createReadStream(path);
3687
3771
  stream.on("data", (chunk) => {
3688
3772
  hash.update(chunk);
@@ -3740,7 +3824,7 @@ function digestJson(value) {
3740
3824
  return digestBytes(JSON.stringify(value));
3741
3825
  }
3742
3826
  function digestBytes(value) {
3743
- return `sha256:${createHash2("sha256").update(value).digest("hex")}`;
3827
+ return `sha256:${createHash22("sha256").update(value).digest("hex")}`;
3744
3828
  }
3745
3829
  function integer(value, minimum, maximum) {
3746
3830
  return Number.isSafeInteger(value) && value >= minimum && value <= maximum;
@@ -3861,7 +3945,7 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir3()) {
3861
3945
  const target = resolve32(sourceDir, file.path);
3862
3946
  if (!target.startsWith(`${resolve32(sourceDir)}${sep22}`)) throw new TypeError("Code source path escapes its root");
3863
3947
  await mkdir3(dirname4(target), { recursive: true });
3864
- await writeFile2(target, file.content, { flag: "wx", mode: 420 });
3948
+ await writeFile3(target, file.content, { flag: "wx", mode: 420 });
3865
3949
  }
3866
3950
  for (const reference of snapshot.references ?? []) {
3867
3951
  validateAlias(reference.alias);
@@ -3876,7 +3960,7 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir3()) {
3876
3960
  const target = resolve32(sourceDir, path);
3877
3961
  if (!target.startsWith(`${resolve32(sourceDir)}${sep22}`)) throw new TypeError("Code reference path escapes its root");
3878
3962
  await mkdir3(dirname4(target), { recursive: true });
3879
- await writeFile2(target, file.content, { flag: "wx", mode: 292 });
3963
+ await writeFile3(target, file.content, { flag: "wx", mode: 292 });
3880
3964
  }
3881
3965
  }
3882
3966
  return { sourceDir, cleanup: () => rm3(root, { recursive: true, force: true }) };
@@ -3903,7 +3987,7 @@ async function attachCodeRuntimeReferences(workspace, references) {
3903
3987
  const target = resolve32(root, path);
3904
3988
  if (!target.startsWith(`${resolve32(root)}${sep22}`)) throw new TypeError("Code reference path escapes its root");
3905
3989
  await mkdir3(dirname4(target), { recursive: true });
3906
- await writeFile2(target, file.content, { flag: "wx", mode: 292 });
3990
+ await writeFile3(target, file.content, { flag: "wx", mode: 292 });
3907
3991
  }
3908
3992
  }
3909
3993
  }
@@ -4093,7 +4177,7 @@ async function read(context, request, options, policy) {
4093
4177
  if (!info.isFile() || info.size > Math.max(options.maxReadBytes ?? 128 * 1024, 2 * 1024 * 1024)) {
4094
4178
  throw new TypeError("file is not a bounded regular source file");
4095
4179
  }
4096
- const source = await readFile2(target);
4180
+ const source = await readFile22(target);
4097
4181
  if (source.includes(0)) throw new TypeError("binary files are not readable through this tool");
4098
4182
  const lines = source.toString("utf8").split("\n");
4099
4183
  const content = lines.slice(startLine - 1, endLine).join("\n");
@@ -4219,6 +4303,7 @@ function codeCommandMetadata(payload, resume) {
4219
4303
  const role = payload.role;
4220
4304
  const title = payload.title;
4221
4305
  const prompt = payload.prompt;
4306
+ const maxTokensPerInteraction = payload.maxTokensPerInteraction ?? 32e3;
4222
4307
  if (role !== "coding" && role !== "review" || typeof title !== "string" || typeof prompt !== "string") {
4223
4308
  throw new TypeError(`invalid Code ${resume ? "resume" : "start"} metadata`);
4224
4309
  }
@@ -4230,10 +4315,14 @@ function codeCommandMetadata(payload, resume) {
4230
4315
  if (typeof repository !== "string" || !repository.includes("/") || typeof baseCommitSha !== "string" || !/^[0-9a-f]{40}$/.test(baseCommitSha) || typeof sourceTreeDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(sourceTreeDigest)) {
4231
4316
  throw new TypeError(`invalid Code ${resume ? "resume" : "start"} trusted base`);
4232
4317
  }
4318
+ if (!Number.isSafeInteger(maxTokensPerInteraction) || Number(maxTokensPerInteraction) < 4e3 || Number(maxTokensPerInteraction) > 2e5) {
4319
+ throw new TypeError(`invalid Code ${resume ? "resume" : "start"} interaction token limit`);
4320
+ }
4233
4321
  return {
4234
4322
  role,
4235
4323
  title,
4236
4324
  prompt,
4325
+ maxTokensPerInteraction: Number(maxTokensPerInteraction),
4237
4326
  planningInputDigest: typeof planning === "string" && /^sha256:[0-9a-f]{64}$/.test(planning) ? planning : null,
4238
4327
  attestationDigest: typeof attestation === "string" ? attestation : "resume",
4239
4328
  repository,
@@ -4309,6 +4398,57 @@ function createCodeRuntimeToolBroker(input, lease, role) {
4309
4398
  });
4310
4399
  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" }) };
4311
4400
  }
4401
+ async function handleCodeRuntimeInference(input) {
4402
+ const { command, metadata: metadata2, request, state } = input;
4403
+ if (state.tokens >= metadata2.maxTokensPerInteraction) {
4404
+ if (!state.noticeEmitted) {
4405
+ state.noticeEmitted = true;
4406
+ await input.event({
4407
+ type: "message",
4408
+ actor: "system",
4409
+ body: `Pi paused at the ${metadata2.maxTokensPerInteraction.toLocaleString("en-US")}-token per-interaction limit. Send a new instruction to continue.`
4410
+ }).catch(() => void 0);
4411
+ }
4412
+ return {
4413
+ protocolVersion: HARNESS_PROTOCOL_VERSION,
4414
+ type: "inference.response",
4415
+ requestId: request.requestId,
4416
+ response: {
4417
+ id: `budget:${command.commandId}`,
4418
+ provider: "openai",
4419
+ model: "interaction-budget",
4420
+ role: "assistant",
4421
+ content: [{ type: "text", text: "Pause now. The owner-set token limit for this interaction has been reached." }],
4422
+ stopReason: "end_turn",
4423
+ usage: { inputTokens: 0, outputTokens: 0 }
4424
+ }
4425
+ };
4426
+ }
4427
+ const startedAt = Date.now();
4428
+ const response2 = await input.control.infer(command.sessionId, {
4429
+ requestId: request.requestId,
4430
+ interactionId: command.commandId,
4431
+ call: request.call
4432
+ });
4433
+ state.tokens += response2.receipt.inputTokens + response2.receipt.outputTokens;
4434
+ await input.event({
4435
+ type: "usage",
4436
+ provider: response2.receipt.provider,
4437
+ model: response2.receipt.model,
4438
+ inputTokens: response2.receipt.inputTokens,
4439
+ outputTokens: response2.receipt.outputTokens,
4440
+ durationMs: Date.now() - startedAt,
4441
+ interactionId: command.commandId,
4442
+ interactionTokens: state.tokens,
4443
+ interactionMaxTokens: metadata2.maxTokensPerInteraction
4444
+ }).catch(() => void 0);
4445
+ return {
4446
+ protocolVersion: HARNESS_PROTOCOL_VERSION,
4447
+ type: "inference.response",
4448
+ requestId: request.requestId,
4449
+ response: response2.response
4450
+ };
4451
+ }
4312
4452
  async function appendCodeRuntimeEvent(control, command, event, refs) {
4313
4453
  const eventId = `${command.commandId.slice(0, 45)}:${refs.length + 1}`;
4314
4454
  refs.push(eventId);
@@ -4338,6 +4478,7 @@ function runtimeResultError(value) {
4338
4478
  var CodePiRuntimeEngine = class {
4339
4479
  constructor(options) {
4340
4480
  this.options = options;
4481
+ if (options.imageAuthorization === "cli_embedded" && !/^odla-ai\/pi-agent:embedded-sha256-[0-9a-f]{64}$/.test(options.image)) throw new TypeError("CLI-embedded Pi image must use its content-addressed local tag");
4341
4482
  this.#run = options.runAttempt ?? runContainerAttempt;
4342
4483
  this.#buildPolicyDigest = digestRuntimeValue(JSON.stringify(options.recipes));
4343
4484
  this.#checkpoints = new CodeRuntimeCheckpointManager({
@@ -4420,6 +4561,7 @@ var CodePiRuntimeEngine = class {
4420
4561
  acknowledged: false,
4421
4562
  role: metadata2.role,
4422
4563
  title: metadata2.title,
4564
+ maxTokensPerInteraction: metadata2.maxTokensPerInteraction,
4423
4565
  baseCommitSha: metadata2.baseCommitSha,
4424
4566
  repository: metadata2.repository,
4425
4567
  sourceTreeDigest: metadata2.sourceTreeDigest,
@@ -4456,6 +4598,11 @@ var CodePiRuntimeEngine = class {
4456
4598
  if (!active || typeof prompt !== "string" || !prompt.trim() || prompt.length > 2e4) {
4457
4599
  throw new TypeError("prompt requires an active Code session and bounded text");
4458
4600
  }
4601
+ const requestedLimit = command.payload.maxTokensPerInteraction ?? active.maxTokensPerInteraction;
4602
+ if (!Number.isSafeInteger(requestedLimit) || Number(requestedLimit) < 4e3 || Number(requestedLimit) > 2e5) {
4603
+ throw new TypeError("prompt requires a valid interaction token limit");
4604
+ }
4605
+ active.maxTokensPerInteraction = Number(requestedLimit);
4459
4606
  await active.done;
4460
4607
  active.abort = new AbortController();
4461
4608
  active.acknowledged = false;
@@ -4464,6 +4611,7 @@ var CodePiRuntimeEngine = class {
4464
4611
  role: active.role,
4465
4612
  title: active.title,
4466
4613
  prompt,
4614
+ maxTokensPerInteraction: active.maxTokensPerInteraction,
4467
4615
  planningInputDigest: active.planningInputDigest,
4468
4616
  attestationDigest: "follow-up",
4469
4617
  repository: active.repository,
@@ -4492,9 +4640,11 @@ var CodePiRuntimeEngine = class {
4492
4640
  }, lease, metadata2.role);
4493
4641
  const startedAt = Date.now();
4494
4642
  let completionSeen = false;
4643
+ const interaction = { tokens: 0, noticeEmitted: false };
4495
4644
  const result = await this.#run({
4496
4645
  engine: this.options.engine,
4497
4646
  image: this.options.image,
4647
+ allowUnpinnedImage: this.options.imageAuthorization === "cli_embedded",
4498
4648
  workspaceDir: active.workspace.workspaceDir,
4499
4649
  workspaceAccess: "none",
4500
4650
  task: lease.task,
@@ -4507,25 +4657,18 @@ var CodePiRuntimeEngine = class {
4507
4657
  }, active.conversationRefs),
4508
4658
  onMessage: async (output) => {
4509
4659
  if (output.type === "inference.request") {
4510
- const inferenceStarted = Date.now();
4511
- const response2 = await this.options.control.infer(command.sessionId, {
4512
- requestId: output.requestId,
4513
- call: output.call
4660
+ return handleCodeRuntimeInference({
4661
+ command,
4662
+ metadata: metadata2,
4663
+ request: output,
4664
+ state: interaction,
4665
+ control: this.options.control,
4666
+ event: (event) => this.#event(
4667
+ command,
4668
+ event,
4669
+ active.conversationRefs
4670
+ )
4514
4671
  });
4515
- await this.#event(command, {
4516
- type: "usage",
4517
- provider: response2.receipt.provider,
4518
- model: response2.receipt.model,
4519
- inputTokens: response2.receipt.inputTokens,
4520
- outputTokens: response2.receipt.outputTokens,
4521
- durationMs: Date.now() - inferenceStarted
4522
- }, active.conversationRefs).catch(() => void 0);
4523
- return {
4524
- protocolVersion: HARNESS_PROTOCOL_VERSION,
4525
- type: "inference.response",
4526
- requestId: output.requestId,
4527
- response: response2.response
4528
- };
4529
4672
  }
4530
4673
  if (output.type === "tool.request") {
4531
4674
  const toolStarted = Date.now();
@@ -4795,20 +4938,6 @@ function digestText(value) {
4795
4938
  return `sha256:${createHash4("sha256").update(value).digest("hex")}`;
4796
4939
  }
4797
4940
 
4798
- // src/code-images.ts
4799
- import { execFile as execFile4 } from "child_process";
4800
- async function prepareCodeImages(engine, images) {
4801
- for (const image of images) {
4802
- await new Promise((accept, reject) => {
4803
- const args = engine === "container" ? ["image", "pull", image] : ["pull", image];
4804
- execFile4(engine, args, { encoding: "utf8", maxBuffer: 2 * 1024 * 1024, timeout: 10 * 6e4 }, (error) => {
4805
- if (error) reject(new Error(`could not prepare pinned Code image ${image}`));
4806
- else accept();
4807
- });
4808
- });
4809
- }
4810
- }
4811
-
4812
4941
  // src/code-connect.ts
4813
4942
  async function codeConnect(options) {
4814
4943
  const cwd = options.cwd ?? process.cwd();
@@ -4841,10 +4970,11 @@ async function codeConnect(options) {
4841
4970
  const out = options.stdout ?? console;
4842
4971
  const doFetch = options.fetch ?? fetch;
4843
4972
  const engine = await (options.selectEngine ?? selectContainerEngine)(options.engine ?? "auto");
4844
- await (options.prepareImages ?? prepareCodeImages)(engine, [
4845
- CODE_PI_IMAGE,
4846
- ...new Set(CODE_BUILD_RECIPES.map((recipe2) => recipe2.image))
4847
- ]);
4973
+ const [piImage] = await (options.prepareImages ?? prepareCodeImages)(
4974
+ engine,
4975
+ [CODE_PI_IMAGE, ...new Set(CODE_BUILD_RECIPES.map((recipe2) => recipe2.image))]
4976
+ );
4977
+ if (!piImage || !/^odla-ai\/pi-agent:embedded-sha256-[0-9a-f]{64}$/.test(piImage)) throw new Error("Code image preflight did not produce the content-addressed embedded Pi runtime");
4848
4978
  const hostPlatform = process.platform === "darwin" ? "macos" : "linux";
4849
4979
  const hostName = (options.name ?? hostname()).trim();
4850
4980
  if (!hostName || hostName.length > 120) throw new Error("--name must contain 1 to 120 characters");
@@ -4888,7 +5018,8 @@ async function codeConnect(options) {
4888
5018
  source: descriptor2,
4889
5019
  images: {
4890
5020
  ready: true,
4891
- pi: CODE_PI_IMAGE,
5021
+ pi: piImage,
5022
+ piSource: "cli_embedded",
4892
5023
  recipes: CODE_BUILD_RECIPES.map((recipe2) => ({ id: recipe2.id, image: recipe2.image }))
4893
5024
  }
4894
5025
  };
@@ -4903,6 +5034,7 @@ async function codeConnect(options) {
4903
5034
  engine,
4904
5035
  capabilities,
4905
5036
  localSource,
5037
+ piImage,
4906
5038
  heartbeatMs,
4907
5039
  once: options.once === true,
4908
5040
  signal: options.signal,
@@ -4932,7 +5064,8 @@ async function runCodeRuntime(input) {
4932
5064
  const commandEngine = new CodePiRuntimeEngine({
4933
5065
  control,
4934
5066
  engine: input.engine,
4935
- image: CODE_PI_IMAGE,
5067
+ image: input.piImage ?? input.capabilities.images.pi,
5068
+ imageAuthorization: "cli_embedded",
4936
5069
  recipes: CODE_BUILD_RECIPES,
4937
5070
  recipeAuthorization: "registered_recipe",
4938
5071
  localSource: input.localSource,
@@ -6310,7 +6443,7 @@ function securityExecutionDigest(value) {
6310
6443
  import { existsSync as existsSync8, lstatSync, mkdirSync as mkdirSync3, readFileSync as readFileSync6, readdirSync, writeFileSync as writeFileSync3 } from "fs";
6311
6444
  import { homedir } from "os";
6312
6445
  import { dirname as dirname6, isAbsolute as isAbsolute5, join as join7, relative as relative5, resolve as resolve9, sep as sep5 } from "path";
6313
- import { fileURLToPath } from "url";
6446
+ import { fileURLToPath as fileURLToPath2 } from "url";
6314
6447
 
6315
6448
  // src/skill-adapters.ts
6316
6449
  var PROJECT_INSTRUCTIONS = `<!-- odla-ai agent setup:start -->
@@ -6366,7 +6499,7 @@ runbook. Do not substitute a remote documentation page for the installed copy.
6366
6499
  var AGENT_HARNESSES = ["claude", "codex", "cursor", "copilot", "gemini", "agents"];
6367
6500
  function installSkill(options = {}) {
6368
6501
  const out = options.stdout ?? console;
6369
- const sourceDir = options.sourceDir ?? fileURLToPath(new URL("../skills", import.meta.url));
6502
+ const sourceDir = options.sourceDir ?? fileURLToPath2(new URL("../skills", import.meta.url));
6370
6503
  const files = listFiles(sourceDir);
6371
6504
  if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
6372
6505
  const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
@@ -7753,6 +7886,7 @@ export {
7753
7886
  inferGitHubRepository,
7754
7887
  CODE_PI_IMAGE,
7755
7888
  CODE_BUILD_RECIPES,
7889
+ prepareCodeImages,
7756
7890
  codeConnect,
7757
7891
  runCodeRuntime,
7758
7892
  doctor,
@@ -7776,4 +7910,4 @@ export {
7776
7910
  exitCodeFor,
7777
7911
  runCli
7778
7912
  };
7779
- //# sourceMappingURL=chunk-PQX6SKYJ.js.map
7913
+ //# sourceMappingURL=chunk-GAFC4EPJ.js.map