@odla-ai/harness 0.5.0 → 0.5.2

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.
@@ -6,7 +6,7 @@ import {
6
6
  assertCodeBuildRecipe,
7
7
  createCodeRuntimeControlClient,
8
8
  runCodeRuntimeHeartbeatLoop
9
- } from "./chunk-UVGZHNLW.js";
9
+ } from "./chunk-5HX5LWTG.js";
10
10
  import {
11
11
  assertPinnedImage,
12
12
  selectContainerEngine
package/dist/node.cjs CHANGED
@@ -1055,6 +1055,7 @@ function parseSnapshot(value) {
1055
1055
  return { host, bindings, commands };
1056
1056
  }
1057
1057
  async function parseSource(value) {
1058
+ const repositoryLimits = { maximumFiles: 1e5, maximumBytes: 80 * 1024 * 1024 };
1058
1059
  const snapshot = record2(record2(value)?.snapshot);
1059
1060
  if (!snapshot || typeof snapshot.repository !== "string" || typeof snapshot.commitSha !== "string" || typeof snapshot.treeDigest !== "string" || !Array.isArray(snapshot.files)) throw invalid("source");
1060
1061
  const files = snapshot.files.map((value2) => {
@@ -1076,12 +1077,12 @@ async function parseSource(value) {
1076
1077
  return { path: file.path, content: file.content };
1077
1078
  });
1078
1079
  const source2 = { repository: reference.repository, commitSha: reference.commitSha, files: referenceFiles };
1079
- const referenceDigest = await (0, import_code.digestCodeRepositorySnapshot)(source2, { maximumFiles: 1e4, maximumBytes: 16 * 1024 * 1024 });
1080
+ const referenceDigest = await (0, import_code.digestCodeRepositorySnapshot)(source2, repositoryLimits);
1080
1081
  if (referenceDigest !== reference.treeDigest) throw invalid("reference source digest");
1081
1082
  references.push({ alias: reference.alias, ...source2, treeDigest: referenceDigest });
1082
1083
  }
1083
1084
  const source = { repository: snapshot.repository, commitSha: snapshot.commitSha, files };
1084
- const digest = await (0, import_code.digestCodeRepositorySnapshot)(source, { maximumFiles: 1e4, maximumBytes: 16 * 1024 * 1024 });
1085
+ const digest = await (0, import_code.digestCodeRepositorySnapshot)(source, repositoryLimits);
1085
1086
  if (digest !== snapshot.treeDigest) throw invalid("source digest");
1086
1087
  return { ...source, treeDigest: digest, ...references.length ? { references } : {} };
1087
1088
  }
@@ -1848,8 +1849,11 @@ var import_node_os3 = require("os");
1848
1849
  var import_node_path8 = require("path");
1849
1850
  var RESERVED2 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
1850
1851
  var SECRET2 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
1852
+ var SOURCE_MAX_FILES = 1e5;
1853
+ var SOURCE_MAX_BYTES = 80 * 1024 * 1024;
1854
+ var SOURCE_SET_MAX_BYTES = 480 * 1024 * 1024;
1851
1855
  async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node_os3.tmpdir)()) {
1852
- if (!snapshot.files.length || snapshot.files.length > 1e4) throw new TypeError("Code source file count is invalid");
1856
+ if (!snapshot.files.length || snapshot.files.length > SOURCE_MAX_FILES) throw new TypeError("Code source file count is invalid");
1853
1857
  const root = await (0, import_promises8.mkdtemp)((0, import_node_path8.join)(tempRoot, "odla-code-source-"));
1854
1858
  const sourceDir = (0, import_node_path8.join)(root, "source");
1855
1859
  await (0, import_promises8.mkdir)(sourceDir);
@@ -1861,7 +1865,7 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node
1861
1865
  if (seen.has(file.path)) throw new TypeError("Code source repeats a path");
1862
1866
  seen.add(file.path);
1863
1867
  bytes += Buffer.byteLength(file.path) + Buffer.byteLength(file.content);
1864
- if (bytes > 16 * 1024 * 1024) throw new TypeError("Code source exceeds its byte bound");
1868
+ if (bytes > SOURCE_MAX_BYTES) throw new TypeError("Code source exceeds its byte bound");
1865
1869
  const target = (0, import_node_path8.resolve)(sourceDir, file.path);
1866
1870
  if (!target.startsWith(`${(0, import_node_path8.resolve)(sourceDir)}${import_node_path8.sep}`)) throw new TypeError("Code source path escapes its root");
1867
1871
  await (0, import_promises8.mkdir)((0, import_node_path8.dirname)(target), { recursive: true });
@@ -1869,14 +1873,14 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node
1869
1873
  }
