@odla-ai/harness 0.4.0 → 0.5.1

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  HARNESS_PROTOCOL_VERSION
3
- } from "./chunk-3QP4VDQS.js";
3
+ } from "./chunk-LNQNFGQC.js";
4
4
 
5
5
  // src/protocol.ts
6
6
  var CONTROL = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/;
@@ -90,4 +90,4 @@ export {
90
90
  encodeAgentInput,
91
91
  makeHarnessEvent
92
92
  };
93
- //# sourceMappingURL=chunk-C5VQI2IF.js.map
93
+ //# sourceMappingURL=chunk-FVOMJKWK.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  encodeAgentInput,
3
3
  parseAgentOutput
4
- } from "./chunk-C5VQI2IF.js";
4
+ } from "./chunk-FVOMJKWK.js";
5
5
  import {
6
6
  HARNESS_PROTOCOL_VERSION
7
- } from "./chunk-3QP4VDQS.js";
7
+ } from "./chunk-LNQNFGQC.js";
8
8
 
9
9
  // src/container.ts
10
10
  import { execFile, spawn } from "child_process";
@@ -548,4 +548,4 @@ export {
548
548
  stageWorkspacePair,
549
549
  safeWorkspaceLabel
550
550
  };
551
- //# sourceMappingURL=chunk-GKDKIU4P.js.map
551
+ //# sourceMappingURL=chunk-K76I2TCQ.js.map
@@ -4,10 +4,10 @@ import {
4
4
  stageWorkspace,
5
5
  stageWorkspacePair,
6
6
  verifyContainerEngineBoundary
7
- } from "./chunk-GKDKIU4P.js";
7
+ } from "./chunk-K76I2TCQ.js";
8
8
  import {
9
9
  HARNESS_PROTOCOL_VERSION
10
- } from "./chunk-3QP4VDQS.js";
10
+ } from "./chunk-LNQNFGQC.js";
11
11
 
12
12
  // src/workspace-digest.ts
13
13
  import { createHash } from "crypto";
@@ -277,6 +277,7 @@ function parseSnapshot(value) {
277
277
  return { host, bindings, commands };
278
278
  }
279
279
  async function parseSource(value) {
280
+ const repositoryLimits = { maximumFiles: 1e5, maximumBytes: 80 * 1024 * 1024 };
280
281
  const snapshot = record(record(value)?.snapshot);
281
282
  if (!snapshot || typeof snapshot.repository !== "string" || typeof snapshot.commitSha !== "string" || typeof snapshot.treeDigest !== "string" || !Array.isArray(snapshot.files)) throw invalid("source");
282
283
  const files = snapshot.files.map((value2) => {
@@ -298,12 +299,12 @@ async function parseSource(value) {
298
299
  return { path: file.path, content: file.content };
299
300
  });
300
301
  const source2 = { repository: reference.repository, commitSha: reference.commitSha, files: referenceFiles };
301
- const referenceDigest = await digestCodeRepositorySnapshot(source2, { maximumFiles: 1e4, maximumBytes: 16 * 1024 * 1024 });
302
+ const referenceDigest = await digestCodeRepositorySnapshot(source2, repositoryLimits);
302
303
  if (referenceDigest !== reference.treeDigest) throw invalid("reference source digest");
303
304
  references.push({ alias: reference.alias, ...source2, treeDigest: referenceDigest });
304
305
  }
305
306
  const source = { repository: snapshot.repository, commitSha: snapshot.commitSha, files };
306
- const digest = await digestCodeRepositorySnapshot(source, { maximumFiles: 1e4, maximumBytes: 16 * 1024 * 1024 });
307
+ const digest = await digestCodeRepositorySnapshot(source, repositoryLimits);
307
308
  if (digest !== snapshot.treeDigest) throw invalid("source digest");
308
309
  return { ...source, treeDigest: digest, ...references.length ? { references } : {} };
309
310
  }
@@ -999,8 +1000,11 @@ import { tmpdir } from "os";
999
1000
  import { dirname, join as join2, resolve as resolve3, sep as sep2 } from "path";
1000
1001
  var RESERVED2 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
1001
1002
  var SECRET2 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
