@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.
- package/dist/{chunk-UVGZHNLW.js → chunk-5HX5LWTG.js} +48 -51
- package/dist/chunk-5HX5LWTG.js.map +1 -0
- package/dist/code-runtime-cli.cjs +49 -50
- package/dist/code-runtime-cli.cjs.map +1 -1
- package/dist/code-runtime-cli.js +1 -1
- package/dist/node.cjs +49 -50
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +0 -1
- package/dist/node.d.ts +0 -1
- package/dist/node.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-UVGZHNLW.js.map +0 -1
|
@@ -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,
|
|
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,
|
|
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 >
|
|
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 >
|
|
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 >
|
|
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 >
|
|
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 >
|
|
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
|
|
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
|
|
1621
|
+
status: run.stoppedReason === "refusal" || missingClosing ? "failed" : "completed",
|
|
1622
|
+
finalText,
|
|
1598
1623
|
stoppedReason: run.stoppedReason,
|
|
1599
|
-
...run.stoppedReason === "refusal" ? { error:
|
|
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,
|
|
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,
|
|
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
|
|
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:
|
|
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:
|
|
2789
|
+
status: completed ? "idle" : "failed",
|
|
2792
2790
|
durationMs: Date.now() - startedAt
|
|
2793
2791
|
}, active.conversationRefs).catch(() => void 0);
|
|
2794
|
-
if (
|
|
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
|
};
|