@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
package/dist/code-runtime-cli.js
CHANGED
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,
|
|
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,
|
|
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 >
|
|
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 >
|
|
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 >
|
|
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 >
|
|
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 >
|
|
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
|
|
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
|
|
2203
|
+
status: run.stoppedReason === "refusal" || missingClosing ? "failed" : "completed",
|
|
2204
|
+
finalText,
|
|
2180
2205
|
stoppedReason: run.stoppedReason,
|
|
2181
|
-
...run.stoppedReason === "refusal" ? { error:
|
|
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,
|
|
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,
|
|
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
|
|
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:
|
|
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:
|
|
3394
|
+
status: completed ? "idle" : "failed",
|
|
3397
3395
|
durationMs: Date.now() - startedAt
|
|
3398
3396
|
}, active.conversationRefs).catch(() => void 0);
|
|
3399
|
-
if (
|
|
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
|
};
|