1870
1874
  for (const reference of snapshot.references ?? []) {
1871
1875
  validateAlias(reference.alias);
1872
- if (!reference.files.length || reference.files.length > 1e4) throw new TypeError("Code reference file count is invalid");
1876
+ if (!reference.files.length || reference.files.length > SOURCE_MAX_FILES) throw new TypeError("Code reference file count is invalid");
1873
1877
  for (const file of reference.files) {
1874
1878
  validatePath(file.path);
1875
1879
  const path = `.odla-references/${reference.alias}/${file.path}`;
1876
1880
  if (seen.has(path)) throw new TypeError("Code reference repeats a path");
1877
1881
  seen.add(path);
1878
1882
  bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
1879
- if (bytes > 80 * 1024 * 1024) throw new TypeError("Code source set exceeds its byte bound");
1883
+ if (bytes > SOURCE_SET_MAX_BYTES) throw new TypeError("Code source set exceeds its byte bound");
1880
1884
  const target = (0, import_node_path8.resolve)(sourceDir, path);
1881
1885
  if (!target.startsWith(`${(0, import_node_path8.resolve)(sourceDir)}${import_node_path8.sep}`)) throw new TypeError("Code reference path escapes its root");
1882
1886
  await (0, import_promises8.mkdir)((0, import_node_path8.dirname)(target), { recursive: true });
@@ -1902,7 +1906,7 @@ async function attachCodeRuntimeReferences(workspace, references) {
1902
1906
  validatePath(file.path);
1903
1907
  const path = `.odla-references/${reference.alias}/${file.path}`;
1904
1908
  bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
1905
- if (bytes > 64 * 1024 * 1024) throw new TypeError("Code reference set exceeds its byte bound");
1909
+ if (bytes > SOURCE_SET_MAX_BYTES - SOURCE_MAX_BYTES) throw new TypeError("Code reference set exceeds its byte bound");
1906
1910
  for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
1907
1911
  const target = (0, import_node_path8.resolve)(root, path);
1908
1912
  if (!target.startsWith(`${(0, import_node_path8.resolve)(root)}${import_node_path8.sep}`)) throw new TypeError("Code reference path escapes its root");
@@ -1952,13 +1956,19 @@ async function materializeCommandWorkspace(input) {
1952
1956
  trustedBaseDir: materialized.sourceDir,
1953
1957
  trustedBaseCommitSha: source.commitSha,
1954
1958
  checkpoint: codeCheckpointPayload(command.payload)
1955
- })).workspace : await stageWorkspace(materialized.sourceDir);
1959
+ })).workspace : await stageWorkspace(materialized.sourceDir, {
1960
+ maxFiles: SOURCE_MAX_FILES,
1961
+ maxBytes: SOURCE_SET_MAX_BYTES
1962
+ });
1956
1963
  return { workspace, sourceDigest: source.treeDigest, requestedLocal: null };
1957
1964
  } finally {
1958
1965
  await materialized.cleanup();
1959
1966
  }
1960
1967
  }
1961
1968
 
1969
+ // src/code-runtime-attempt.ts
1970
+ var import_ai2 = require("@odla-ai/ai");
1971
+
1962
1972
  // src/code-agent.ts
