@odla-ai/harness 0.7.1 → 0.8.1
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-K76I2TCQ.js → chunk-CR6RE3A2.js} +3 -3
- package/dist/{chunk-GYWQM76X.js → chunk-IWVGSWY6.js} +202 -59
- package/dist/chunk-IWVGSWY6.js.map +1 -0
- package/dist/{chunk-XWXRNPGY.js → chunk-NWNBSX56.js} +4 -4
- package/dist/{chunk-FVOMJKWK.js → chunk-Q7CKOT7T.js} +2 -2
- package/dist/{chunk-LNQNFGQC.js → chunk-RXNHCGWE.js} +1 -1
- package/dist/chunk-RXNHCGWE.js.map +1 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +4 -4
- package/dist/code-runtime-cli.cjs +202 -59
- package/dist/code-runtime-cli.cjs.map +1 -1
- package/dist/code-runtime-cli.js +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/node.cjs +211 -68
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +15 -1
- package/dist/node.d.ts +15 -1
- package/dist/node.js +5 -5
- 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-CK5EKmKm.d.cts → types-BNJikP5h.d.cts} +53 -5
- package/dist/{types-CK5EKmKm.d.ts → types-BNJikP5h.d.ts} +53 -5
- package/package.json +1 -1
- package/dist/chunk-GYWQM76X.js.map +0 -1
- package/dist/chunk-LNQNFGQC.js.map +0 -1
- /package/dist/{chunk-K76I2TCQ.js.map → chunk-CR6RE3A2.js.map} +0 -0
- /package/dist/{chunk-XWXRNPGY.js.map → chunk-NWNBSX56.js.map} +0 -0
- /package/dist/{chunk-FVOMJKWK.js.map → chunk-Q7CKOT7T.js.map} +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
encodeAgentInput,
|
|
3
3
|
parseAgentOutput
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Q7CKOT7T.js";
|
|
5
5
|
import {
|
|
6
6
|
HARNESS_PROTOCOL_VERSION
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-RXNHCGWE.js";
|
|
8
8
|
|
|
9
9
|
// src/container.ts
|
|
10
10
|
import { execFile, spawn } from "child_process";
|
|
@@ -548,4 +548,4 @@ export {
|
|
|
548
548
|
stageWorkspacePair,
|
|
549
549
|
safeWorkspaceLabel
|
|
550
550
|
};
|
|
551
|
-
//# sourceMappingURL=chunk-
|
|
551
|
+
//# sourceMappingURL=chunk-CR6RE3A2.js.map
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
stageWorkspace,
|
|
5
5
|
stageWorkspacePair,
|
|
6
6
|
verifyContainerEngineBoundary
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-CR6RE3A2.js";
|
|
8
8
|
import {
|
|
9
9
|
HARNESS_PROTOCOL_VERSION
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-RXNHCGWE.js";
|
|
11
11
|
|
|
12
12
|
// src/workspace-digest.ts
|
|
13
13
|
import { createHash } from "crypto";
|
|
@@ -351,7 +351,7 @@ function validateCodePatch(rawPatch, maxBytes) {
|
|
|
351
351
|
if (FORBIDDEN.test(patch2) || /(?:old|new)(?: file)? mode 120000/.test(patch2)) {
|
|
352
352
|
throw new TypeError("patch uses a forbidden binary, link, mode, rename, or copy operation");
|
|
353
353
|
}
|
|
354
|
-
const
|
|
354
|
+
const paths2 = [];
|
|
355
355
|
const lines = patch2.split("\n");
|
|
356
356
|
for (let index = 0; index < lines.length; index += 1) {
|
|
357
357
|
const line = lines[index];
|
|
@@ -367,10 +367,10 @@ function validateCodePatch(rawPatch, maxBytes) {
|
|
|
367
367
|
if (!validHeaderPath(oldPath, path, "a") || !validHeaderPath(newPath, path, "b")) {
|
|
368
368
|
throw new TypeError("patch file headers do not match the declared path");
|
|
369
369
|
}
|
|
370
|
-
|
|
370
|
+
paths2.push(path);
|
|
371
371
|
}
|
|
372
|
-
if (!
|
|
373
|
-
return
|
|
372
|
+
if (!paths2.length || new Set(paths2).size !== paths2.length) throw new TypeError("patch has no diffs or repeats a path");
|
|
373
|
+
return paths2;
|
|
374
374
|
}
|
|
375
375
|
function validHeaderPath(value, path, prefix) {
|
|
376
376
|
return value === "/dev/null" || value === `${prefix}/${path}`;
|
|
@@ -395,11 +395,11 @@ function describePatchFailure(patch2, detail) {
|
|
|
395
395
|
const hint = hunks.length > 0 && contextless ? " A hunk has no context lines; include at least one unchanged line above or below each change." : "";
|
|
396
396
|
return `patch did not apply: ${detail}${hint}`;
|
|
397
397
|
}
|
|
398
|
-
async function applyCodePatch(workspaceDir, rawPatch,
|
|
398
|
+
async function applyCodePatch(workspaceDir, rawPatch, paths2) {
|
|
399
399
|
const patch2 = stripPatchEnvelope(rawPatch);
|
|
400
400
|
await gitApply(workspaceDir, patch2, true);
|
|
401
401
|
await gitApply(workspaceDir, patch2, false);
|
|
402
|
-
for (const path of
|
|
402
|
+
for (const path of paths2) {
|
|
403
403
|
try {
|
|
404
404
|
const info = await lstat(resolveCodePath(workspaceDir, path));
|
|
405
405
|
if (info.isSymbolicLink() || !info.isFile() && !info.isDirectory()) {
|
|
@@ -421,8 +421,8 @@ function gitApply(cwd, patch2, check) {
|
|
|
421
421
|
});
|
|
422
422
|
let stderr = "";
|
|
423
423
|
child.stderr.setEncoding("utf8");
|
|
424
|
-
child.stderr.on("data", (
|
|
425
|
-
if (stderr.length < 4e3) stderr +=
|
|
424
|
+
child.stderr.on("data", (text2) => {
|
|
425
|
+
if (stderr.length < 4e3) stderr += text2.slice(0, 4e3);
|
|
426
426
|
});
|
|
427
427
|
child.once("error", reject);
|
|
428
428
|
child.once("exit", (code) => code === 0 ? accept() : reject(new TypeError(describePatchFailure(patch2, stderr.trim().slice(0, 500)))));
|
|
@@ -452,8 +452,8 @@ async function restoreCodeWorkspaceCheckpoint(input) {
|
|
|
452
452
|
const workspace = await stageWorkspace(input.trustedBaseDir, input.stage);
|
|
453
453
|
try {
|
|
454
454
|
if (checkpoint.patch) {
|
|
455
|
-
const
|
|
456
|
-
await applyCodePatch(workspace.workspaceDir, checkpoint.patch,
|
|
455
|
+
const paths2 = validateCodePatch(checkpoint.patch, 256 * 1024);
|
|
456
|
+
await applyCodePatch(workspace.workspaceDir, checkpoint.patch, paths2);
|
|
457
457
|
}
|
|
458
458
|
return { workspace, checkpoint };
|
|
459
459
|
} catch (error) {
|
|
@@ -622,8 +622,8 @@ async function verifyCodeCandidate(input) {
|
|
|
622
622
|
try {
|
|
623
623
|
const baseDigest = await digestStagedWorkspace(staged.workspaceDir, limits);
|
|
624
624
|
if (baseDigest !== input.trustedBaseDigest) throw new TypeError("trusted base does not match its registered digest");
|
|
625
|
-
const
|
|
626
|
-
await applyCodePatch(staged.workspaceDir, input.candidatePatch,
|
|
625
|
+
const paths2 = validateCodePatch(input.candidatePatch, policy.maximumPatchBytes);
|
|
626
|
+
await applyCodePatch(staged.workspaceDir, input.candidatePatch, paths2);
|
|
627
627
|
const sourceDigest = await digestStagedWorkspace(staged.workspaceDir, limits);
|
|
628
628
|
const policyDigest = digestPolicy(policy);
|
|
629
629
|
const patchDigest = digestBytes(input.candidatePatch);
|
|
@@ -632,7 +632,7 @@ async function verifyCodeCandidate(input) {
|
|
|
632
632
|
trustedBaseDigest: input.trustedBaseDigest,
|
|
633
633
|
patchDigest
|
|
634
634
|
});
|
|
635
|
-
const changedTests = changedTestPaths(
|
|
635
|
+
const changedTests = changedTestPaths(paths2, policy);
|
|
636
636
|
if (changedTests.length > policy.maximumChangedTests) throw new TypeError("candidate changes too many test files");
|
|
637
637
|
const recipes = [];
|
|
638
638
|
const logs = [];
|
|
@@ -699,8 +699,8 @@ function validate(input) {
|
|
|
699
699
|
}
|
|
700
700
|
return result;
|
|
701
701
|
}
|
|
702
|
-
function changedTestPaths(
|
|
703
|
-
return
|
|
702
|
+
function changedTestPaths(paths2, policy) {
|
|
703
|
+
return paths2.filter((path) => policy.testPathSuffixes.some((suffix) => path.endsWith(suffix)) || policy.testPathPrefixes.some((prefix) => path.startsWith(prefix) || path.includes(`/${prefix}`))).sort();
|
|
704
704
|
}
|
|
705
705
|
function recipeReceipt(recipe2, result, artifacts) {
|
|
706
706
|
const status = result.timedOut ? "timed_out" : result.outputLimitExceeded ? "output_limited" : result.exitCode === 0 && artifacts.every((item) => item.status === "verified") ? "passed" : "failed";
|
|
@@ -1358,6 +1358,7 @@ async function runCodeAgentAttempt(options) {
|
|
|
1358
1358
|
model: "brokered",
|
|
1359
1359
|
surface,
|
|
1360
1360
|
...options.recipeIds ? { recipeIds: options.recipeIds } : {},
|
|
1361
|
+
...options.extraSkills ? { extraSkills: options.extraSkills } : {},
|
|
1361
1362
|
...options.maxSteps === void 0 ? {} : { maxSteps: options.maxSteps },
|
|
1362
1363
|
...options.budget ? { budget: options.budget } : {},
|
|
1363
1364
|
...options.signal ? { signal: options.signal } : {},
|
|
@@ -1464,7 +1465,7 @@ function createWorkspaceFileRegistry(limit = DEFAULT_MAX_FILES, enumerate = regi
|
|
|
1464
1465
|
files(root) {
|
|
1465
1466
|
const existing = cache2.get(root);
|
|
1466
1467
|
if (existing) return existing;
|
|
1467
|
-
const pending = enumerate(root, limit).then((
|
|
1468
|
+
const pending = enumerate(root, limit).then((paths2) => Object.freeze(paths2));
|
|
1468
1469
|
cache2.set(root, pending);
|
|
1469
1470
|
void pending.catch(() => {
|
|
1470
1471
|
if (cache2.get(root) === pending) cache2.delete(root);
|
|
@@ -1477,7 +1478,7 @@ function createWorkspaceFileRegistry(limit = DEFAULT_MAX_FILES, enumerate = regi
|
|
|
1477
1478
|
};
|
|
1478
1479
|
}
|
|
1479
1480
|
async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
1480
|
-
const
|
|
1481
|
+
const paths2 = [];
|
|
1481
1482
|
const walk = async (directory) => {
|
|
1482
1483
|
for (const entry of await readdir2(directory, { withFileTypes: true })) {
|
|
1483
1484
|
if (SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
|
|
@@ -1491,26 +1492,26 @@ async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
|
1491
1492
|
} catch {
|
|
1492
1493
|
continue;
|
|
1493
1494
|
}
|
|
1494
|
-
|
|
1495
|
-
if (
|
|
1495
|
+
paths2.push(path);
|
|
1496
|
+
if (paths2.length > limit) throw new TypeError("workspace file registry exceeds its bound");
|
|
1496
1497
|
}
|
|
1497
1498
|
}
|
|
1498
1499
|
};
|
|
1499
1500
|
await walk(resolve4(root));
|
|
1500
|
-
return
|
|
1501
|
+
return paths2.sort();
|
|
1501
1502
|
}
|
|
1502
|
-
function listWorkspace(
|
|
1503
|
+
function listWorkspace(paths2, options = {}) {
|
|
1503
1504
|
const max = options.maxEntries ?? 1e3;
|
|
1504
1505
|
const prefix = options.prefix?.replace(/\/+$/, "");
|
|
1505
|
-
const scoped = prefix ?
|
|
1506
|
+
const scoped = prefix ? paths2.filter((path) => path === prefix || path.startsWith(`${prefix}/`)) : [...paths2];
|
|
1506
1507
|
return scoped.slice(0, max);
|
|
1507
1508
|
}
|
|
1508
|
-
async function searchWorkspace(root,
|
|
1509
|
+
async function searchWorkspace(root, paths2, options) {
|
|
1509
1510
|
options.signal?.throwIfAborted();
|
|
1510
1511
|
if (!options.query) throw new TypeError("search query must be a non-empty string");
|
|
1511
1512
|
const maxResults = options.maxResults ?? DEFAULT_MAX_RESULTS;
|
|
1512
1513
|
const maxFileBytes = options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
|
|
1513
|
-
const scoped = listWorkspace(
|
|
1514
|
+
const scoped = listWorkspace(paths2, { ...options.prefix ? { prefix: options.prefix } : {}, maxEntries: paths2.length });
|
|
1514
1515
|
if (scoped.length === 0) return [];
|
|
1515
1516
|
try {
|
|
1516
1517
|
return await nativeSearch(root, scoped, { ...options, maxResults, maxFileBytes });
|
|
@@ -1520,11 +1521,11 @@ async function searchWorkspace(root, paths, options) {
|
|
|
1520
1521
|
}
|
|
1521
1522
|
}
|
|
1522
1523
|
var MAX_NATIVE_ARG_BYTES = 96 * 1024;
|
|
1523
|
-
async function nativeSearch(root,
|
|
1524
|
+
async function nativeSearch(root, paths2, options) {
|
|
1524
1525
|
const batches = [];
|
|
1525
1526
|
let batch = [];
|
|
1526
1527
|
let bytes = 0;
|
|
1527
|
-
for (const path of
|
|
1528
|
+
for (const path of paths2) {
|
|
1528
1529
|
const size = Buffer.byteLength(path) + 1;
|
|
1529
1530
|
if (batch.length > 0 && bytes + size > MAX_NATIVE_ARG_BYTES) {
|
|
1530
1531
|
batches.push(batch);
|
|
@@ -1543,7 +1544,7 @@ async function nativeSearch(root, paths, options) {
|
|
|
1543
1544
|
}
|
|
1544
1545
|
return matches;
|
|
1545
1546
|
}
|
|
1546
|
-
function nativeSearchBatch(root,
|
|
1547
|
+
function nativeSearchBatch(root, paths2, options, remaining) {
|
|
1547
1548
|
return new Promise((resolveMatches, reject) => {
|
|
1548
1549
|
const args = [
|
|
1549
1550
|
"--fixed-strings",
|
|
@@ -1556,7 +1557,7 @@ function nativeSearchBatch(root, paths, options, remaining) {
|
|
|
1556
1557
|
options.caseSensitive === false ? "--ignore-case" : "--case-sensitive",
|
|
1557
1558
|
"--",
|
|
1558
1559
|
options.query,
|
|
1559
|
-
...
|
|
1560
|
+
...paths2
|
|
1560
1561
|
];
|
|
1561
1562
|
const child = spawn3("rg", args, {
|
|
1562
1563
|
cwd: root,
|
|
@@ -1754,16 +1755,16 @@ function createCodePolicyGate(options) {
|
|
|
1754
1755
|
}
|
|
1755
1756
|
};
|
|
1756
1757
|
}
|
|
1757
|
-
function directoryPrefixes(
|
|
1758
|
+
function directoryPrefixes(paths2) {
|
|
1758
1759
|
const prefixes = /* @__PURE__ */ new Set(["."]);
|
|
1759
|
-
for (const path of
|
|
1760
|
+
for (const path of paths2) {
|
|
1760
1761
|
const parts = path.split("/");
|
|
1761
1762
|
for (let index = 1; index < parts.length; index += 1) prefixes.add(parts.slice(0, index).join("/"));
|
|
1762
1763
|
}
|
|
1763
1764
|
return [...prefixes].sort();
|
|
1764
1765
|
}
|
|
1765
|
-
async function safePrefix(base,
|
|
1766
|
-
const prefixes = directoryPrefixes(
|
|
1766
|
+
async function safePrefix(base, paths2, prefix) {
|
|
1767
|
+
const prefixes = directoryPrefixes(paths2);
|
|
1767
1768
|
const conversions = await conversionRegistry(
|
|
1768
1769
|
[await registeredPolicy("code.prefix.v1", "code.prefixes.v1", prefixes)],
|
|
1769
1770
|
{ "code.prefixes.v1": prefixes }
|
|
@@ -1893,7 +1894,7 @@ import {
|
|
|
1893
1894
|
} from "@odla-ai/graph";
|
|
1894
1895
|
import { buildCodeGraph, FILE, IMPORTS, PACKAGE, READS, SYMBOL, WRITES } from "@odla-ai/graph/code";
|
|
1895
1896
|
var cache = /* @__PURE__ */ new Map();
|
|
1896
|
-
function workspaceGraphs(workspaceDir,
|
|
1897
|
+
function workspaceGraphs(workspaceDir, paths2) {
|
|
1897
1898
|
const existing = cache.get(workspaceDir);
|
|
1898
1899
|
if (existing) return existing;
|
|
1899
1900
|
const read2 = (path) => readFile3(join3(workspaceDir, path), "utf8");
|
|
@@ -1901,7 +1902,7 @@ function workspaceGraphs(workspaceDir, paths) {
|
|
|
1901
1902
|
// No knownTables: a staged workspace may not carry migrations, and a filter
|
|
1902
1903
|
// that silently drops every table is worse than an unfiltered one. Callers
|
|
1903
1904
|
// with ground truth should build the graph themselves.
|
|
1904
|
-
graph: await buildCodeGraph({ paths, read: read2, data: { ignore: (path) => path.includes(".generated.") } })
|
|
1905
|
+
graph: await buildCodeGraph({ paths: paths2, read: read2, data: { ignore: (path) => path.includes(".generated.") } })
|
|
1905
1906
|
}))();
|
|
1906
1907
|
cache.set(workspaceDir, built);
|
|
1907
1908
|
return built;
|
|
@@ -1963,11 +1964,11 @@ async function read(context, request, options, policy, registry) {
|
|
|
1963
1964
|
if (endLine < startLine || endLine - startLine + 1 > (options.maxReadLines ?? 2e3)) {
|
|
1964
1965
|
throw new TypeError("requested line range exceeds its bound");
|
|
1965
1966
|
}
|
|
1966
|
-
const
|
|
1967
|
-
if (!
|
|
1967
|
+
const paths2 = await registry.files(context.workspaceDir);
|
|
1968
|
+
if (!paths2.includes(path)) {
|
|
1968
1969
|
throw new TypeError(`no such file in the staged workspace: "${path}". Use sandbox.overview, sandbox.where_is or sandbox.search to find the correct path.`);
|
|
1969
1970
|
}
|
|
1970
|
-
const allowed = await policy.read(policyContext(context, request, options, { paths, path, startLine, endLine }));
|
|
1971
|
+
const allowed = await policy.read(policyContext(context, request, options, { paths: paths2, path, startLine, endLine }));
|
|
1971
1972
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
1972
1973
|
const target = resolveCodePath(context.workspaceDir, path);
|
|
1973
1974
|
const info = await stat(target);
|
|
@@ -1989,16 +1990,16 @@ async function list(context, request, options, policy, registry) {
|
|
|
1989
1990
|
const prefix = typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
1990
1991
|
const maxEntries = optionalInteger(request.input.maxEntries) ?? 1e3;
|
|
1991
1992
|
if (maxEntries > 5e3) throw new TypeError("maxEntries exceeds its bound");
|
|
1992
|
-
const
|
|
1993
|
-
const allowed = await policy.list(policyContext(context, request, options, { paths, ...prefix ? { prefix } : {} }));
|
|
1993
|
+
const paths2 = await registry.files(context.workspaceDir);
|
|
1994
|
+
const allowed = await policy.list(policyContext(context, request, options, { paths: paths2, ...prefix ? { prefix } : {} }));
|
|
1994
1995
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
1995
|
-
const entries = listWorkspace(
|
|
1996
|
+
const entries = listWorkspace(paths2, { ...prefix ? { prefix } : {}, maxEntries });
|
|
1996
1997
|
if (!entries.length) {
|
|
1997
1998
|
return response(request, true, prefix ? `No files under "${prefix}".` : "Workspace is empty.", { count: 0 });
|
|
1998
1999
|
}
|
|
1999
|
-
const truncated = entries.length <
|
|
2000
|
-
const hint = !prefix &&
|
|
2001
|
-
\u2026 ${
|
|
2000
|
+
const truncated = entries.length < paths2.length && entries.length === maxEntries;
|
|
2001
|
+
const hint = !prefix && paths2.length > 500 ? `
|
|
2002
|
+
\u2026 ${paths2.length} files total. sandbox.overview is far cheaper for orientation; use a prefix here once you know the area.` : "";
|
|
2002
2003
|
return response(
|
|
2003
2004
|
request,
|
|
2004
2005
|
true,
|
|
@@ -2016,10 +2017,10 @@ async function search(context, request, options, policy, registry) {
|
|
|
2016
2017
|
const maxResults = optionalInteger(request.input.maxResults) ?? 100;
|
|
2017
2018
|
if (maxResults > 500) throw new TypeError("maxResults exceeds its bound");
|
|
2018
2019
|
const caseSensitive = request.input.caseSensitive === void 0 ? true : request.input.caseSensitive === true;
|
|
2019
|
-
const
|
|
2020
|
-
const allowed = await policy.search(policyContext(context, request, options, { paths, query, ...prefix ? { prefix } : {} }));
|
|
2020
|
+
const paths2 = await registry.files(context.workspaceDir);
|
|
2021
|
+
const allowed = await policy.search(policyContext(context, request, options, { paths: paths2, query, ...prefix ? { prefix } : {} }));
|
|
2021
2022
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
2022
|
-
const matches = await searchWorkspace(context.workspaceDir,
|
|
2023
|
+
const matches = await searchWorkspace(context.workspaceDir, paths2, {
|
|
2023
2024
|
query,
|
|
2024
2025
|
maxResults,
|
|
2025
2026
|
caseSensitive,
|
|
@@ -2041,8 +2042,8 @@ async function graphQuery(context, request, options, policy, registry) {
|
|
|
2041
2042
|
selector: query
|
|
2042
2043
|
}));
|
|
2043
2044
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
2044
|
-
const
|
|
2045
|
-
const graphs = await workspaceGraphs(context.workspaceDir,
|
|
2045
|
+
const paths2 = await registry.files(context.workspaceDir);
|
|
2046
|
+
const graphs = await workspaceGraphs(context.workspaceDir, paths2);
|
|
2046
2047
|
if (request.tool === "sandbox.overview") {
|
|
2047
2048
|
return response(request, true, renderOverview(graphs, query || void 0));
|
|
2048
2049
|
}
|
|
@@ -2106,16 +2107,16 @@ function toolFailureMessage(reason) {
|
|
|
2106
2107
|
async function patch(context, request, options, policy, registry) {
|
|
2107
2108
|
exactKeys(request.input, ["patch"]);
|
|
2108
2109
|
const value = stringField(request.input, "patch");
|
|
2109
|
-
const
|
|
2110
|
-
if (
|
|
2110
|
+
const paths2 = validateCodePatch(value, options.maxPatchBytes ?? 256 * 1024);
|
|
2111
|
+
if (paths2.some((path) => options.readOnlyPrefixes?.some((prefix) => path === prefix || path.startsWith(`${prefix}/`)))) {
|
|
2111
2112
|
throw new TypeError("patch targets a read-only reference source");
|
|
2112
2113
|
}
|
|
2113
2114
|
const allowed = await policy.patch(policyContext(context, request, options, { patch: value }));
|
|
2114
2115
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
2115
|
-
await applyCodePatch(context.workspaceDir, value,
|
|
2116
|
+
await applyCodePatch(context.workspaceDir, value, paths2);
|
|
2116
2117
|
registry.invalidate(context.workspaceDir);
|
|
2117
2118
|
forgetWorkspaceGraphs(context.workspaceDir);
|
|
2118
|
-
return response(request, true, `Applied patch to ${
|
|
2119
|
+
return response(request, true, `Applied patch to ${paths2.length} file(s).`, { paths: paths2 });
|
|
2119
2120
|
}
|
|
2120
2121
|
async function recipe(context, request, options, recipes, policy) {
|
|
2121
2122
|
exactKeys(request.input, ["recipeId"]);
|
|
@@ -2323,6 +2324,18 @@ function assertBudget(budget) {
|
|
|
2323
2324
|
}
|
|
2324
2325
|
}
|
|
2325
2326
|
|
|
2327
|
+
// src/code-runtime-session-skills.ts
|
|
2328
|
+
async function sessionSkillsFor(options, command) {
|
|
2329
|
+
try {
|
|
2330
|
+
return await options.sessionSkills?.(command) ?? [];
|
|
2331
|
+
} catch (cause) {
|
|
2332
|
+
options.onDiagnostic?.(
|
|
2333
|
+
`session skills unavailable, continuing with code tools only: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
2334
|
+
);
|
|
2335
|
+
return [];
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2326
2339
|
// src/code-runtime-broker.ts
|
|
2327
2340
|
function createCodeRuntimeToolBroker(input, lease, role) {
|
|
2328
2341
|
const broker = createCodeToolBroker({
|
|
@@ -2499,12 +2512,129 @@ import { createHash as createHash3 } from "crypto";
|
|
|
2499
2512
|
async function appendCodeRuntimeEvent(control, command, event, refs) {
|
|
2500
2513
|
const eventId = `${command.commandId.slice(0, 45)}:${refs.length + 1}`;
|
|
2501
2514
|
refs.push(eventId);
|
|
2502
|
-
const
|
|
2515
|
+
const attributed = { ...event, interactionId: command.commandId };
|
|
2516
|
+
const bounded = attributed.type === "message" ? { ...attributed, body: attributed.body.trim().slice(0, 2e4) || `${attributed.actor} event` } : attributed;
|
|
2503
2517
|
await control.appendSessionEvent(command.sessionId, eventId, bounded);
|
|
2504
2518
|
}
|
|
2505
2519
|
var digestRuntimeValue = (value) => `sha256:${createHash3("sha256").update(value).digest("hex")}`;
|
|
2506
2520
|
var runtimeErrorMessage = (value) => value instanceof Error ? value.message : String(value);
|
|
2507
2521
|
|
|
2522
|
+
// src/code-tool-presentation.ts
|
|
2523
|
+
var text = (value, maximum) => {
|
|
2524
|
+
if (typeof value !== "string") return void 0;
|
|
2525
|
+
const bounded = value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
|
|
2526
|
+
return bounded ? bounded.slice(0, maximum) : void 0;
|
|
2527
|
+
};
|
|
2528
|
+
var integer2 = (value) => Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : void 0;
|
|
2529
|
+
var record3 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
2530
|
+
var excerpt = (value, tail = false) => {
|
|
2531
|
+
if (typeof value !== "string") return void 0;
|
|
2532
|
+
const safe = value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ");
|
|
2533
|
+
const source = (tail ? safe.slice(-1e4) : safe.slice(0, 1e4)).trim();
|
|
2534
|
+
if (!source) return void 0;
|
|
2535
|
+
const lines = source.split("\n").filter((line) => line.trim()).map((line) => line.slice(0, 240));
|
|
2536
|
+
const selected = tail ? lines.slice(-10) : lines.slice(0, 10);
|
|
2537
|
+
return text(selected.join("\n"), 2400);
|
|
2538
|
+
};
|
|
2539
|
+
var paths = (value) => {
|
|
2540
|
+
if (!Array.isArray(value)) return void 0;
|
|
2541
|
+
const items = value.flatMap((item) => {
|
|
2542
|
+
const path = text(item, 1024);
|
|
2543
|
+
return path ? [path] : [];
|
|
2544
|
+
}).slice(0, 12);
|
|
2545
|
+
return items.length ? items : void 0;
|
|
2546
|
+
};
|
|
2547
|
+
function patchStats(value) {
|
|
2548
|
+
if (typeof value !== "string") return {};
|
|
2549
|
+
let additions = 0;
|
|
2550
|
+
let deletions = 0;
|
|
2551
|
+
for (const line of value.slice(0, 262144).split("\n")) {
|
|
2552
|
+
if (line.startsWith("+++") || line.startsWith("---")) continue;
|
|
2553
|
+
if (line.startsWith("+")) additions += 1;
|
|
2554
|
+
else if (line.startsWith("-")) deletions += 1;
|
|
2555
|
+
}
|
|
2556
|
+
return { ...additions ? { additions } : {}, ...deletions ? { deletions } : {} };
|
|
2557
|
+
}
|
|
2558
|
+
function searchResults(value) {
|
|
2559
|
+
if (typeof value !== "string") return void 0;
|
|
2560
|
+
const results = value.split("\n").flatMap((line) => {
|
|
2561
|
+
const match = /^([^:\n]{1,1024}):(\d+):\s?(.*)$/.exec(line);
|
|
2562
|
+
if (!match) return [];
|
|
2563
|
+
const lineNumber = Number(match[2]);
|
|
2564
|
+
const itemText = text(match[3], 240);
|
|
2565
|
+
if (!Number.isSafeInteger(lineNumber) || lineNumber < 1) return [];
|
|
2566
|
+
return [{ path: match[1], line: lineNumber, ...itemText ? { text: itemText } : {} }];
|
|
2567
|
+
}).slice(0, 5);
|
|
2568
|
+
return results.length ? results : void 0;
|
|
2569
|
+
}
|
|
2570
|
+
function codeToolRequestPresentation(request) {
|
|
2571
|
+
const input = request.input;
|
|
2572
|
+
if (request.tool === "sandbox.read") {
|
|
2573
|
+
const path = text(input.path, 1024);
|
|
2574
|
+
if (!path) return void 0;
|
|
2575
|
+
const startLine = integer2(input.startLine);
|
|
2576
|
+
const endLine = integer2(input.endLine);
|
|
2577
|
+
return { kind: "read", path, ...startLine ? { startLine } : {}, ...endLine ? { endLine } : {} };
|
|
2578
|
+
}
|
|
2579
|
+
if (request.tool === "sandbox.list") {
|
|
2580
|
+
const scope = text(input.prefix, 1024);
|
|
2581
|
+
return { kind: "list", ...scope ? { scope } : {} };
|
|
2582
|
+
}
|
|
2583
|
+
if (request.tool === "sandbox.search" || request.tool === "sandbox.overview" || request.tool === "sandbox.where_is" || request.tool === "sandbox.who_imports" || request.tool === "sandbox.who_touches") {
|
|
2584
|
+
const query = text(input.query, 512);
|
|
2585
|
+
const scope = request.tool === "sandbox.search" ? text(input.prefix, 1024) : void 0;
|
|
2586
|
+
if (request.tool === "sandbox.search" && !query) return void 0;
|
|
2587
|
+
return { kind: "query", ...query ? { query } : {}, ...scope ? { scope } : {} };
|
|
2588
|
+
}
|
|
2589
|
+
if (request.tool === "sandbox.apply_patch") {
|
|
2590
|
+
return { kind: "patch", ...patchStats(input.patch) };
|
|
2591
|
+
}
|
|
2592
|
+
const recipeId = text(input.recipeId, 120);
|
|
2593
|
+
return recipeId ? { kind: "recipe", recipeId } : void 0;
|
|
2594
|
+
}
|
|
2595
|
+
function codeToolResultPresentation(request, response2) {
|
|
2596
|
+
const started = codeToolRequestPresentation(request);
|
|
2597
|
+
if (!started || !response2.ok) return started;
|
|
2598
|
+
const details = record3(response2.details);
|
|
2599
|
+
if (started.kind === "read") {
|
|
2600
|
+
return {
|
|
2601
|
+
...started,
|
|
2602
|
+
...integer2(details?.startLine) ? { startLine: integer2(details?.startLine) } : {},
|
|
2603
|
+
...integer2(details?.endLine) ? { endLine: integer2(details?.endLine) } : {},
|
|
2604
|
+
...excerpt(response2.content) ? { excerpt: excerpt(response2.content) } : {}
|
|
2605
|
+
};
|
|
2606
|
+
}
|
|
2607
|
+
if (started.kind === "list") {
|
|
2608
|
+
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);
|
|
2609
|
+
return {
|
|
2610
|
+
...started,
|
|
2611
|
+
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
2612
|
+
...listed.length ? { paths: listed } : {}
|
|
2613
|
+
};
|
|
2614
|
+
}
|
|
2615
|
+
if (started.kind === "query") {
|
|
2616
|
+
const results = request.tool === "sandbox.search" ? searchResults(response2.content) : void 0;
|
|
2617
|
+
const resultExcerpt = request.tool === "sandbox.search" ? void 0 : excerpt(response2.content);
|
|
2618
|
+
return {
|
|
2619
|
+
...started,
|
|
2620
|
+
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
2621
|
+
...results ? { results } : {},
|
|
2622
|
+
...resultExcerpt ? { excerpt: resultExcerpt } : {}
|
|
2623
|
+
};
|
|
2624
|
+
}
|
|
2625
|
+
if (started.kind === "patch") {
|
|
2626
|
+
return { ...started, ...paths(details?.paths) ? { paths: paths(details?.paths) } : {} };
|
|
2627
|
+
}
|
|
2628
|
+
const output = response2.content.replace(/^Recipe [^\n]*\.?\s*/u, "");
|
|
2629
|
+
return {
|
|
2630
|
+
...started,
|
|
2631
|
+
...integer2(details?.exitCode) !== void 0 ? { exitCode: integer2(details?.exitCode) } : {},
|
|
2632
|
+
...typeof details?.timedOut === "boolean" ? { timedOut: details.timedOut } : {},
|
|
2633
|
+
...typeof details?.outputLimitExceeded === "boolean" ? { outputLimitExceeded: details.outputLimitExceeded } : {},
|
|
2634
|
+
...excerpt(output, true) ? { excerpt: excerpt(output, true) } : {}
|
|
2635
|
+
};
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2508
2638
|
// src/code-runtime-engine.ts
|
|
2509
2639
|
var TheseusRuntimeEngine = class {
|
|
2510
2640
|
constructor(options) {
|
|
@@ -2699,6 +2829,7 @@ var TheseusRuntimeEngine = class {
|
|
|
2699
2829
|
event: (event) => this.#event(command, event, active.conversationRefs)
|
|
2700
2830
|
});
|
|
2701
2831
|
await this.#event(command, { type: "status", status: "running" }, active.conversationRefs);
|
|
2832
|
+
const extraSkills = await sessionSkillsFor(this.options, command);
|
|
2702
2833
|
const result = await this.#attempt({
|
|
2703
2834
|
inference,
|
|
2704
2835
|
broker,
|
|
@@ -2706,7 +2837,8 @@ var TheseusRuntimeEngine = class {
|
|
|
2706
2837
|
workspaceDir: active.workspace.workspaceDir,
|
|
2707
2838
|
prompt: metadata.prompt,
|
|
2708
2839
|
signal: active.abort.signal,
|
|
2709
|
-
recipeIds: this.options.recipes.map((recipe2) => recipe2.id)
|
|
2840
|
+
recipeIds: this.options.recipes.map((recipe2) => recipe2.id),
|
|
2841
|
+
...extraSkills.length ? { extraSkills } : {}
|
|
2710
2842
|
});
|
|
2711
2843
|
const closing = result.finalText.trim();
|
|
2712
2844
|
const completed = result.status === "completed" && Boolean(closing);
|
|
@@ -2739,18 +2871,29 @@ var TheseusRuntimeEngine = class {
|
|
|
2739
2871
|
return {
|
|
2740
2872
|
execute: async (context, request) => {
|
|
2741
2873
|
const startedAt = Date.now();
|
|
2874
|
+
const operationId = digestRuntimeValue(`${command.commandId}:${request.requestId}`);
|
|
2875
|
+
const startedPresentation = codeToolRequestPresentation(request);
|
|
2742
2876
|
await this.#event(
|
|
2743
2877
|
command,
|
|
2744
|
-
{
|
|
2878
|
+
{
|
|
2879
|
+
type: "tool",
|
|
2880
|
+
phase: "started",
|
|
2881
|
+
tool: request.tool,
|
|
2882
|
+
operationId,
|
|
2883
|
+
...startedPresentation ? { presentation: startedPresentation } : {}
|
|
2884
|
+
},
|
|
2745
2885
|
active.conversationRefs
|
|
2746
2886
|
).catch(() => void 0);
|
|
2747
2887
|
const response2 = await broker.execute(context, request);
|
|
2888
|
+
const completedPresentation = codeToolResultPresentation(request, response2);
|
|
2748
2889
|
await this.#event(command, {
|
|
2749
2890
|
type: "tool",
|
|
2750
2891
|
phase: "completed",
|
|
2751
2892
|
tool: request.tool,
|
|
2752
2893
|
ok: response2.ok,
|
|
2753
|
-
durationMs: Date.now() - startedAt
|
|
2894
|
+
durationMs: Date.now() - startedAt,
|
|
2895
|
+
operationId,
|
|
2896
|
+
...completedPresentation ? { presentation: completedPresentation } : {}
|
|
2754
2897
|
}, active.conversationRefs).catch(() => void 0);
|
|
2755
2898
|
return response2;
|
|
2756
2899
|
}
|
|
@@ -2826,4 +2969,4 @@ export {
|
|
|
2826
2969
|
runGoal,
|
|
2827
2970
|
TheseusRuntimeEngine
|
|
2828
2971
|
};
|
|
2829
|
-
//# sourceMappingURL=chunk-
|
|
2972
|
+
//# sourceMappingURL=chunk-IWVGSWY6.js.map
|