1003
+ var SOURCE_MAX_FILES = 1e5;
1004
+ var SOURCE_MAX_BYTES = 80 * 1024 * 1024;
1005
+ var SOURCE_SET_MAX_BYTES = 480 * 1024 * 1024;
1002
1006
  async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir()) {
1003
- if (!snapshot.files.length || snapshot.files.length > 1e4) throw new TypeError("Code source file count is invalid");
1007
+ if (!snapshot.files.length || snapshot.files.length > SOURCE_MAX_FILES) throw new TypeError("Code source file count is invalid");
1004
1008
  const root = await mkdtemp(join2(tempRoot, "odla-code-source-"));
1005
1009
  const sourceDir = join2(root, "source");
1006
1010
  await mkdir(sourceDir);
@@ -1012,7 +1016,7 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir()) {
1012
1016
  if (seen.has(file.path)) throw new TypeError("Code source repeats a path");
1013
1017
  seen.add(file.path);
1014
1018
  bytes += Buffer.byteLength(file.path) + Buffer.byteLength(file.content);
1015
- if (bytes > 16 * 1024 * 1024) throw new TypeError("Code source exceeds its byte bound");
1019
+ if (bytes > SOURCE_MAX_BYTES) throw new TypeError("Code source exceeds its byte bound");
1016
1020
  const target = resolve3(sourceDir, file.path);
1017
1021
  if (!target.startsWith(`${resolve3(sourceDir)}${sep2}`)) throw new TypeError("Code source path escapes its root");
1018
1022
  await mkdir(dirname(target), { recursive: true });
@@ -1020,14 +1024,14 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir()) {
1020
1024
  }
1021
1025
  for (const reference of snapshot.references ?? []) {
1022
1026
  validateAlias(reference.alias);
1023
- if (!reference.files.length || reference.files.length > 1e4) throw new TypeError("Code reference file count is invalid");
1027
+ if (!reference.files.length || reference.files.length > SOURCE_MAX_FILES) throw new TypeError("Code reference file count is invalid");
1024
1028
  for (const file of reference.files) {
1025
1029
  validatePath(file.path);
1026
1030
  const path = `.odla-references/${reference.alias}/${file.path}`;
1027
1031
  if (seen.has(path)) throw new TypeError("Code reference repeats a path");
1028
1032
  seen.add(path);
1029
1033
  bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
1030
- if (bytes > 80 * 1024 * 1024) throw new TypeError("Code source set exceeds its byte bound");
1034
+ if (bytes > SOURCE_SET_MAX_BYTES) throw new TypeError("Code source set exceeds its byte bound");
1031
1035
  const target = resolve3(sourceDir, path);
1032
1036
  if (!target.startsWith(`${resolve3(sourceDir)}${sep2}`)) throw new TypeError("Code reference path escapes its root");
1033
1037
  await mkdir(dirname(target), { recursive: true });
@@ -1053,7 +1057,7 @@ async function attachCodeRuntimeReferences(workspace, references) {
1053
1057
  validatePath(file.path);
1054
1058
  const path = `.odla-references/${reference.alias}/${file.path}`;
1055
1059
  bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
1056
- if (bytes > 64 * 1024 * 1024) throw new TypeError("Code reference set exceeds its byte bound");
1060
+ if (bytes > SOURCE_SET_MAX_BYTES - SOURCE_MAX_BYTES) throw new TypeError("Code reference set exceeds its byte bound");
1057
1061
  for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
1058
1062
  const target = resolve3(root, path);
1059
1063
  if (!target.startsWith(`${resolve3(root)}${sep2}`)) throw new TypeError("Code reference path escapes its root");
@@ -1103,7 +1107,10 @@ async function materializeCommandWorkspace(input) {
1103
1107
  trustedBaseDir: materialized.sourceDir,
1104
1108
  trustedBaseCommitSha: source.commitSha,
1105
1109
  checkpoint: codeCheckpointPayload(command.payload)
1106
- })).workspace : await stageWorkspace(materialized.sourceDir);
1110
+ })).workspace : await stageWorkspace(materialized.sourceDir, {
1111
+ maxFiles: SOURCE_MAX_FILES,
1112
+ maxBytes: SOURCE_SET_MAX_BYTES
1113
+ });
1107
1114
  return { workspace, sourceDigest: source.treeDigest, requestedLocal: null };
