@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.
@@ -168,6 +168,7 @@ function parseSnapshot(value) {
168
168
  return { host, bindings, commands };
169
169
  }
170
170
  async function parseSource(value) {
171
+ const repositoryLimits = { maximumFiles: 1e5, maximumBytes: 80 * 1024 * 1024 };
171
172
  const snapshot = record(record(value)?.snapshot);
172
173
  if (!snapshot || typeof snapshot.repository !== "string" || typeof snapshot.commitSha !== "string" || typeof snapshot.treeDigest !== "string" || !Array.isArray(snapshot.files)) throw invalid("source");
173
174
  const files = snapshot.files.map((value2) => {
@@ -189,12 +190,12 @@ async function parseSource(value) {
189
190
  return { path: file.path, content: file.content };
190
191
  });
191
192
  const source2 = { repository: reference.repository, commitSha: reference.commitSha, files: referenceFiles };
192
- const referenceDigest = await (0, import_code.digestCodeRepositorySnapshot)(source2, { maximumFiles: 1e4, maximumBytes: 16 * 1024 * 1024 });
193
+ const referenceDigest = await (0, import_code.digestCodeRepositorySnapshot)(source2, repositoryLimits);
193
194
  if (referenceDigest !== reference.treeDigest) throw invalid("reference source digest");
194
195
  references.push({ alias: reference.alias, ...source2, treeDigest: referenceDigest });
195
196
  }
196
197
  const source = { repository: snapshot.repository, commitSha: snapshot.commitSha, files };
197
- const digest = await (0, import_code.digestCodeRepositorySnapshot)(source, { maximumFiles: 1e4, maximumBytes: 16 * 1024 * 1024 });
198
+ const digest = await (0, import_code.digestCodeRepositorySnapshot)(source, repositoryLimits);
198
199
  if (digest !== snapshot.treeDigest) throw invalid("source digest");
199
200
  return { ...source, treeDigest: digest, ...references.length ? { references } : {} };
200
201
  }
@@ -1266,8 +1267,11 @@ var import_node_os2 = require("os");
1266
1267
  var import_node_path7 = require("path");
1267
1268
  var RESERVED2 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
1268
1269
  var SECRET2 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
1270
+ var SOURCE_MAX_FILES = 1e5;
1271
+ var SOURCE_MAX_BYTES = 80 * 1024 * 1024;
1272
+ var SOURCE_SET_MAX_BYTES = 480 * 1024 * 1024;
1269
1273
  async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node_os2.tmpdir)()) {
1270
- if (!snapshot.files.length || snapshot.files.length > 1e4) throw new TypeError("Code source file count is invalid");
1274
+ if (!snapshot.files.length || snapshot.files.length > SOURCE_MAX_FILES) throw new TypeError("Code source file count is invalid");
1271
1275
  const root = await (0, import_promises6.mkdtemp)((0, import_node_path7.join)(tempRoot, "odla-code-source-"));
1272
1276
  const sourceDir = (0, import_node_path7.join)(root, "source");
1273
1277
  await (0, import_promises6.mkdir)(sourceDir);
@@ -1279,7 +1283,7 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node
1279
1283
  if (seen.has(file.path)) throw new TypeError("Code source repeats a path");
1280
1284
  seen.add(file.path);
1281
1285
  bytes += Buffer.byteLength(file.path) + Buffer.byteLength(file.content);
1282
- if (bytes > 16 * 1024 * 1024) throw new TypeError("Code source exceeds its byte bound");
1286
+ if (bytes > SOURCE_MAX_BYTES) throw new TypeError("Code source exceeds its byte bound");
1283
1287
  const target = (0, import_node_path7.resolve)(sourceDir, file.path);
1284
1288
  if (!target.startsWith(`${(0, import_node_path7.resolve)(sourceDir)}${import_node_path7.sep}`)) throw new TypeError("Code source path escapes its root");
1285
1289
  await (0, import_promises6.mkdir)((0, import_node_path7.dirname)(target), { recursive: true });
@@ -1287,14 +1291,14 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node
1287
1291
  }
