@odla-ai/harness 0.9.1 → 0.9.3
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-HDIR4MM5.js → chunk-5LRYJKUI.js} +3 -3
- package/dist/{chunk-4EPJJMFG.js → chunk-AEAISFY3.js} +157 -217
- package/dist/chunk-AEAISFY3.js.map +1 -0
- package/dist/chunk-FAN2R3GW.js +166 -0
- package/dist/chunk-FAN2R3GW.js.map +1 -0
- package/dist/{chunk-CIKYMC67.js → chunk-OZBJNTML.js} +4 -4
- package/dist/{chunk-I43KTCJ2.js → chunk-U324RQ4N.js} +1 -1
- package/dist/{chunk-I43KTCJ2.js.map → chunk-U324RQ4N.js.map} +1 -1
- package/dist/{chunk-VGNIDRKM.js → chunk-VDY5V7ZG.js} +2 -2
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +4 -4
- package/dist/code-runtime-cli.cjs +258 -163
- package/dist/code-runtime-cli.cjs.map +1 -1
- package/dist/code-runtime-cli.js +6 -5
- package/dist/code-runtime-cli.js.map +1 -1
- package/dist/index.cjs +172 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -3
- package/dist/index.d.ts +24 -3
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/node.cjs +261 -162
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +39 -6
- package/dist/node.d.ts +39 -6
- package/dist/node.js +10 -5
- package/dist/node.js.map +1 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/dist/{types-BazqxWK8.d.cts → types-_8y8vBDI.d.cts} +5 -1
- package/dist/{types-BazqxWK8.d.ts → types-_8y8vBDI.d.ts} +5 -1
- package/package.json +1 -1
- package/dist/chunk-4EPJJMFG.js.map +0 -1
- /package/dist/{chunk-HDIR4MM5.js.map → chunk-5LRYJKUI.js.map} +0 -0
- /package/dist/{chunk-CIKYMC67.js.map → chunk-OZBJNTML.js.map} +0 -0
- /package/dist/{chunk-VGNIDRKM.js.map → chunk-VDY5V7ZG.js.map} +0 -0
|
@@ -110,7 +110,7 @@ async function parseSource(value) {
|
|
|
110
110
|
}
|
|
111
111
|
function parseReview(value) {
|
|
112
112
|
const review = record(record(value)?.review);
|
|
113
|
-
if (!review || !["approved", "rejected"].includes(String(review.verdict)) || typeof review.reviewDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(review.reviewDigest) || typeof review.provider !== "string" || !review.provider || typeof review.model !== "string" || !review.model || !Number.isSafeInteger(review.policyVersion) || Number(review.policyVersion) < 1) throw invalid("review");
|
|
113
|
+
if (!review || !["approved", "rejected"].includes(String(review.verdict)) || typeof review.reviewDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(review.reviewDigest) || typeof review.provider !== "string" || !review.provider || typeof review.model !== "string" || !review.model || !Number.isSafeInteger(review.policyVersion) || Number(review.policyVersion) < 1 || !Number.isSafeInteger(review.score) || Number(review.score) < 0 || Number(review.score) > 100 || typeof review.summary !== "string" || !Array.isArray(review.findings) || review.findings.length > 12) throw invalid("review");
|
|
114
114
|
return review;
|
|
115
115
|
}
|
|
116
116
|
function parseCandidate(value) {
|
|
@@ -417,10 +417,41 @@ var SECRET = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-toke
|
|
|
417
417
|
var PATH = /^[A-Za-z0-9_@+.,-]+(?:\/[A-Za-z0-9_@+.,-]+)*$/;
|
|
418
418
|
var FORBIDDEN = /^(?:GIT binary patch|Binary files |rename (?:from|to) |copy (?:from|to) |similarity index |old mode |new mode |deleted file mode 160000|new file mode 160000)/m;
|
|
419
419
|
function stripPatchEnvelope(patch2) {
|
|
420
|
-
if (!/^\*\*\* (?:Begin|End) Patch\s*$/m.test(patch2)) return patch2;
|
|
420
|
+
if (!/^\*\*\* (?:Begin|End) Patch\s*$/m.test(patch2)) return applyPatchDialectToDiff(patch2);
|
|
421
421
|
const kept = patch2.split("\n").filter((line) => !/^\*\*\* (?:Begin|End) Patch\s*$/.test(line));
|
|
422
422
|
const stripped = kept.join("\n");
|
|
423
|
-
|
|
423
|
+
if (/^diff --git /m.test(stripped)) return stripped;
|
|
424
|
+
const translated = applyPatchDialectToDiff(stripped);
|
|
425
|
+
return translated === stripped ? patch2 : translated;
|
|
426
|
+
}
|
|
427
|
+
function applyPatchDialectToDiff(patch2) {
|
|
428
|
+
if (!/^\*\*\* (?:Update|Add|Delete) File: /m.test(patch2)) return patch2;
|
|
429
|
+
const out = [];
|
|
430
|
+
let open = false;
|
|
431
|
+
for (const line of patch2.split("\n")) {
|
|
432
|
+
const file = /^\*\*\* (Update|Add|Delete) File: (.+?)\s*$/.exec(line);
|
|
433
|
+
if (file) {
|
|
434
|
+
const [, verb, raw] = file;
|
|
435
|
+
const path = raw.trim();
|
|
436
|
+
if (!PATH.test(path)) return patch2;
|
|
437
|
+
out.push(`diff --git a/${path} b/${path}`);
|
|
438
|
+
if (verb === "Add") out.push("new file mode 100644", "--- /dev/null", `+++ b/${path}`);
|
|
439
|
+
else if (verb === "Delete") out.push(`--- a/${path}`, "+++ /dev/null");
|
|
440
|
+
else out.push(`--- a/${path}`, `+++ b/${path}`);
|
|
441
|
+
open = true;
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
if (/^\*\*\* /.test(line)) continue;
|
|
445
|
+
if (!open) continue;
|
|
446
|
+
if (/^@@/.test(line)) {
|
|
447
|
+
out.push("@@ -1 +1 @@");
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
out.push(line);
|
|
451
|
+
}
|
|
452
|
+
if (!open) return patch2;
|
|
453
|
+
return `${out.join("\n").replace(/\n+$/, "")}
|
|
454
|
+
`;
|
|
424
455
|
}
|
|
425
456
|
function validateCodePatch(rawPatch, maxBytes) {
|
|
426
457
|
const patch2 = stripPatchEnvelope(rawPatch);
|
|
@@ -473,17 +504,20 @@ function resolveCodePath(workspaceDir, path) {
|
|
|
473
504
|
if (target !== root && !target.startsWith(`${root}${import_node_path.sep}`)) throw new TypeError("path escapes the staged workspace");
|
|
474
505
|
return target;
|
|
475
506
|
}
|
|
507
|
+
function hasContextFreeHunk(patch2) {
|
|
508
|
+
const bodies = patch2.split(/^@@.*$/m).slice(1);
|
|
509
|
+
return bodies.some((body) => !body.split("\n").some((line) => line.startsWith(" ") && line.trim().length > 0));
|
|
510
|
+
}
|
|
476
511
|
function describePatchFailure(patch2, detail) {
|
|
477
512
|
const hunks = patch2.split("\n").filter((line) => line.startsWith("@@"));
|
|
478
|
-
const
|
|
479
|
-
const contextless = bodies.some((body) => !body.split("\n").some((line) => line.startsWith(" ") && line.trim().length > 0));
|
|
480
|
-
const hint = hunks.length > 0 && contextless ? " A hunk has no context lines; include at least one unchanged line above or below each change." : "";
|
|
513
|
+
const hint = hunks.length > 0 && hasContextFreeHunk(patch2) ? " A hunk has no context lines; include at least one unchanged line above or below each change." : "";
|
|
481
514
|
return `patch did not apply: ${detail}${hint}`;
|
|
482
515
|
}
|
|
483
516
|
async function applyCodePatch(workspaceDir, rawPatch, paths2) {
|
|
484
517
|
const patch2 = stripPatchEnvelope(rawPatch);
|
|
485
|
-
|
|
486
|
-
await gitApply(workspaceDir, patch2,
|
|
518
|
+
const zero = hasContextFreeHunk(patch2);
|
|
519
|
+
await gitApply(workspaceDir, patch2, true, zero);
|
|
520
|
+
await gitApply(workspaceDir, patch2, false, zero);
|
|
487
521
|
for (const path of paths2) {
|
|
488
522
|
try {
|
|
489
523
|
const info = await (0, import_promises.lstat)(resolveCodePath(workspaceDir, path));
|
|
@@ -495,9 +529,16 @@ async function applyCodePatch(workspaceDir, rawPatch, paths2) {
|
|
|
495
529
|
}
|
|
496
530
|
}
|
|
497
531
|
}
|
|
498
|
-
function gitApply(cwd, patch2, check) {
|
|
532
|
+
function gitApply(cwd, patch2, check, unidiffZero = false) {
|
|
499
533
|
return new Promise((accept, reject) => {
|
|
500
|
-
const args = [
|
|
534
|
+
const args = [
|
|
535
|
+
"apply",
|
|
536
|
+
"--recount",
|
|
537
|
+
...unidiffZero ? ["--unidiff-zero"] : [],
|
|
538
|
+
"--whitespace=nowarn",
|
|
539
|
+
...check ? ["--check"] : [],
|
|
540
|
+
"-"
|
|
541
|
+
];
|
|
501
542
|
const child = (0, import_node_child_process.spawn)("git", args, {
|
|
502
543
|
cwd,
|
|
503
544
|
shell: false,
|
|
@@ -1210,9 +1251,9 @@ async function prepareRuntimeCheckpoint(input) {
|
|
|
1210
1251
|
if (evidence.receipt.outcome === "passed") {
|
|
1211
1252
|
verification = evidence.receipt;
|
|
1212
1253
|
review = await input.review(patch2, verification);
|
|
1213
|
-
note = review
|
|
1254
|
+
note = describeReview(review);
|
|
1214
1255
|
} else {
|
|
1215
|
-
note =
|
|
1256
|
+
note = describeGateFailure(evidence);
|
|
1216
1257
|
}
|
|
1217
1258
|
} catch (cause) {
|
|
1218
1259
|
note = `Candidate verification or review failed closed: ${message(cause)}`;
|
|
@@ -1237,6 +1278,23 @@ async function prepareRuntimeCheckpoint(input) {
|
|
|
1237
1278
|
});
|
|
1238
1279
|
return { checkpoint, verification, review, note };
|
|
1239
1280
|
}
|
|
1281
|
+
function describeReview(review) {
|
|
1282
|
+
const findings = review.findings.map((finding) => ` - [${finding.severity}] ${finding.detail}`).join("\n");
|
|
1283
|
+
const head = review.verdict === "approved" ? `Clean verification and independent review passed (score ${review.score}/100).` : `Clean verification passed; independent review REJECTED this candidate (score ${review.score}/100). Address the findings and checkpoint again.`;
|
|
1284
|
+
return [head, review.summary, findings].filter(Boolean).join("\n").slice(0, 4e3);
|
|
1285
|
+
}
|
|
1286
|
+
function describeGateFailure(evidence) {
|
|
1287
|
+
const failed = evidence.receipt.recipes.filter((recipe2) => recipe2.status !== "passed");
|
|
1288
|
+
const detail = failed.map((recipe2) => {
|
|
1289
|
+
const log = evidence.logs.find((entry) => entry.recipeId === recipe2.recipeId);
|
|
1290
|
+
const output = `${log?.stdout ?? ""}
|
|
1291
|
+
${log?.stderr ?? ""}`.trim();
|
|
1292
|
+
return `${recipe2.recipeId}=${recipe2.status}${output ? `
|
|
1293
|
+
${output.slice(0, 1500)}` : ""}`;
|
|
1294
|
+
}).join("\n\n");
|
|
1295
|
+
return `Clean verification failed. Fix this and checkpoint again:
|
|
1296
|
+
${detail}`.slice(0, 4e3);
|
|
1297
|
+
}
|
|
1240
1298
|
var message = (value) => (value instanceof Error ? value.message : String(value)).slice(0, 500);
|
|
1241
1299
|
|
|
1242
1300
|
// src/code-runtime-checkpoint-manager.ts
|
|
@@ -1472,12 +1530,24 @@ async function materializeCommandWorkspace(input) {
|
|
|
1472
1530
|
resume
|
|
1473
1531
|
});
|
|
1474
1532
|
if (command.payload.sourceSet) {
|
|
1475
|
-
|
|
1476
|
-
if (selected.repository !== metadata.repository || selected.commitSha !== metadata.baseCommitSha || selected.treeDigest !== metadata.sourceTreeDigest) {
|
|
1533
|
+
if (prepared.trustedBaseDigest !== metadata.sourceTreeDigest) {
|
|
1477
1534
|
await prepared.workspace.cleanup();
|
|
1478
1535
|
throw new TypeError("Code local source does not match the selected GitHub primary source");
|
|
1479
1536
|
}
|
|
1480
|
-
|
|
1537
|
+
const set = command.payload.sourceSet && typeof command.payload.sourceSet === "object" && !Array.isArray(command.payload.sourceSet) ? command.payload.sourceSet : null;
|
|
1538
|
+
const references = set?.references;
|
|
1539
|
+
if (!Array.isArray(references)) {
|
|
1540
|
+
await prepared.workspace.cleanup();
|
|
1541
|
+
throw new TypeError("Code selected source set is invalid");
|
|
1542
|
+
}
|
|
1543
|
+
if (references.length) {
|
|
1544
|
+
const selected = await input.control.source(command.sessionId);
|
|
1545
|
+
if (selected.repository !== metadata.repository || selected.commitSha !== metadata.baseCommitSha || selected.treeDigest !== metadata.sourceTreeDigest) {
|
|
1546
|
+
await prepared.workspace.cleanup();
|
|
1547
|
+
throw new TypeError("Code local source does not match the selected GitHub primary source");
|
|
1548
|
+
}
|
|
1549
|
+
await attachCodeRuntimeReferences(prepared.workspace, selected.references ?? []);
|
|
1550
|
+
}
|
|
1481
1551
|
}
|
|
1482
1552
|
return {
|
|
1483
1553
|
workspace: prepared.workspace,
|
|
@@ -1881,6 +1951,166 @@ function createCodeRuntimeInference(options) {
|
|
|
1881
1951
|
};
|
|
1882
1952
|
}
|
|
1883
1953
|
|
|
1954
|
+
// src/code-tool-presentation.ts
|
|
1955
|
+
var text = (value, maximum) => {
|
|
1956
|
+
if (typeof value !== "string") return void 0;
|
|
1957
|
+
const bounded = value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
|
|
1958
|
+
return bounded ? bounded.slice(0, maximum) : void 0;
|
|
1959
|
+
};
|
|
1960
|
+
var integer2 = (value) => Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : void 0;
|
|
1961
|
+
var record3 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1962
|
+
var excerpt = (value, tail = false) => {
|
|
1963
|
+
if (typeof value !== "string") return void 0;
|
|
1964
|
+
const safe = value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ");
|
|
1965
|
+
const source = (tail ? safe.slice(-1e4) : safe.slice(0, 1e4)).trim();
|
|
1966
|
+
if (!source) return void 0;
|
|
1967
|
+
const lines = source.split("\n").filter((line) => line.trim()).map((line) => line.slice(0, 240));
|
|
1968
|
+
const selected = tail ? lines.slice(-10) : lines.slice(0, 10);
|
|
1969
|
+
return text(selected.join("\n"), 2400);
|
|
1970
|
+
};
|
|
1971
|
+
var paths = (value) => {
|
|
1972
|
+
if (!Array.isArray(value)) return void 0;
|
|
1973
|
+
const items = value.flatMap((item) => {
|
|
1974
|
+
const path = text(item, 1024);
|
|
1975
|
+
return path ? [path] : [];
|
|
1976
|
+
}).slice(0, 12);
|
|
1977
|
+
return items.length ? items : void 0;
|
|
1978
|
+
};
|
|
1979
|
+
function patchStats(value) {
|
|
1980
|
+
if (typeof value !== "string") return {};
|
|
1981
|
+
let additions = 0;
|
|
1982
|
+
let deletions = 0;
|
|
1983
|
+
for (const line of value.slice(0, 262144).split("\n")) {
|
|
1984
|
+
if (line.startsWith("+++") || line.startsWith("---")) continue;
|
|
1985
|
+
if (line.startsWith("+")) additions += 1;
|
|
1986
|
+
else if (line.startsWith("-")) deletions += 1;
|
|
1987
|
+
}
|
|
1988
|
+
return { ...additions ? { additions } : {}, ...deletions ? { deletions } : {} };
|
|
1989
|
+
}
|
|
1990
|
+
function searchResults(value) {
|
|
1991
|
+
if (typeof value !== "string") return void 0;
|
|
1992
|
+
const results = value.split("\n").flatMap((line) => {
|
|
1993
|
+
const match = /^([^:\n]{1,1024}):(\d+):\s?(.*)$/.exec(line);
|
|
1994
|
+
if (!match) return [];
|
|
1995
|
+
const lineNumber = Number(match[2]);
|
|
1996
|
+
const itemText = text(match[3], 240);
|
|
1997
|
+
if (!Number.isSafeInteger(lineNumber) || lineNumber < 1) return [];
|
|
1998
|
+
return [{ path: match[1], line: lineNumber, ...itemText ? { text: itemText } : {} }];
|
|
1999
|
+
}).slice(0, 5);
|
|
2000
|
+
return results.length ? results : void 0;
|
|
2001
|
+
}
|
|
2002
|
+
function codeToolRequestPresentation(request) {
|
|
2003
|
+
const input = request.input;
|
|
2004
|
+
if (request.tool === "sandbox.read") {
|
|
2005
|
+
const path = text(input.path, 1024);
|
|
2006
|
+
if (!path) return void 0;
|
|
2007
|
+
const startLine = integer2(input.startLine);
|
|
2008
|
+
const endLine = integer2(input.endLine);
|
|
2009
|
+
return { kind: "read", path, ...startLine ? { startLine } : {}, ...endLine ? { endLine } : {} };
|
|
2010
|
+
}
|
|
2011
|
+
if (request.tool === "sandbox.list") {
|
|
2012
|
+
const scope = text(input.prefix, 1024);
|
|
2013
|
+
return { kind: "list", ...scope ? { scope } : {} };
|
|
2014
|
+
}
|
|
2015
|
+
if (request.tool === "sandbox.search" || request.tool === "sandbox.overview" || request.tool === "sandbox.where_is" || request.tool === "sandbox.who_imports" || request.tool === "sandbox.who_touches") {
|
|
2016
|
+
const query = text(input.query, 512);
|
|
2017
|
+
const scope = request.tool === "sandbox.search" ? text(input.prefix, 1024) : void 0;
|
|
2018
|
+
if (request.tool === "sandbox.search" && !query) return void 0;
|
|
2019
|
+
return { kind: "query", ...query ? { query } : {}, ...scope ? { scope } : {} };
|
|
2020
|
+
}
|
|
2021
|
+
if (request.tool === "sandbox.apply_patch") {
|
|
2022
|
+
return { kind: "patch", ...patchStats(input.patch) };
|
|
2023
|
+
}
|
|
2024
|
+
const recipeId = text(input.recipeId, 120);
|
|
2025
|
+
return recipeId ? { kind: "recipe", recipeId } : void 0;
|
|
2026
|
+
}
|
|
2027
|
+
function codeToolResultPresentation(request, response2) {
|
|
2028
|
+
const started = codeToolRequestPresentation(request);
|
|
2029
|
+
if (!started || !response2.ok) return started;
|
|
2030
|
+
const details = record3(response2.details);
|
|
2031
|
+
if (started.kind === "read") {
|
|
2032
|
+
return {
|
|
2033
|
+
...started,
|
|
2034
|
+
...integer2(details?.startLine) ? { startLine: integer2(details?.startLine) } : {},
|
|
2035
|
+
...integer2(details?.endLine) ? { endLine: integer2(details?.endLine) } : {},
|
|
2036
|
+
...excerpt(response2.content) ? { excerpt: excerpt(response2.content) } : {}
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
if (started.kind === "list") {
|
|
2040
|
+
const listed = response2.content.split("\n").filter((line) => line && !line.startsWith("\u2026") && !line.startsWith("Workspace ")).map((line) => text(line, 1024)).filter((line) => Boolean(line)).slice(0, 8);
|
|
2041
|
+
return {
|
|
2042
|
+
...started,
|
|
2043
|
+
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
2044
|
+
...listed.length ? { paths: listed } : {}
|
|
2045
|
+
};
|
|
2046
|
+
}
|
|
2047
|
+
if (started.kind === "query") {
|
|
2048
|
+
const results = request.tool === "sandbox.search" ? searchResults(response2.content) : void 0;
|
|
2049
|
+
const resultExcerpt = request.tool === "sandbox.search" ? void 0 : excerpt(response2.content);
|
|
2050
|
+
return {
|
|
2051
|
+
...started,
|
|
2052
|
+
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
2053
|
+
...results ? { results } : {},
|
|
2054
|
+
...resultExcerpt ? { excerpt: resultExcerpt } : {}
|
|
2055
|
+
};
|
|
2056
|
+
}
|
|
2057
|
+
if (started.kind === "patch") {
|
|
2058
|
+
return { ...started, ...paths(details?.paths) ? { paths: paths(details?.paths) } : {} };
|
|
2059
|
+
}
|
|
2060
|
+
const output = response2.content.replace(/^Recipe [^\n]*\.?\s*/u, "");
|
|
2061
|
+
return {
|
|
2062
|
+
...started,
|
|
2063
|
+
...integer2(details?.exitCode) !== void 0 ? { exitCode: integer2(details?.exitCode) } : {},
|
|
2064
|
+
...typeof details?.timedOut === "boolean" ? { timedOut: details.timedOut } : {},
|
|
2065
|
+
...typeof details?.outputLimitExceeded === "boolean" ? { outputLimitExceeded: details.outputLimitExceeded } : {},
|
|
2066
|
+
...excerpt(output, true) ? { excerpt: excerpt(output, true) } : {}
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
// src/code-runtime-observer.ts
|
|
2071
|
+
var MAX_FAILURE_REASON = 240;
|
|
2072
|
+
function toolFailureReason(response2) {
|
|
2073
|
+
if (response2.ok) return void 0;
|
|
2074
|
+
const supplied = response2.details?.failureReason;
|
|
2075
|
+
const reason = typeof supplied === "string" && supplied || DEFAULT_FAILURE_REASON[response2.content] || response2.content || "tool request failed; inspect the tool input and workspace state";
|
|
2076
|
+
return reason.slice(0, MAX_FAILURE_REASON);
|
|
2077
|
+
}
|
|
2078
|
+
var DEFAULT_FAILURE_REASON = {
|
|
2079
|
+
"tool denied by CaMeL policy": "tool denied by CaMeL policy",
|
|
2080
|
+
"review sessions are read-only": "review session is read-only; workspace changes are not permitted"
|
|
2081
|
+
};
|
|
2082
|
+
function observedBroker(input) {
|
|
2083
|
+
const now = input.now ?? Date.now;
|
|
2084
|
+
return {
|
|
2085
|
+
execute: async (context, request) => {
|
|
2086
|
+
const startedAt = now();
|
|
2087
|
+
const operationId = input.operationIdFor(request.requestId);
|
|
2088
|
+
const startedPresentation = codeToolRequestPresentation(request);
|
|
2089
|
+
await input.emit({
|
|
2090
|
+
type: "tool",
|
|
2091
|
+
phase: "started",
|
|
2092
|
+
tool: request.tool,
|
|
2093
|
+
operationId,
|
|
2094
|
+
...startedPresentation ? { presentation: startedPresentation } : {}
|
|
2095
|
+
});
|
|
2096
|
+
const response2 = await input.broker.execute(context, request);
|
|
2097
|
+
const completedPresentation = codeToolResultPresentation(request, response2);
|
|
2098
|
+
const failureReason = toolFailureReason(response2);
|
|
2099
|
+
await input.emit({
|
|
2100
|
+
type: "tool",
|
|
2101
|
+
phase: "completed",
|
|
2102
|
+
tool: request.tool,
|
|
2103
|
+
ok: response2.ok,
|
|
2104
|
+
durationMs: now() - startedAt,
|
|
2105
|
+
operationId,
|
|
2106
|
+
...failureReason ? { failureReason } : {},
|
|
2107
|
+
...completedPresentation ? { presentation: completedPresentation } : {}
|
|
2108
|
+
});
|
|
2109
|
+
return response2;
|
|
2110
|
+
}
|
|
2111
|
+
};
|
|
2112
|
+
}
|
|
2113
|
+
|
|
1884
2114
|
// src/code-tool-policy.ts
|
|
1885
2115
|
var import_camel = require("@odla-ai/camel");
|
|
1886
2116
|
var import_policy = require("@odla-ai/camel/policy");
|
|
@@ -2119,8 +2349,14 @@ function optionalInteger(value) {
|
|
|
2119
2349
|
if (!Number.isSafeInteger(value) || value < 1) throw new TypeError("line bounds must be positive integers");
|
|
2120
2350
|
return value;
|
|
2121
2351
|
}
|
|
2352
|
+
var DEFAULT_FAILURE_REASON2 = {
|
|
2353
|
+
"tool denied by CaMeL policy": "tool denied by CaMeL policy",
|
|
2354
|
+
"review sessions are read-only": "review session is read-only; workspace changes are not permitted"
|
|
2355
|
+
};
|
|
2122
2356
|
function response(request, ok, content, details) {
|
|
2123
|
-
return { requestId: request.requestId, ok, content, ...details ? { details } : {} };
|
|
2357
|
+
if (ok) return { requestId: request.requestId, ok, content, ...details ? { details } : {} };
|
|
2358
|
+
const failureReason = typeof details?.failureReason === "string" && details.failureReason || DEFAULT_FAILURE_REASON2[content] || content || "tool request failed; inspect the tool input and workspace state";
|
|
2359
|
+
return { requestId: request.requestId, ok, content, details: { ...details, failureReason } };
|
|
2124
2360
|
}
|
|
2125
2361
|
|
|
2126
2362
|
// src/code-tool-reads.ts
|
|
@@ -2898,122 +3134,6 @@ async function appendCodeRuntimeEvent(control, command, event, refs) {
|
|
|
2898
3134
|
var digestRuntimeValue = (value) => `sha256:${(0, import_node_crypto4.createHash)("sha256").update(value).digest("hex")}`;
|
|
2899
3135
|
var runtimeErrorMessage = (value) => value instanceof Error ? value.message : String(value);
|
|
2900
3136
|
|
|
2901
|
-
// src/code-tool-presentation.ts
|
|
2902
|
-
var text = (value, maximum) => {
|
|
2903
|
-
if (typeof value !== "string") return void 0;
|
|
2904
|
-
const bounded = value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
|
|
2905
|
-
return bounded ? bounded.slice(0, maximum) : void 0;
|
|
2906
|
-
};
|
|
2907
|
-
var integer2 = (value) => Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : void 0;
|
|
2908
|
-
var record3 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
2909
|
-
var excerpt = (value, tail = false) => {
|
|
2910
|
-
if (typeof value !== "string") return void 0;
|
|
2911
|
-
const safe = value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ");
|
|
2912
|
-
const source = (tail ? safe.slice(-1e4) : safe.slice(0, 1e4)).trim();
|
|
2913
|
-
if (!source) return void 0;
|
|
2914
|
-
const lines = source.split("\n").filter((line) => line.trim()).map((line) => line.slice(0, 240));
|
|
2915
|
-
const selected = tail ? lines.slice(-10) : lines.slice(0, 10);
|
|
2916
|
-
return text(selected.join("\n"), 2400);
|
|
2917
|
-
};
|
|
2918
|
-
var paths = (value) => {
|
|
2919
|
-
if (!Array.isArray(value)) return void 0;
|
|
2920
|
-
const items = value.flatMap((item) => {
|
|
2921
|
-
const path = text(item, 1024);
|
|
2922
|
-
return path ? [path] : [];
|
|
2923
|
-
}).slice(0, 12);
|
|
2924
|
-
return items.length ? items : void 0;
|
|
2925
|
-
};
|
|
2926
|
-
function patchStats(value) {
|
|
2927
|
-
if (typeof value !== "string") return {};
|
|
2928
|
-
let additions = 0;
|
|
2929
|
-
let deletions = 0;
|
|
2930
|
-
for (const line of value.slice(0, 262144).split("\n")) {
|
|
2931
|
-
if (line.startsWith("+++") || line.startsWith("---")) continue;
|
|
2932
|
-
if (line.startsWith("+")) additions += 1;
|
|
2933
|
-
else if (line.startsWith("-")) deletions += 1;
|
|
2934
|
-
}
|
|
2935
|
-
return { ...additions ? { additions } : {}, ...deletions ? { deletions } : {} };
|
|
2936
|
-
}
|
|
2937
|
-
function searchResults(value) {
|
|
2938
|
-
if (typeof value !== "string") return void 0;
|
|
2939
|
-
const results = value.split("\n").flatMap((line) => {
|
|
2940
|
-
const match = /^([^:\n]{1,1024}):(\d+):\s?(.*)$/.exec(line);
|
|
2941
|
-
if (!match) return [];
|
|
2942
|
-
const lineNumber = Number(match[2]);
|
|
2943
|
-
const itemText = text(match[3], 240);
|
|
2944
|
-
if (!Number.isSafeInteger(lineNumber) || lineNumber < 1) return [];
|
|
2945
|
-
return [{ path: match[1], line: lineNumber, ...itemText ? { text: itemText } : {} }];
|
|
2946
|
-
}).slice(0, 5);
|
|
2947
|
-
return results.length ? results : void 0;
|
|
2948
|
-
}
|
|
2949
|
-
function codeToolRequestPresentation(request) {
|
|
2950
|
-
const input = request.input;
|
|
2951
|
-
if (request.tool === "sandbox.read") {
|
|
2952
|
-
const path = text(input.path, 1024);
|
|
2953
|
-
if (!path) return void 0;
|
|
2954
|
-
const startLine = integer2(input.startLine);
|
|
2955
|
-
const endLine = integer2(input.endLine);
|
|
2956
|
-
return { kind: "read", path, ...startLine ? { startLine } : {}, ...endLine ? { endLine } : {} };
|
|
2957
|
-
}
|
|
2958
|
-
if (request.tool === "sandbox.list") {
|
|
2959
|
-
const scope = text(input.prefix, 1024);
|
|
2960
|
-
return { kind: "list", ...scope ? { scope } : {} };
|
|
2961
|
-
}
|
|
2962
|
-
if (request.tool === "sandbox.search" || request.tool === "sandbox.overview" || request.tool === "sandbox.where_is" || request.tool === "sandbox.who_imports" || request.tool === "sandbox.who_touches") {
|
|
2963
|
-
const query = text(input.query, 512);
|
|
2964
|
-
const scope = request.tool === "sandbox.search" ? text(input.prefix, 1024) : void 0;
|
|
2965
|
-
if (request.tool === "sandbox.search" && !query) return void 0;
|
|
2966
|
-
return { kind: "query", ...query ? { query } : {}, ...scope ? { scope } : {} };
|
|
2967
|
-
}
|
|
2968
|
-
if (request.tool === "sandbox.apply_patch") {
|
|
2969
|
-
return { kind: "patch", ...patchStats(input.patch) };
|
|
2970
|
-
}
|
|
2971
|
-
const recipeId = text(input.recipeId, 120);
|
|
2972
|
-
return recipeId ? { kind: "recipe", recipeId } : void 0;
|
|
2973
|
-
}
|
|
2974
|
-
function codeToolResultPresentation(request, response2) {
|
|
2975
|
-
const started = codeToolRequestPresentation(request);
|
|
2976
|
-
if (!started || !response2.ok) return started;
|
|
2977
|
-
const details = record3(response2.details);
|
|
2978
|
-
if (started.kind === "read") {
|
|
2979
|
-
return {
|
|
2980
|
-
...started,
|
|
2981
|
-
...integer2(details?.startLine) ? { startLine: integer2(details?.startLine) } : {},
|
|
2982
|
-
...integer2(details?.endLine) ? { endLine: integer2(details?.endLine) } : {},
|
|
2983
|
-
...excerpt(response2.content) ? { excerpt: excerpt(response2.content) } : {}
|
|
2984
|
-
};
|
|
2985
|
-
}
|
|
2986
|
-
if (started.kind === "list") {
|
|
2987
|
-
const listed = response2.content.split("\n").filter((line) => line && !line.startsWith("\u2026") && !line.startsWith("Workspace ")).map((line) => text(line, 1024)).filter((line) => Boolean(line)).slice(0, 8);
|
|
2988
|
-
return {
|
|
2989
|
-
...started,
|
|
2990
|
-
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
2991
|
-
...listed.length ? { paths: listed } : {}
|
|
2992
|
-
};
|
|
2993
|
-
}
|
|
2994
|
-
if (started.kind === "query") {
|
|
2995
|
-
const results = request.tool === "sandbox.search" ? searchResults(response2.content) : void 0;
|
|
2996
|
-
const resultExcerpt = request.tool === "sandbox.search" ? void 0 : excerpt(response2.content);
|
|
2997
|
-
return {
|
|
2998
|
-
...started,
|
|
2999
|
-
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
3000
|
-
...results ? { results } : {},
|
|
3001
|
-
...resultExcerpt ? { excerpt: resultExcerpt } : {}
|
|
3002
|
-
};
|
|
3003
|
-
}
|
|
3004
|
-
if (started.kind === "patch") {
|
|
3005
|
-
return { ...started, ...paths(details?.paths) ? { paths: paths(details?.paths) } : {} };
|
|
3006
|
-
}
|
|
3007
|
-
const output = response2.content.replace(/^Recipe [^\n]*\.?\s*/u, "");
|
|
3008
|
-
return {
|
|
3009
|
-
...started,
|
|
3010
|
-
...integer2(details?.exitCode) !== void 0 ? { exitCode: integer2(details?.exitCode) } : {},
|
|
3011
|
-
...typeof details?.timedOut === "boolean" ? { timedOut: details.timedOut } : {},
|
|
3012
|
-
...typeof details?.outputLimitExceeded === "boolean" ? { outputLimitExceeded: details.outputLimitExceeded } : {},
|
|
3013
|
-
...excerpt(output, true) ? { excerpt: excerpt(output, true) } : {}
|
|
3014
|
-
};
|
|
3015
|
-
}
|
|
3016
|
-
|
|
3017
3137
|
// src/code-runtime-acknowledgement-gate.ts
|
|
3018
3138
|
function codeRuntimeAcknowledgementGate(signal) {
|
|
3019
3139
|
let settle;
|
|
@@ -3320,36 +3440,11 @@ var TheseusRuntimeEngine = class {
|
|
|
3320
3440
|
}
|
|
3321
3441
|
/** Report every brokered effect as it starts and finishes. */
|
|
3322
3442
|
#observed(command, active, broker) {
|
|
3323
|
-
return {
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
await this.#event(
|
|
3329
|
-
command,
|
|
3330
|
-
{
|
|
3331
|
-
type: "tool",
|
|
3332
|
-
phase: "started",
|
|
3333
|
-
tool: request.tool,
|
|
3334
|
-
operationId,
|
|
3335
|
-
...startedPresentation ? { presentation: startedPresentation } : {}
|
|
3336
|
-
},
|
|
3337
|
-
active.conversationRefs
|
|
3338
|
-
).catch(() => void 0);
|
|
3339
|
-
const response2 = await broker.execute(context, request);
|
|
3340
|
-
const completedPresentation = codeToolResultPresentation(request, response2);
|
|
3341
|
-
await this.#event(command, {
|
|
3342
|
-
type: "tool",
|
|
3343
|
-
phase: "completed",
|
|
3344
|
-
tool: request.tool,
|
|
3345
|
-
ok: response2.ok,
|
|
3346
|
-
durationMs: Date.now() - startedAt,
|
|
3347
|
-
operationId,
|
|
3348
|
-
...completedPresentation ? { presentation: completedPresentation } : {}
|
|
3349
|
-
}, active.conversationRefs).catch(() => void 0);
|
|
3350
|
-
return response2;
|
|
3351
|
-
}
|
|
3352
|
-
};
|
|
3443
|
+
return observedBroker({
|
|
3444
|
+
broker,
|
|
3445
|
+
operationIdFor: (requestId) => digestRuntimeValue(`${command.commandId}:${requestId}`),
|
|
3446
|
+
emit: (event) => this.#event(command, event, active.conversationRefs).catch(() => void 0)
|
|
3447
|
+
});
|
|
3353
3448
|
}
|
|
3354
3449
|
async #checkpoint(command) {
|
|
3355
3450
|
const active = this.#active.get(command.sessionId);
|
|
@@ -3383,7 +3478,7 @@ var VERSION = "0.1.0";
|
|
|
3383
3478
|
function usage() {
|
|
3384
3479
|
return `Usage:
|
|
3385
3480
|
ODLA_CODE_HOST_TOKEN=odla_code_host_... odla-code-runtime \\
|
|
3386
|
-
--endpoint https://odla.ai --image registry/odla-
|
|
3481
|
+
--endpoint https://odla.ai --image registry/odla-theseus@sha256:... \\
|
|
3387
3482
|
--build-policy ./odla-code-build.json [--engine auto|container|podman|docker] [--once]
|
|
3388
3483
|
|
|
3389
3484
|
The host token is read only from ODLA_CODE_HOST_TOKEN. The runtime makes
|