1963
1973
  var import_ai = require("@odla-ai/ai");
1964
1974
 
@@ -2159,6 +2169,7 @@ async function runCodeAgent(options) {
2159
2169
  // src/code-runtime-attempt.ts
2160
2170
  async function runCodeAgentAttempt(options) {
2161
2171
  try {
2172
+ const surface = options.surface ?? "v2";
2162
2173
  const { run } = await runCodeAgent({
2163
2174
  inference: options.inference,
2164
2175
  broker: options.broker,
@@ -2168,17 +2179,31 @@ async function runCodeAgentAttempt(options) {
2168
2179
  // The brokered route resolves the real model from platform policy; this
2169
2180
  // id only labels the request the control plane is about to rewrite.
2170
2181
  model: "brokered",
2171
- surface: options.surface ?? "v2",
2182
+ surface,
2172
2183
  ...options.maxSteps === void 0 ? {} : { maxSteps: options.maxSteps },
2173
2184
  ...options.budget ? { budget: options.budget } : {},
2174
2185
  ...options.signal ? { signal: options.signal } : {},
2175
2186
  ...options.onToolCall ? { onToolCall: options.onToolCall } : {}
2176
2187
  });
2188
+ let finalText = run.finalText.trim();
2189
+ if (!finalText && run.stoppedReason !== "refusal") {
2190
+ const closing = await options.inference.chat({
2191
+ model: "brokered",
2192
+ system: `${SYSTEM_PROMPT_FOR[surface]}
2193
+
2194
+ Finish with a concise, non-empty answer to the owner. Do not call tools or promise future work.`,
2195
+ messages: [...run.messages, { role: "user", content: "Give the owner the closing answer now, grounded in the repository evidence and tool results above." }],
2196
+ maxTokens: 16384,
2197
+ ...options.signal ? { signal: options.signal } : {}
2198
+ });
2199
+ finalText = (0, import_ai2.extractText)(closing.content).trim();
2200
+ }
2201
+ const missingClosing = !finalText && run.stoppedReason !== "refusal";
2177
2202
  return {
2178
- status: run.stoppedReason === "refusal" ? "failed" : "completed",
2179
- finalText: run.finalText,
2203
+ status: run.stoppedReason === "refusal" || missingClosing ? "failed" : "completed",
2204
+ finalText,
2180
2205
  stoppedReason: run.stoppedReason,
2181
- ...run.stoppedReason === "refusal" ? { error: run.finalText || "the agent refused the task" } : {}
2206
+ ...run.stoppedReason === "refusal" ? { error: finalText || "the agent refused the task" } : missingClosing ? { error: "the Code agent did not produce a closing answer" } : {}
2182
2207
  };
2183
2208
  } catch (cause) {
2184
2209
  const error = (cause instanceof Error ? cause.message : String(cause)).slice(0, 2e3);
@@ -2188,31 +2213,7 @@ async function runCodeAgentAttempt(options) {
2188
2213
 
2189
2214
  // src/code-runtime-inference.ts
2190
2215
  async function handleCodeRuntimeInference(input) {
2191
- const { command, metadata, request, state } = input;
2192
- if (state.tokens >= metadata.maxTokensPerInteraction) {
2193
- if (!state.noticeEmitted) {
2194
- state.noticeEmitted = true;
2195
- await input.event({
2196
- type: "message",
2197
- actor: "system",
2198
- body: `The agent paused at the ${metadata.maxTokensPerInteraction.toLocaleString("en-US")}-token per-interaction limit. Send a new instruction to continue.`
2199
- }).catch(() => void 0);
2200
- }
2201
- return {
2202
- protocolVersion: HARNESS_PROTOCOL_VERSION,
2203
- type: "inference.response",
2204
- requestId: request.requestId,
2205
- response: {
2206
- id: `budget:${command.commandId}`,
2207
- provider: "openai",
2208
- model: "interaction-budget",
2209
- role: "assistant",
2210
- content: [{ type: "text", text: "Pause now. The owner-set token limit for this interaction has been reached." }],
2211
- stopReason: "end_turn",
2212
- usage: { inputTokens: 0, outputTokens: 0 }
2213
- }
2214
- };
2215
- }
2216
+ const { command, request, state } = input;
2216
2217
  const startedAt = Date.now();
2217
2218
  const response2 = await input.control.infer(command.sessionId, {
2218
2219
  requestId: request.requestId,
@@ -2232,7 +2233,6 @@ async function handleCodeRuntimeInference(input) {
2232
2233
  durationMs: Date.now() - startedAt,
2233
2234
  interactionId: command.commandId,
2234
2235
  interactionTokens: state.tokens,
2235
- interactionMaxTokens: metadata.maxTokensPerInteraction,
2236
2236
  ...costUsd === void 0 ? {} : { costUsd },
2237
2237
  ...state.costKnown ? { interactionCostUsd: state.costUsd } : {}
2238
2238
  }).catch(() => void 0);
@@ -3363,7 +3363,7 @@ var CodePiRuntimeEngine = class {
3363
3363
  recipeAuthorization: this.options.recipeAuthorization
3364
3364
  }, lease, metadata.role));
3365
3365
  const startedAt = Date.now();
3366
- const interaction = { tokens: 0, noticeEmitted: false, costUsd: 0, costKnown: true };
3366
+ const interaction = { tokens: 0, costUsd: 0, costKnown: true };
3367
3367
  const inference = createCodeRuntimeInference({
3368
3368
  command,
3369
3369
  metadata,
@@ -3378,31 +3378,30 @@ var CodePiRuntimeEngine = class {
3378
3378
  lease,
3379
3379
  workspaceDir: active.workspace.workspaceDir,
3380
3380
  prompt: metadata.prompt,
3381
- signal: active.abort.signal,
3382
- // The owner's per-interaction allowance, enforced by runAgent against
3383
- // INCREMENTAL usage. The control plane still reserves against the same
3384
- // ceiling, but this is what stops the loop cleanly at the boundary rather
3385
- // than letting it discover the limit through a synthesized pause reply.
3386
- budget: { maxTotalTokens: metadata.maxTokensPerInteraction }
3381
+ signal: active.abort.signal
3387
3382
  });
3388
- const body = result.finalText.trim() || (result.status === "completed" ? "The agent finished without a closing message." : result.error ?? "The agent failed.");
3383
+ const closing = result.finalText.trim();
3384
+ const completed = result.status === "completed" && Boolean(closing);
3385
+ const detail = result.error?.trim() || (closing ? "the Code agent failed" : "the Code agent did not produce a closing answer");
3386
+ const body = closing || detail;
3389
3387
  await this.#event(command, {
3390
3388
  type: "message",
3391
- actor: result.status === "completed" ? "agent" : "system",
3389
+ actor: completed ? "agent" : "system",
3392
3390
  body
3393
3391
  }, active.conversationRefs).catch(() => void 0);
3394
3392
  await this.#event(command, {
3395
3393
  type: "status",
3396
- status: result.status === "completed" ? "idle" : "failed",
3394
+ status: completed ? "idle" : "failed",
3397
3395
  durationMs: Date.now() - startedAt
3398
3396
  }, active.conversationRefs).catch(() => void 0);
3399
- if (result.status === "failed") {
3400
- const detail = (result.error ?? "").trim() || "the Code agent failed";
3397
+ if (!completed) {
3401
3398
  await this.#diagnostic(command, active, detail);
3402
3399
  await this.#failure(command, active, detail);
3403
3400
  }
3404
3401
  return {
3405
3402
  ...result,
3403
+ status: completed ? "completed" : "failed",
3404
+ ...!completed ? { error: detail } : {},
3406
3405
  tokens: interaction.tokens,
3407
3406
  ...interaction.costKnown ? { costUsd: interaction.costUsd } : {}
3408
3407
  };