1288
1292
  for (const reference of snapshot.references ?? []) {
1289
1293
  validateAlias(reference.alias);
1290
- if (!reference.files.length || reference.files.length > 1e4) throw new TypeError("Code reference file count is invalid");
1294
+ if (!reference.files.length || reference.files.length > SOURCE_MAX_FILES) throw new TypeError("Code reference file count is invalid");
1291
1295
  for (const file of reference.files) {
1292
1296
  validatePath(file.path);
1293
1297
  const path = `.odla-references/${reference.alias}/${file.path}`;
1294
1298
  if (seen.has(path)) throw new TypeError("Code reference repeats a path");
1295
1299
  seen.add(path);
1296
1300
  bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
1297
- if (bytes > 80 * 1024 * 1024) throw new TypeError("Code source set exceeds its byte bound");
1301
+ if (bytes > SOURCE_SET_MAX_BYTES) throw new TypeError("Code source set exceeds its byte bound");
1298
1302
  const target = (0, import_node_path7.resolve)(sourceDir, path);
1299
1303
  if (!target.startsWith(`${(0, import_node_path7.resolve)(sourceDir)}${import_node_path7.sep}`)) throw new TypeError("Code reference path escapes its root");
1300
1304
  await (0, import_promises6.mkdir)((0, import_node_path7.dirname)(target), { recursive: true });
@@ -1320,7 +1324,7 @@ async function attachCodeRuntimeReferences(workspace, references) {
1320
1324
  validatePath(file.path);
1321
1325
  const path = `.odla-references/${reference.alias}/${file.path}`;
1322
1326
  bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
1323
- if (bytes > 64 * 1024 * 1024) throw new TypeError("Code reference set exceeds its byte bound");
1327
+ if (bytes > SOURCE_SET_MAX_BYTES - SOURCE_MAX_BYTES) throw new TypeError("Code reference set exceeds its byte bound");
1324
1328
  for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
1325
1329
  const target = (0, import_node_path7.resolve)(root, path);
1326
1330
  if (!target.startsWith(`${(0, import_node_path7.resolve)(root)}${import_node_path7.sep}`)) throw new TypeError("Code reference path escapes its root");
@@ -1370,13 +1374,19 @@ async function materializeCommandWorkspace(input) {
1370
1374
  trustedBaseDir: materialized.sourceDir,
1371
1375
  trustedBaseCommitSha: source.commitSha,
1372
1376
  checkpoint: codeCheckpointPayload(command.payload)
1373
- })).workspace : await stageWorkspace(materialized.sourceDir);
1377
+ })).workspace : await stageWorkspace(materialized.sourceDir, {
1378
+ maxFiles: SOURCE_MAX_FILES,
1379
+ maxBytes: SOURCE_SET_MAX_BYTES
1380
+ });
1374
1381
  return { workspace, sourceDigest: source.treeDigest, requestedLocal: null };
1375
1382
  } finally {
1376
1383
  await materialized.cleanup();
1377
1384
  }
1378
1385
  }
1379
1386
 
1387
+ // src/code-runtime-attempt.ts
1388
+ var import_ai2 = require("@odla-ai/ai");
1389
+
1380
1390
  // src/code-agent.ts
1381
1391
  var import_ai = require("@odla-ai/ai");
1382
1392
 
