@odla-ai/harness 0.9.0 → 0.9.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-CR6RE3A2.js → chunk-5LRYJKUI.js} +3 -3
- package/dist/chunk-FAN2R3GW.js +166 -0
- package/dist/chunk-FAN2R3GW.js.map +1 -0
- package/dist/{chunk-NWNBSX56.js → chunk-OZBJNTML.js} +4 -4
- package/dist/{chunk-NG7AYYH3.js → chunk-Q4NL5XO3.js} +204 -217
- package/dist/chunk-Q4NL5XO3.js.map +1 -0
- package/dist/{chunk-RXNHCGWE.js → chunk-U324RQ4N.js} +1 -1
- package/dist/{chunk-RXNHCGWE.js.map → chunk-U324RQ4N.js.map} +1 -1
- package/dist/{chunk-Q7CKOT7T.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 +311 -164
- package/dist/code-runtime-cli.cjs.map +1 -1
- package/dist/code-runtime-cli.js +12 -6
- 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 +308 -162
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +44 -7
- package/dist/node.d.ts +44 -7
- 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-BNJikP5h.d.ts → types-_8y8vBDI.d.cts} +19 -1
- package/dist/{types-BNJikP5h.d.cts → types-_8y8vBDI.d.ts} +19 -1
- package/package.json +1 -1
- package/dist/chunk-NG7AYYH3.js.map +0 -1
- /package/dist/{chunk-CR6RE3A2.js.map → chunk-5LRYJKUI.js.map} +0 -0
- /package/dist/{chunk-NWNBSX56.js.map → chunk-OZBJNTML.js.map} +0 -0
- /package/dist/{chunk-Q7CKOT7T.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) {
|
|
@@ -351,12 +351,14 @@ async function runCodeRuntimeHeartbeatLoop(options) {
|
|
|
351
351
|
} while (!options.signal?.aborted);
|
|
352
352
|
}
|
|
353
353
|
var CodeRuntimeReconciler = class {
|
|
354
|
-
constructor(control, engine) {
|
|
354
|
+
constructor(control, engine, onDiagnostic) {
|
|
355
355
|
this.control = control;
|
|
356
356
|
this.engine = engine;
|
|
357
|
+
this.onDiagnostic = onDiagnostic;
|
|
357
358
|
}
|
|
358
359
|
control;
|
|
359
360
|
engine;
|
|
361
|
+
onDiagnostic;
|
|
360
362
|
results = /* @__PURE__ */ new Map();
|
|
361
363
|
async reconcile(snapshot) {
|
|
362
364
|
for (const command of snapshot.commands) {
|
|
@@ -374,7 +376,13 @@ var CodeRuntimeReconciler = class {
|
|
|
374
376
|
}
|
|
375
377
|
await this.control.acknowledge(command.commandId, completed.result);
|
|
376
378
|
if (!completed.notified) {
|
|
377
|
-
|
|
379
|
+
try {
|
|
380
|
+
await this.engine.acknowledged?.(command, completed.result);
|
|
381
|
+
} catch (error) {
|
|
382
|
+
this.onDiagnostic?.(
|
|
383
|
+
`command ${command.commandId} acknowledged handling failed \xB7 ${error instanceof Error ? error.message : String(error)}`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
378
386
|
completed.notified = true;
|
|
379
387
|
}
|
|
380
388
|
}
|
|
@@ -409,10 +417,41 @@ var SECRET = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-toke
|
|
|
409
417
|
var PATH = /^[A-Za-z0-9_@+.,-]+(?:\/[A-Za-z0-9_@+.,-]+)*$/;
|
|
410
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;
|
|
411
419
|
function stripPatchEnvelope(patch2) {
|
|
412
|
-
if (!/^\*\*\* (?:Begin|End) Patch\s*$/m.test(patch2)) return patch2;
|
|
420
|
+
if (!/^\*\*\* (?:Begin|End) Patch\s*$/m.test(patch2)) return applyPatchDialectToDiff(patch2);
|
|
413
421
|
const kept = patch2.split("\n").filter((line) => !/^\*\*\* (?:Begin|End) Patch\s*$/.test(line));
|
|
414
422
|
const stripped = kept.join("\n");
|
|
415
|
-
|
|
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
|
+
`;
|
|
416
455
|
}
|
|
417
456
|
function validateCodePatch(rawPatch, maxBytes) {
|
|
418
457
|
const patch2 = stripPatchEnvelope(rawPatch);
|
|
@@ -465,17 +504,20 @@ function resolveCodePath(workspaceDir, path) {
|
|
|
465
504
|
if (target !== root && !target.startsWith(`${root}${import_node_path.sep}`)) throw new TypeError("path escapes the staged workspace");
|
|
466
505
|
return target;
|
|
467
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
|
+
}
|
|
468
511
|
function describePatchFailure(patch2, detail) {
|
|
469
512
|
const hunks = patch2.split("\n").filter((line) => line.startsWith("@@"));
|
|
470
|
-
const
|
|
471
|
-
const contextless = bodies.some((body) => !body.split("\n").some((line) => line.startsWith(" ") && line.trim().length > 0));
|
|
472
|
-
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." : "";
|
|
473
514
|
return `patch did not apply: ${detail}${hint}`;
|
|
474
515
|
}
|
|
475
516
|
async function applyCodePatch(workspaceDir, rawPatch, paths2) {
|
|
476
517
|
const patch2 = stripPatchEnvelope(rawPatch);
|
|
477
|
-
|
|
478
|
-
await gitApply(workspaceDir, patch2,
|
|
518
|
+
const zero = hasContextFreeHunk(patch2);
|
|
519
|
+
await gitApply(workspaceDir, patch2, true, zero);
|
|
520
|
+
await gitApply(workspaceDir, patch2, false, zero);
|
|
479
521
|
for (const path of paths2) {
|
|
480
522
|
try {
|
|
481
523
|
const info = await (0, import_promises.lstat)(resolveCodePath(workspaceDir, path));
|
|
@@ -487,9 +529,16 @@ async function applyCodePatch(workspaceDir, rawPatch, paths2) {
|
|
|
487
529
|
}
|
|
488
530
|
}
|
|
489
531
|
}
|
|
490
|
-
function gitApply(cwd, patch2, check) {
|
|
532
|
+
function gitApply(cwd, patch2, check, unidiffZero = false) {
|
|
491
533
|
return new Promise((accept, reject) => {
|
|
492
|
-
const args = [
|
|
534
|
+
const args = [
|
|
535
|
+
"apply",
|
|
536
|
+
"--recount",
|
|
537
|
+
...unidiffZero ? ["--unidiff-zero"] : [],
|
|
538
|
+
"--whitespace=nowarn",
|
|
539
|
+
...check ? ["--check"] : [],
|
|
540
|
+
"-"
|
|
541
|
+
];
|
|
493
542
|
const child = (0, import_node_child_process.spawn)("git", args, {
|
|
494
543
|
cwd,
|
|
495
544
|
shell: false,
|
|
@@ -1202,9 +1251,9 @@ async function prepareRuntimeCheckpoint(input) {
|
|
|
1202
1251
|
if (evidence.receipt.outcome === "passed") {
|
|
1203
1252
|
verification = evidence.receipt;
|
|
1204
1253
|
review = await input.review(patch2, verification);
|
|
1205
|
-
note = review
|
|
1254
|
+
note = describeReview(review);
|
|
1206
1255
|
} else {
|
|
1207
|
-
note =
|
|
1256
|
+
note = describeGateFailure(evidence);
|
|
1208
1257
|
}
|
|
1209
1258
|
} catch (cause) {
|
|
1210
1259
|
note = `Candidate verification or review failed closed: ${message(cause)}`;
|
|
@@ -1229,6 +1278,23 @@ async function prepareRuntimeCheckpoint(input) {
|
|
|
1229
1278
|
});
|
|
1230
1279
|
return { checkpoint, verification, review, note };
|
|
1231
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
|
+
}
|
|
1232
1298
|
var message = (value) => (value instanceof Error ? value.message : String(value)).slice(0, 500);
|
|
1233
1299
|
|
|
1234
1300
|
// src/code-runtime-checkpoint-manager.ts
|
|
@@ -1873,6 +1939,166 @@ function createCodeRuntimeInference(options) {
|
|
|
1873
1939
|
};
|
|
1874
1940
|
}
|
|
1875
1941
|
|
|
1942
|
+
// src/code-tool-presentation.ts
|
|
1943
|
+
var text = (value, maximum) => {
|
|
1944
|
+
if (typeof value !== "string") return void 0;
|
|
1945
|
+
const bounded = value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
|
|
1946
|
+
return bounded ? bounded.slice(0, maximum) : void 0;
|
|
1947
|
+
};
|
|
1948
|
+
var integer2 = (value) => Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : void 0;
|
|
1949
|
+
var record3 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1950
|
+
var excerpt = (value, tail = false) => {
|
|
1951
|
+
if (typeof value !== "string") return void 0;
|
|
1952
|
+
const safe = value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ");
|
|
1953
|
+
const source = (tail ? safe.slice(-1e4) : safe.slice(0, 1e4)).trim();
|
|
1954
|
+
if (!source) return void 0;
|
|
1955
|
+
const lines = source.split("\n").filter((line) => line.trim()).map((line) => line.slice(0, 240));
|
|
1956
|
+
const selected = tail ? lines.slice(-10) : lines.slice(0, 10);
|
|
1957
|
+
return text(selected.join("\n"), 2400);
|
|
1958
|
+
};
|
|
1959
|
+
var paths = (value) => {
|
|
1960
|
+
if (!Array.isArray(value)) return void 0;
|
|
1961
|
+
const items = value.flatMap((item) => {
|
|
1962
|
+
const path = text(item, 1024);
|
|
1963
|
+
return path ? [path] : [];
|
|
1964
|
+
}).slice(0, 12);
|
|
1965
|
+
return items.length ? items : void 0;
|
|
1966
|
+
};
|
|
1967
|
+
function patchStats(value) {
|
|
1968
|
+
if (typeof value !== "string") return {};
|
|
1969
|
+
let additions = 0;
|
|
1970
|
+
let deletions = 0;
|
|
1971
|
+
for (const line of value.slice(0, 262144).split("\n")) {
|
|
1972
|
+
if (line.startsWith("+++") || line.startsWith("---")) continue;
|
|
1973
|
+
if (line.startsWith("+")) additions += 1;
|
|
1974
|
+
else if (line.startsWith("-")) deletions += 1;
|
|
1975
|
+
}
|
|
1976
|
+
return { ...additions ? { additions } : {}, ...deletions ? { deletions } : {} };
|
|
1977
|
+
}
|
|
1978
|
+
function searchResults(value) {
|
|
1979
|
+
if (typeof value !== "string") return void 0;
|
|
1980
|
+
const results = value.split("\n").flatMap((line) => {
|
|
1981
|
+
const match = /^([^:\n]{1,1024}):(\d+):\s?(.*)$/.exec(line);
|
|
1982
|
+
if (!match) return [];
|
|
1983
|
+
const lineNumber = Number(match[2]);
|
|
1984
|
+
const itemText = text(match[3], 240);
|
|
1985
|
+
if (!Number.isSafeInteger(lineNumber) || lineNumber < 1) return [];
|
|
1986
|
+
return [{ path: match[1], line: lineNumber, ...itemText ? { text: itemText } : {} }];
|
|
1987
|
+
}).slice(0, 5);
|
|
1988
|
+
return results.length ? results : void 0;
|
|
1989
|
+
}
|
|
1990
|
+
function codeToolRequestPresentation(request) {
|
|
1991
|
+
const input = request.input;
|
|
1992
|
+
if (request.tool === "sandbox.read") {
|
|
1993
|
+
const path = text(input.path, 1024);
|
|
1994
|
+
if (!path) return void 0;
|
|
1995
|
+
const startLine = integer2(input.startLine);
|
|
1996
|
+
const endLine = integer2(input.endLine);
|
|
1997
|
+
return { kind: "read", path, ...startLine ? { startLine } : {}, ...endLine ? { endLine } : {} };
|
|
1998
|
+
}
|
|
1999
|
+
if (request.tool === "sandbox.list") {
|
|
2000
|
+
const scope = text(input.prefix, 1024);
|
|
2001
|
+
return { kind: "list", ...scope ? { scope } : {} };
|
|
2002
|
+
}
|
|
2003
|
+
if (request.tool === "sandbox.search" || request.tool === "sandbox.overview" || request.tool === "sandbox.where_is" || request.tool === "sandbox.who_imports" || request.tool === "sandbox.who_touches") {
|
|
2004
|
+
const query = text(input.query, 512);
|
|
2005
|
+
const scope = request.tool === "sandbox.search" ? text(input.prefix, 1024) : void 0;
|
|
2006
|
+
if (request.tool === "sandbox.search" && !query) return void 0;
|
|
2007
|
+
return { kind: "query", ...query ? { query } : {}, ...scope ? { scope } : {} };
|
|
2008
|
+
}
|
|
2009
|
+
if (request.tool === "sandbox.apply_patch") {
|
|
2010
|
+
return { kind: "patch", ...patchStats(input.patch) };
|
|
2011
|
+
}
|
|
2012
|
+
const recipeId = text(input.recipeId, 120);
|
|
2013
|
+
return recipeId ? { kind: "recipe", recipeId } : void 0;
|
|
2014
|
+
}
|
|
2015
|
+
function codeToolResultPresentation(request, response2) {
|
|
2016
|
+
const started = codeToolRequestPresentation(request);
|
|
2017
|
+
if (!started || !response2.ok) return started;
|
|
2018
|
+
const details = record3(response2.details);
|
|
2019
|
+
if (started.kind === "read") {
|
|
2020
|
+
return {
|
|
2021
|
+
...started,
|
|
2022
|
+
...integer2(details?.startLine) ? { startLine: integer2(details?.startLine) } : {},
|
|
2023
|
+
...integer2(details?.endLine) ? { endLine: integer2(details?.endLine) } : {},
|
|
2024
|
+
...excerpt(response2.content) ? { excerpt: excerpt(response2.content) } : {}
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
if (started.kind === "list") {
|
|
2028
|
+
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);
|
|
2029
|
+
return {
|
|
2030
|
+
...started,
|
|
2031
|
+
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
2032
|
+
...listed.length ? { paths: listed } : {}
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
if (started.kind === "query") {
|
|
2036
|
+
const results = request.tool === "sandbox.search" ? searchResults(response2.content) : void 0;
|
|
2037
|
+
const resultExcerpt = request.tool === "sandbox.search" ? void 0 : excerpt(response2.content);
|
|
2038
|
+
return {
|
|
2039
|
+
...started,
|
|
2040
|
+
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
2041
|
+
...results ? { results } : {},
|
|
2042
|
+
...resultExcerpt ? { excerpt: resultExcerpt } : {}
|
|
2043
|
+
};
|
|
2044
|
+
}
|
|
2045
|
+
if (started.kind === "patch") {
|
|
2046
|
+
return { ...started, ...paths(details?.paths) ? { paths: paths(details?.paths) } : {} };
|
|
2047
|
+
}
|
|
2048
|
+
const output = response2.content.replace(/^Recipe [^\n]*\.?\s*/u, "");
|
|
2049
|
+
return {
|
|
2050
|
+
...started,
|
|
2051
|
+
...integer2(details?.exitCode) !== void 0 ? { exitCode: integer2(details?.exitCode) } : {},
|
|
2052
|
+
...typeof details?.timedOut === "boolean" ? { timedOut: details.timedOut } : {},
|
|
2053
|
+
...typeof details?.outputLimitExceeded === "boolean" ? { outputLimitExceeded: details.outputLimitExceeded } : {},
|
|
2054
|
+
...excerpt(output, true) ? { excerpt: excerpt(output, true) } : {}
|
|
2055
|
+
};
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
// src/code-runtime-observer.ts
|
|
2059
|
+
var MAX_FAILURE_REASON = 240;
|
|
2060
|
+
function toolFailureReason(response2) {
|
|
2061
|
+
if (response2.ok) return void 0;
|
|
2062
|
+
const supplied = response2.details?.failureReason;
|
|
2063
|
+
const reason = typeof supplied === "string" && supplied || DEFAULT_FAILURE_REASON[response2.content] || response2.content || "tool request failed; inspect the tool input and workspace state";
|
|
2064
|
+
return reason.slice(0, MAX_FAILURE_REASON);
|
|
2065
|
+
}
|
|
2066
|
+
var DEFAULT_FAILURE_REASON = {
|
|
2067
|
+
"tool denied by CaMeL policy": "tool denied by CaMeL policy",
|
|
2068
|
+
"review sessions are read-only": "review session is read-only; workspace changes are not permitted"
|
|
2069
|
+
};
|
|
2070
|
+
function observedBroker(input) {
|
|
2071
|
+
const now = input.now ?? Date.now;
|
|
2072
|
+
return {
|
|
2073
|
+
execute: async (context, request) => {
|
|
2074
|
+
const startedAt = now();
|
|
2075
|
+
const operationId = input.operationIdFor(request.requestId);
|
|
2076
|
+
const startedPresentation = codeToolRequestPresentation(request);
|
|
2077
|
+
await input.emit({
|
|
2078
|
+
type: "tool",
|
|
2079
|
+
phase: "started",
|
|
2080
|
+
tool: request.tool,
|
|
2081
|
+
operationId,
|
|
2082
|
+
...startedPresentation ? { presentation: startedPresentation } : {}
|
|
2083
|
+
});
|
|
2084
|
+
const response2 = await input.broker.execute(context, request);
|
|
2085
|
+
const completedPresentation = codeToolResultPresentation(request, response2);
|
|
2086
|
+
const failureReason = toolFailureReason(response2);
|
|
2087
|
+
await input.emit({
|
|
2088
|
+
type: "tool",
|
|
2089
|
+
phase: "completed",
|
|
2090
|
+
tool: request.tool,
|
|
2091
|
+
ok: response2.ok,
|
|
2092
|
+
durationMs: now() - startedAt,
|
|
2093
|
+
operationId,
|
|
2094
|
+
...failureReason ? { failureReason } : {},
|
|
2095
|
+
...completedPresentation ? { presentation: completedPresentation } : {}
|
|
2096
|
+
});
|
|
2097
|
+
return response2;
|
|
2098
|
+
}
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
|
|
1876
2102
|
// src/code-tool-policy.ts
|
|
1877
2103
|
var import_camel = require("@odla-ai/camel");
|
|
1878
2104
|
var import_policy = require("@odla-ai/camel/policy");
|
|
@@ -2111,8 +2337,14 @@ function optionalInteger(value) {
|
|
|
2111
2337
|
if (!Number.isSafeInteger(value) || value < 1) throw new TypeError("line bounds must be positive integers");
|
|
2112
2338
|
return value;
|
|
2113
2339
|
}
|
|
2340
|
+
var DEFAULT_FAILURE_REASON2 = {
|
|
2341
|
+
"tool denied by CaMeL policy": "tool denied by CaMeL policy",
|
|
2342
|
+
"review sessions are read-only": "review session is read-only; workspace changes are not permitted"
|
|
2343
|
+
};
|
|
2114
2344
|
function response(request, ok, content, details) {
|
|
2115
|
-
return { requestId: request.requestId, ok, content, ...details ? { details } : {} };
|
|
2345
|
+
if (ok) return { requestId: request.requestId, ok, content, ...details ? { details } : {} };
|
|
2346
|
+
const failureReason = typeof details?.failureReason === "string" && details.failureReason || DEFAULT_FAILURE_REASON2[content] || content || "tool request failed; inspect the tool input and workspace state";
|
|
2347
|
+
return { requestId: request.requestId, ok, content, details: { ...details, failureReason } };
|
|
2116
2348
|
}
|
|
2117
2349
|
|
|
2118
2350
|
// src/code-tool-reads.ts
|
|
@@ -2890,122 +3122,6 @@ async function appendCodeRuntimeEvent(control, command, event, refs) {
|
|
|
2890
3122
|
var digestRuntimeValue = (value) => `sha256:${(0, import_node_crypto4.createHash)("sha256").update(value).digest("hex")}`;
|
|
2891
3123
|
var runtimeErrorMessage = (value) => value instanceof Error ? value.message : String(value);
|
|
2892
3124
|
|
|
2893
|
-
// src/code-tool-presentation.ts
|
|
2894
|
-
var text = (value, maximum) => {
|
|
2895
|
-
if (typeof value !== "string") return void 0;
|
|
2896
|
-
const bounded = value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
|
|
2897
|
-
return bounded ? bounded.slice(0, maximum) : void 0;
|
|
2898
|
-
};
|
|
2899
|
-
var integer2 = (value) => Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : void 0;
|
|
2900
|
-
var record3 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
2901
|
-
var excerpt = (value, tail = false) => {
|
|
2902
|
-
if (typeof value !== "string") return void 0;
|
|
2903
|
-
const safe = value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ");
|
|
2904
|
-
const source = (tail ? safe.slice(-1e4) : safe.slice(0, 1e4)).trim();
|
|
2905
|
-
if (!source) return void 0;
|
|
2906
|
-
const lines = source.split("\n").filter((line) => line.trim()).map((line) => line.slice(0, 240));
|
|
2907
|
-
const selected = tail ? lines.slice(-10) : lines.slice(0, 10);
|
|
2908
|
-
return text(selected.join("\n"), 2400);
|
|
2909
|
-
};
|
|
2910
|
-
var paths = (value) => {
|
|
2911
|
-
if (!Array.isArray(value)) return void 0;
|
|
2912
|
-
const items = value.flatMap((item) => {
|
|
2913
|
-
const path = text(item, 1024);
|
|
2914
|
-
return path ? [path] : [];
|
|
2915
|
-
}).slice(0, 12);
|
|
2916
|
-
return items.length ? items : void 0;
|
|
2917
|
-
};
|
|
2918
|
-
function patchStats(value) {
|
|
2919
|
-
if (typeof value !== "string") return {};
|
|
2920
|
-
let additions = 0;
|
|
2921
|
-
let deletions = 0;
|
|
2922
|
-
for (const line of value.slice(0, 262144).split("\n")) {
|
|
2923
|
-
if (line.startsWith("+++") || line.startsWith("---")) continue;
|
|
2924
|
-
if (line.startsWith("+")) additions += 1;
|
|
2925
|
-
else if (line.startsWith("-")) deletions += 1;
|
|
2926
|
-
}
|
|
2927
|
-
return { ...additions ? { additions } : {}, ...deletions ? { deletions } : {} };
|
|
2928
|
-
}
|
|
2929
|
-
function searchResults(value) {
|
|
2930
|
-
if (typeof value !== "string") return void 0;
|
|
2931
|
-
const results = value.split("\n").flatMap((line) => {
|
|
2932
|
-
const match = /^([^:\n]{1,1024}):(\d+):\s?(.*)$/.exec(line);
|
|
2933
|
-
if (!match) return [];
|
|
2934
|
-
const lineNumber = Number(match[2]);
|
|
2935
|
-
const itemText = text(match[3], 240);
|
|
2936
|
-
if (!Number.isSafeInteger(lineNumber) || lineNumber < 1) return [];
|
|
2937
|
-
return [{ path: match[1], line: lineNumber, ...itemText ? { text: itemText } : {} }];
|
|
2938
|
-
}).slice(0, 5);
|
|
2939
|
-
return results.length ? results : void 0;
|
|
2940
|
-
}
|
|
2941
|
-
function codeToolRequestPresentation(request) {
|
|
2942
|
-
const input = request.input;
|
|
2943
|
-
if (request.tool === "sandbox.read") {
|
|
2944
|
-
const path = text(input.path, 1024);
|
|
2945
|
-
if (!path) return void 0;
|
|
2946
|
-
const startLine = integer2(input.startLine);
|
|
2947
|
-
const endLine = integer2(input.endLine);
|
|
2948
|
-
return { kind: "read", path, ...startLine ? { startLine } : {}, ...endLine ? { endLine } : {} };
|
|
2949
|
-
}
|
|
2950
|
-
if (request.tool === "sandbox.list") {
|
|
2951
|
-
const scope = text(input.prefix, 1024);
|
|
2952
|
-
return { kind: "list", ...scope ? { scope } : {} };
|
|
2953
|
-
}
|
|
2954
|
-
if (request.tool === "sandbox.search" || request.tool === "sandbox.overview" || request.tool === "sandbox.where_is" || request.tool === "sandbox.who_imports" || request.tool === "sandbox.who_touches") {
|
|
2955
|
-
const query = text(input.query, 512);
|
|
2956
|
-
const scope = request.tool === "sandbox.search" ? text(input.prefix, 1024) : void 0;
|
|
2957
|
-
if (request.tool === "sandbox.search" && !query) return void 0;
|
|
2958
|
-
return { kind: "query", ...query ? { query } : {}, ...scope ? { scope } : {} };
|
|
2959
|
-
}
|
|
2960
|
-
if (request.tool === "sandbox.apply_patch") {
|
|
2961
|
-
return { kind: "patch", ...patchStats(input.patch) };
|
|
2962
|
-
}
|
|
2963
|
-
const recipeId = text(input.recipeId, 120);
|
|
2964
|
-
return recipeId ? { kind: "recipe", recipeId } : void 0;
|
|
2965
|
-
}
|
|
2966
|
-
function codeToolResultPresentation(request, response2) {
|
|
2967
|
-
const started = codeToolRequestPresentation(request);
|
|
2968
|
-
if (!started || !response2.ok) return started;
|
|
2969
|
-
const details = record3(response2.details);
|
|
2970
|
-
if (started.kind === "read") {
|
|
2971
|
-
return {
|
|
2972
|
-
...started,
|
|
2973
|
-
...integer2(details?.startLine) ? { startLine: integer2(details?.startLine) } : {},
|
|
2974
|
-
...integer2(details?.endLine) ? { endLine: integer2(details?.endLine) } : {},
|
|
2975
|
-
...excerpt(response2.content) ? { excerpt: excerpt(response2.content) } : {}
|
|
2976
|
-
};
|
|
2977
|
-
}
|
|
2978
|
-
if (started.kind === "list") {
|
|
2979
|
-
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);
|
|
2980
|
-
return {
|
|
2981
|
-
...started,
|
|
2982
|
-
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
2983
|
-
...listed.length ? { paths: listed } : {}
|
|
2984
|
-
};
|
|
2985
|
-
}
|
|
2986
|
-
if (started.kind === "query") {
|
|
2987
|
-
const results = request.tool === "sandbox.search" ? searchResults(response2.content) : void 0;
|
|
2988
|
-
const resultExcerpt = request.tool === "sandbox.search" ? void 0 : excerpt(response2.content);
|
|
2989
|
-
return {
|
|
2990
|
-
...started,
|
|
2991
|
-
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
2992
|
-
...results ? { results } : {},
|
|
2993
|
-
...resultExcerpt ? { excerpt: resultExcerpt } : {}
|
|
2994
|
-
};
|
|
2995
|
-
}
|
|
2996
|
-
if (started.kind === "patch") {
|
|
2997
|
-
return { ...started, ...paths(details?.paths) ? { paths: paths(details?.paths) } : {} };
|
|
2998
|
-
}
|
|
2999
|
-
const output = response2.content.replace(/^Recipe [^\n]*\.?\s*/u, "");
|
|
3000
|
-
return {
|
|
3001
|
-
...started,
|
|
3002
|
-
...integer2(details?.exitCode) !== void 0 ? { exitCode: integer2(details?.exitCode) } : {},
|
|
3003
|
-
...typeof details?.timedOut === "boolean" ? { timedOut: details.timedOut } : {},
|
|
3004
|
-
...typeof details?.outputLimitExceeded === "boolean" ? { outputLimitExceeded: details.outputLimitExceeded } : {},
|
|
3005
|
-
...excerpt(output, true) ? { excerpt: excerpt(output, true) } : {}
|
|
3006
|
-
};
|
|
3007
|
-
}
|
|
3008
|
-
|
|
3009
3125
|
// src/code-runtime-acknowledgement-gate.ts
|
|
3010
3126
|
function codeRuntimeAcknowledgementGate(signal) {
|
|
3011
3127
|
let settle;
|
|
@@ -3025,6 +3141,57 @@ function codeRuntimeAcknowledgementGate(signal) {
|
|
|
3025
3141
|
return { ready, release };
|
|
3026
3142
|
}
|
|
3027
3143
|
|
|
3144
|
+
// src/code-runtime-session-activity.ts
|
|
3145
|
+
function observeCodeRuntimeSessionSkills(command, skills, emit) {
|
|
3146
|
+
return skills.map((skill) => ({
|
|
3147
|
+
...skill,
|
|
3148
|
+
tools: skill.tools.map((tool) => {
|
|
3149
|
+
if (!tool.handler) return tool;
|
|
3150
|
+
const handler = tool.handler;
|
|
3151
|
+
return {
|
|
3152
|
+
...tool,
|
|
3153
|
+
handler: async (input, context) => {
|
|
3154
|
+
const startedAt = Date.now();
|
|
3155
|
+
const operationId = digestRuntimeValue(
|
|
3156
|
+
`${command.commandId}:${skill.name}:${tool.name}:${context.toolCallId ?? "missing"}`
|
|
3157
|
+
);
|
|
3158
|
+
await emit({
|
|
3159
|
+
type: "collaboration",
|
|
3160
|
+
phase: "started",
|
|
3161
|
+
skill: skill.name,
|
|
3162
|
+
tool: tool.name,
|
|
3163
|
+
operationId
|
|
3164
|
+
}).catch(() => void 0);
|
|
3165
|
+
try {
|
|
3166
|
+
const output = await handler(input, context);
|
|
3167
|
+
await emit({
|
|
3168
|
+
type: "collaboration",
|
|
3169
|
+
phase: "completed",
|
|
3170
|
+
skill: skill.name,
|
|
3171
|
+
tool: tool.name,
|
|
3172
|
+
operationId,
|
|
3173
|
+
ok: output.isError !== true,
|
|
3174
|
+
durationMs: Date.now() - startedAt
|
|
3175
|
+
}).catch(() => void 0);
|
|
3176
|
+
return output;
|
|
3177
|
+
} catch (cause) {
|
|
3178
|
+
await emit({
|
|
3179
|
+
type: "collaboration",
|
|
3180
|
+
phase: "completed",
|
|
3181
|
+
skill: skill.name,
|
|
3182
|
+
tool: tool.name,
|
|
3183
|
+
operationId,
|
|
3184
|
+
ok: false,
|
|
3185
|
+
durationMs: Date.now() - startedAt
|
|
3186
|
+
}).catch(() => void 0);
|
|
3187
|
+
throw cause;
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
};
|
|
3191
|
+
})
|
|
3192
|
+
}));
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3028
3195
|
// src/code-runtime-engine.ts
|
|
3029
3196
|
var TheseusRuntimeEngine = class {
|
|
3030
3197
|
constructor(options) {
|
|
@@ -3222,7 +3389,7 @@ var TheseusRuntimeEngine = class {
|
|
|
3222
3389
|
event: (event) => this.#event(command, event, active.conversationRefs)
|
|
3223
3390
|
});
|
|
3224
3391
|
await this.#event(command, { type: "status", status: "running" }, active.conversationRefs);
|
|
3225
|
-
const extraSkills = await sessionSkillsFor(this.options, command);
|
|
3392
|
+
const extraSkills = observeCodeRuntimeSessionSkills(command, await sessionSkillsFor(this.options, command), (event) => this.#event(command, event, active.conversationRefs));
|
|
3226
3393
|
const result = await this.#attempt({
|
|
3227
3394
|
inference,
|
|
3228
3395
|
broker,
|
|
@@ -3261,36 +3428,11 @@ var TheseusRuntimeEngine = class {
|
|
|
3261
3428
|
}
|
|
3262
3429
|
/** Report every brokered effect as it starts and finishes. */
|
|
3263
3430
|
#observed(command, active, broker) {
|
|
3264
|
-
return {
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
await this.#event(
|
|
3270
|
-
command,
|
|
3271
|
-
{
|
|
3272
|
-
type: "tool",
|
|
3273
|
-
phase: "started",
|
|
3274
|
-
tool: request.tool,
|
|
3275
|
-
operationId,
|
|
3276
|
-
...startedPresentation ? { presentation: startedPresentation } : {}
|
|
3277
|
-
},
|
|
3278
|
-
active.conversationRefs
|
|
3279
|
-
).catch(() => void 0);
|
|
3280
|
-
const response2 = await broker.execute(context, request);
|
|
3281
|
-
const completedPresentation = codeToolResultPresentation(request, response2);
|
|
3282
|
-
await this.#event(command, {
|
|
3283
|
-
type: "tool",
|
|
3284
|
-
phase: "completed",
|
|
3285
|
-
tool: request.tool,
|
|
3286
|
-
ok: response2.ok,
|
|
3287
|
-
durationMs: Date.now() - startedAt,
|
|
3288
|
-
operationId,
|
|
3289
|
-
...completedPresentation ? { presentation: completedPresentation } : {}
|
|
3290
|
-
}, active.conversationRefs).catch(() => void 0);
|
|
3291
|
-
return response2;
|
|
3292
|
-
}
|
|
3293
|
-
};
|
|
3431
|
+
return observedBroker({
|
|
3432
|
+
broker,
|
|
3433
|
+
operationIdFor: (requestId) => digestRuntimeValue(`${command.commandId}:${requestId}`),
|
|
3434
|
+
emit: (event) => this.#event(command, event, active.conversationRefs).catch(() => void 0)
|
|
3435
|
+
});
|
|
3294
3436
|
}
|
|
3295
3437
|
async #checkpoint(command) {
|
|
3296
3438
|
const active = this.#active.get(command.sessionId);
|
|
@@ -3324,7 +3466,7 @@ var VERSION = "0.1.0";
|
|
|
3324
3466
|
function usage() {
|
|
3325
3467
|
return `Usage:
|
|
3326
3468
|
ODLA_CODE_HOST_TOKEN=odla_code_host_... odla-code-runtime \\
|
|
3327
|
-
--endpoint https://odla.ai --image registry/odla-
|
|
3469
|
+
--endpoint https://odla.ai --image registry/odla-theseus@sha256:... \\
|
|
3328
3470
|
--build-policy ./odla-code-build.json [--engine auto|container|podman|docker] [--once]
|
|
3329
3471
|
|
|
3330
3472
|
The host token is read only from ODLA_CODE_HOST_TOKEN. The runtime makes
|
|
@@ -3398,7 +3540,12 @@ async function main() {
|
|
|
3398
3540
|
onDiagnostic: (message2) => process.stderr.write(`[odla-code-runtime] agent failed \xB7 ${message2}
|
|
3399
3541
|
`)
|
|
3400
3542
|
});
|
|
3401
|
-
const reconciler = new CodeRuntimeReconciler(
|
|
3543
|
+
const reconciler = new CodeRuntimeReconciler(
|
|
3544
|
+
control,
|
|
3545
|
+
commandEngine,
|
|
3546
|
+
(message2) => process.stderr.write(`[odla-code-runtime] ${message2}
|
|
3547
|
+
`)
|
|
3548
|
+
);
|
|
3402
3549
|
try {
|
|
3403
3550
|
await runCodeRuntimeHeartbeatLoop({
|
|
3404
3551
|
control,
|