1108
1115
  } finally {
1109
1116
  await materialized.cleanup();
@@ -1372,6 +1379,9 @@ async function handleCodeRuntimeInference(input) {
1372
1379
  call: request.call
1373
1380
  });
1374
1381
  state.tokens += response2.receipt.inputTokens + response2.receipt.outputTokens;
1382
+ const { costUsd } = response2.receipt;
1383
+ if (costUsd === void 0) state.costKnown = false;
1384
+ else state.costUsd += costUsd;
1375
1385
  await input.event({
1376
1386
  type: "usage",
1377
1387
  provider: response2.receipt.provider,
@@ -1381,7 +1391,9 @@ async function handleCodeRuntimeInference(input) {
1381
1391
  durationMs: Date.now() - startedAt,
1382
1392
  interactionId: command.commandId,
1383
1393
  interactionTokens: state.tokens,
1384
- interactionMaxTokens: metadata.maxTokensPerInteraction
1394
+ interactionMaxTokens: metadata.maxTokensPerInteraction,
1395
+ ...costUsd === void 0 ? {} : { costUsd },
1396
+ ...state.costKnown ? { interactionCostUsd: state.costUsd } : {}
1385
1397
  }).catch(() => void 0);
1386
1398
  return {
1387
1399
  protocolVersion: HARNESS_PROTOCOL_VERSION,
@@ -2297,10 +2309,16 @@ async function startGoalPursuit(input) {
2297
2309
  attempt: async ({ prompt }) => {
2298
2310
  const result = await input.attempt(prompt);
2299
2311
  return {
2300
- // The runtime charges tokens through the control plane's own
2301
- // per-interaction reservation, so the goal budget bounds ATTEMPTS here
2302
- // and the token ceiling is enforced where the credential lives.
2303
- tokens: 0,
2312
+ // What the attempt actually spent, so the runner's token_budget and
2313
+ // cost_budget checks can be reached. This used to be a hardcoded 0 with
2314
+ // no cost at all, which made maxTokens and maxUsd unreachable while
2315
+ // callers reasonably read them as hard ceilings.
2316
+ //
2317
+ // costUsd is omitted rather than zeroed when any call in the attempt
2318
+ // was unpriced: the runner only enforces a cost budget while the cost
2319
+ // is known, and a zero would make it enforce against a lie.
2320
+ tokens: result.tokens ?? 0,
2321
+ ...result.costUsd === void 0 ? {} : { costUsd: result.costUsd },
2304
2322
  ...result.status === "failed" ? { error: result.error ?? "attempt failed" } : {}
2305
2323
  };
2306
2324
  },
@@ -2519,7 +2537,7 @@ var CodePiRuntimeEngine = class {
2519
2537
  recipeAuthorization: this.options.recipeAuthorization
2520
2538
  }, lease, metadata.role));
2521
2539
  const startedAt = Date.now();
2522
- const interaction = { tokens: 0, noticeEmitted: false };
2540
+ const interaction = { tokens: 0, noticeEmitted: false, costUsd: 0, costKnown: true };
2523
2541
  const inference = createCodeRuntimeInference({
2524
2542
  command,
2525
2543
  metadata,
@@ -2557,7 +2575,11 @@ var CodePiRuntimeEngine = class {
2557
2575
  await this.#diagnostic(command, active, detail);
2558
2576
  await this.#failure(command, active, detail);
2559
2577
  }
2560
- return result;
2578
+ return {
2579
+ ...result,
2580
+ tokens: interaction.tokens,
2581
+ ...interaction.costKnown ? { costUsd: interaction.costUsd } : {}
2582
+ };
2561
2583
  }
2562
2584
  /** Report every brokered effect as it starts and finishes. */
2563
2585
  #observed(command, active, broker) {
@@ -2651,4 +2673,4 @@ export {
2651
2673
  runGoal,
2652
2674
  CodePiRuntimeEngine
2653
2675
  };
2654
- //# sourceMappingURL=chunk-ANNX7VGK.js.map
2676
+ //# sourceMappingURL=chunk-L2T3LPEF.js.map