@@ -1577,6 +1587,7 @@ async function runCodeAgent(options) {
1577
1587
  // src/code-runtime-attempt.ts
1578
1588
  async function runCodeAgentAttempt(options) {
1579
1589
  try {
1590
+ const surface = options.surface ?? "v2";
1580
1591
  const { run } = await runCodeAgent({
1581
1592
  inference: options.inference,
1582
1593
  broker: options.broker,
@@ -1586,17 +1597,31 @@ async function runCodeAgentAttempt(options) {
1586
1597
  // The brokered route resolves the real model from platform policy; this
1587
1598
  // id only labels the request the control plane is about to rewrite.
1588
1599
  model: "brokered",
1589
- surface: options.surface ?? "v2",
1600
+ surface,
1590
1601
  ...options.maxSteps === void 0 ? {} : { maxSteps: options.maxSteps },
1591
1602
  ...options.budget ? { budget: options.budget } : {},
1592
1603
  ...options.signal ? { signal: options.signal } : {},
1593
1604
  ...options.onToolCall ? { onToolCall: options.onToolCall } : {}
1594
1605
  });
1606
+ let finalText = run.finalText.trim();
1607
+ if (!finalText && run.stoppedReason !== "refusal") {
1608
+ const closing = await options.inference.chat({
1609
+ model: "brokered",
1610
+ system: `${SYSTEM_PROMPT_FOR[surface]}
1611
+
1612
+ Finish with a concise, non-empty answer to the owner. Do not call tools or promise future work.`,
1613
+ messages: [...run.messages, { role: "user", content: "Give the owner the closing answer now, grounded in the repository evidence and tool results above." }],
1614
+ maxTokens: 16384,
1615
+ ...options.signal ? { signal: options.signal } : {}
1616
+ });
1617
+ finalText = (0, import_ai2.extractText)(closing.content).trim();
1618
+ }
1619
+ const missingClosing = !finalText && run.stoppedReason !== "refusal";
1595
1620
  return {
1596
- status: run.stoppedReason === "refusal" ? "failed" : "completed",
1597
- finalText: run.finalText,
1621
+ status: run.stoppedReason === "refusal" || missingClosing ? "failed" : "completed",
1622
+ finalText,
1598
1623
  stoppedReason: run.stoppedReason,
1599
- ...run.stoppedReason === "refusal" ? { error: run.finalText || "the agent refused the task" } : {}
1624
+ ...run.stoppedReason === "refusal" ? { error: finalText || "the agent refused the task" } : missingClosing ? { error: "the Code agent did not produce a closing answer" } : {}
1600
1625
  };
1601
1626
  } catch (cause) {
1602
1627
  const error = (cause instanceof Error ? cause.message : String(cause)).slice(0, 2e3);
@@ -1606,31 +1631,7 @@ async function runCodeAgentAttempt(options) {
1606
1631
 
1607
1632
  // src/code-runtime-inference.ts
1608
1633
  async function handleCodeRuntimeInference(input) {
1609
- const { command, metadata, request, state } = input;
1610
- if (state.tokens >= metadata.maxTokensPerInteraction) {
1611
- if (!state.noticeEmitted) {
1612
- state.noticeEmitted = true;
1613
- await input.event({
1614
- type: "message",
1615
- actor: "system",
1616
- body: `The agent paused at the ${metadata.maxTokensPerInteraction.toLocaleString("en-US")}-token per-interaction limit. Send a new instruction to continue.`
1617
- }).catch(() => void 0);
1618
- }
1619
- return {
1620
- protocolVersion: HARNESS_PROTOCOL_VERSION,
1621
- type: "inference.response",
1622
- requestId: request.requestId,
1623
- response: {
1624
- id: `budget:${command.commandId}`,
1625
- provider: "openai",
1626
- model: "interaction-budget",
1627
- role: "assistant",
1628
- content: [{ type: "text", text: "Pause now. The owner-set token limit for this interaction has been reached." }],
1629
- stopReason: "end_turn",
1630
- usage: { inputTokens: 0, outputTokens: 0 }
1631
- }
1632
- };
1633
- }
1634
+ const { command, request, state } = input;
1634
1635
  const startedAt = Date.now();
1635
1636
  const response2 = await input.control.infer(command.sessionId, {
1636
1637
  requestId: request.requestId,
@@ -1650,7 +1651,6 @@ async function handleCodeRuntimeInference(input) {
1650
1651
  durationMs: Date.now() - startedAt,
1651
1652
  interactionId: command.commandId,
1652
1653
  interactionTokens: state.tokens,
1653
- interactionMaxTokens: metadata.maxTokensPerInteraction,
1654
1654
  ...costUsd === void 0 ? {} : { costUsd },
1655
1655
  ...state.costKnown ? { interactionCostUsd: state.costUsd } : {}
1656
1656
  }).catch(() => void 0);
@@ -2758,7 +2758,7 @@ var CodePiRuntimeEngine = class {
2758
2758
  recipeAuthorization: this.options.recipeAuthorization
2759
2759
  }, lease, metadata.role));
2760
2760
  const startedAt = Date.now();
2761
- const interaction = { tokens: 0, noticeEmitted: false, costUsd: 0, costKnown: true };
2761
+ const interaction = { tokens: 0, costUsd: 0, costKnown: true };
2762
2762
  const inference = createCodeRuntimeInference({
2763
2763
  command,
2764
2764
  metadata,
@@ -2773,31 +2773,30 @@ var CodePiRuntimeEngine = class {
2773
2773
  lease,
2774
2774
  workspaceDir: active.workspace.workspaceDir,
2775
2775
  prompt: metadata.prompt,
2776
- signal: active.abort.signal,
2777
- // The owner's per-interaction allowance, enforced by runAgent against
2778
- // INCREMENTAL usage. The control plane still reserves against the same
2779
- // ceiling, but this is what stops the loop cleanly at the boundary rather
2780
- // than letting it discover the limit through a synthesized pause reply.
2781
- budget: { maxTotalTokens: metadata.maxTokensPerInteraction }
2776
+ signal: active.abort.signal
2782
2777
  });
2783
- const body = result.finalText.trim() || (result.status === "completed" ? "The agent finished without a closing message." : result.error ?? "The agent failed.");
2778
+ const closing = result.finalText.trim();
2779
+ const completed = result.status === "completed" && Boolean(closing);
2780
+ const detail = result.error?.trim() || (closing ? "the Code agent failed" : "the Code agent did not produce a closing answer");
2781
+ const body = closing || detail;
2784
2782
  await this.#event(command, {
2785
2783
  type: "message",
2786
- actor: result.status === "completed" ? "agent" : "system",
2784
+ actor: completed ? "agent" : "system",
2787
2785
  body
2788
2786
  }, active.conversationRefs).catch(() => void 0);
2789
2787
  await this.#event(command, {
2790
2788
  type: "status",
2791
- status: result.status === "completed" ? "idle" : "failed",
2789
+ status: completed ? "idle" : "failed",
2792
2790
  durationMs: Date.now() - startedAt
2793
2791
  }, active.conversationRefs).catch(() => void 0);
2794
- if (result.status === "failed") {
2795
- const detail = (result.error ?? "").trim() || "the Code agent failed";
2792
+ if (!completed) {
2796
2793
  await this.#diagnostic(command, active, detail);
2797
2794
  await this.#failure(command, active, detail);
2798
2795
  }
2799
2796
  return {
2800
2797
  ...result,
2798
+ status: completed ? "completed" : "failed",
2799
+ ...!completed ? { error: detail } : {},
2801
2800
  tokens: interaction.tokens,
2802
2801
  ...interaction.costKnown ? { costUsd: interaction.costUsd } : {}
2803
2802
  };