@odla-ai/harness 0.7.1 → 0.8.0
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-ISR434K7.js} +186 -58
- package/dist/chunk-ISR434K7.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 +186 -58
- 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 +195 -67
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.ts +1 -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";
|
|
@@ -1464,7 +1464,7 @@ function createWorkspaceFileRegistry(limit = DEFAULT_MAX_FILES, enumerate = regi
|
|
|
1464
1464
|
files(root) {
|
|
1465
1465
|
const existing = cache2.get(root);
|
|
1466
1466
|
if (existing) return existing;
|
|
1467
|
-
const pending = enumerate(root, limit).then((
|
|
1467
|
+
const pending = enumerate(root, limit).then((paths2) => Object.freeze(paths2));
|
|
1468
1468
|
cache2.set(root, pending);
|
|
1469
1469
|
void pending.catch(() => {
|
|
1470
1470
|
if (cache2.get(root) === pending) cache2.delete(root);
|
|
@@ -1477,7 +1477,7 @@ function createWorkspaceFileRegistry(limit = DEFAULT_MAX_FILES, enumerate = regi
|
|
|
1477
1477
|
};
|
|
1478
1478
|
}
|
|
1479
1479
|
async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
1480
|
-
const
|
|
1480
|
+
const paths2 = [];
|
|
1481
1481
|
const walk = async (directory) => {
|
|
1482
1482
|
for (const entry of await readdir2(directory, { withFileTypes: true })) {
|
|
1483
1483
|
if (SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
|
|
@@ -1491,26 +1491,26 @@ async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
|
1491
1491
|
} catch {
|
|
1492
1492
|
continue;
|
|
1493
1493
|
}
|
|
1494
|
-
|
|
1495
|
-
if (
|
|
1494
|
+
paths2.push(path);
|
|
1495
|
+
if (paths2.length > limit) throw new TypeError("workspace file registry exceeds its bound");
|
|
1496
1496
|
}
|
|
1497
1497
|
}
|
|
1498
1498
|
};
|
|
1499
1499
|
await walk(resolve4(root));
|
|
1500
|
-
return
|
|
1500
|
+
return paths2.sort();
|
|
1501
1501
|
}
|
|
1502
|
-
function listWorkspace(
|
|
1502
|
+
function listWorkspace(paths2, options = {}) {
|
|
1503
1503
|
const max = options.maxEntries ?? 1e3;
|
|
1504
1504
|
const prefix = options.prefix?.replace(/\/+$/, "");
|
|
1505
|
-
const scoped = prefix ?
|
|
1505
|
+
const scoped = prefix ? paths2.filter((path) => path === prefix || path.startsWith(`${prefix}/`)) : [...paths2];
|
|
1506
1506
|
return scoped.slice(0, max);
|
|
1507
1507
|
}
|
|
1508
|
-
async function searchWorkspace(root,
|
|
1508
|
+
async function searchWorkspace(root, paths2, options) {
|
|
1509
1509
|
options.signal?.throwIfAborted();
|
|
1510
1510
|
if (!options.query) throw new TypeError("search query must be a non-empty string");
|
|
1511
1511
|
const maxResults = options.maxResults ?? DEFAULT_MAX_RESULTS;
|
|
1512
1512
|
const maxFileBytes = options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
|
|
1513
|
-
const scoped = listWorkspace(
|
|
1513
|
+
const scoped = listWorkspace(paths2, { ...options.prefix ? { prefix: options.prefix } : {}, maxEntries: paths2.length });
|
|
1514
1514
|
if (scoped.length === 0) return [];
|
|
1515
1515
|
try {
|
|
1516
1516
|
return await nativeSearch(root, scoped, { ...options, maxResults, maxFileBytes });
|
|
@@ -1520,11 +1520,11 @@ async function searchWorkspace(root, paths, options) {
|
|
|
1520
1520
|
}
|
|
1521
1521
|
}
|
|
1522
1522
|
var MAX_NATIVE_ARG_BYTES = 96 * 1024;
|
|
1523
|
-
async function nativeSearch(root,
|
|
1523
|
+
async function nativeSearch(root, paths2, options) {
|
|
1524
1524
|
const batches = [];
|
|
1525
1525
|
let batch = [];
|
|
1526
1526
|
let bytes = 0;
|
|
1527
|
-
for (const path of
|
|
1527
|
+
for (const path of paths2) {
|
|
1528
1528
|
const size = Buffer.byteLength(path) + 1;
|
|
1529
1529
|
if (batch.length > 0 && bytes + size > MAX_NATIVE_ARG_BYTES) {
|
|
1530
1530
|
batches.push(batch);
|
|
@@ -1543,7 +1543,7 @@ async function nativeSearch(root, paths, options) {
|
|
|
1543
1543
|
}
|
|
1544
1544
|
return matches;
|
|
1545
1545
|
}
|
|
1546
|
-
function nativeSearchBatch(root,
|
|
1546
|
+
function nativeSearchBatch(root, paths2, options, remaining) {
|
|
1547
1547
|
return new Promise((resolveMatches, reject) => {
|
|
1548
1548
|
const args = [
|
|
1549
1549
|
"--fixed-strings",
|
|
@@ -1556,7 +1556,7 @@ function nativeSearchBatch(root, paths, options, remaining) {
|
|
|
1556
1556
|
options.caseSensitive === false ? "--ignore-case" : "--case-sensitive",
|
|
1557
1557
|
"--",
|
|
1558
1558
|
options.query,
|
|
1559
|
-
...
|
|
1559
|
+
...paths2
|
|
1560
1560
|
];
|
|
1561
1561
|
const child = spawn3("rg", args, {
|
|
1562
1562
|
cwd: root,
|
|
@@ -1754,16 +1754,16 @@ function createCodePolicyGate(options) {
|
|
|
1754
1754
|
}
|
|
1755
1755
|
};
|
|
1756
1756
|
}
|
|
1757
|
-
function directoryPrefixes(
|
|
1757
|
+
function directoryPrefixes(paths2) {
|
|
1758
1758
|
const prefixes = /* @__PURE__ */ new Set(["."]);
|
|
1759
|
-
for (const path of
|
|
1759
|
+
for (const path of paths2) {
|
|
1760
1760
|
const parts = path.split("/");
|
|
1761
1761
|
for (let index = 1; index < parts.length; index += 1) prefixes.add(parts.slice(0, index).join("/"));
|
|
1762
1762
|
}
|
|
1763
1763
|
return [...prefixes].sort();
|
|
1764
1764
|
}
|
|
1765
|
-
async function safePrefix(base,
|
|
1766
|
-
const prefixes = directoryPrefixes(
|
|
1765
|
+
async function safePrefix(base, paths2, prefix) {
|
|
1766
|
+
const prefixes = directoryPrefixes(paths2);
|
|
1767
1767
|
const conversions = await conversionRegistry(
|
|
1768
1768
|
[await registeredPolicy("code.prefix.v1", "code.prefixes.v1", prefixes)],
|
|
1769
1769
|
{ "code.prefixes.v1": prefixes }
|
|
@@ -1893,7 +1893,7 @@ import {
|
|
|
1893
1893
|
} from "@odla-ai/graph";
|
|
1894
1894
|
import { buildCodeGraph, FILE, IMPORTS, PACKAGE, READS, SYMBOL, WRITES } from "@odla-ai/graph/code";
|
|
1895
1895
|
var cache = /* @__PURE__ */ new Map();
|
|
1896
|
-
function workspaceGraphs(workspaceDir,
|
|
1896
|
+
function workspaceGraphs(workspaceDir, paths2) {
|
|
1897
1897
|
const existing = cache.get(workspaceDir);
|
|
1898
1898
|
if (existing) return existing;
|
|
1899
1899
|
const read2 = (path) => readFile3(join3(workspaceDir, path), "utf8");
|
|
@@ -1901,7 +1901,7 @@ function workspaceGraphs(workspaceDir, paths) {
|
|
|
1901
1901
|
// No knownTables: a staged workspace may not carry migrations, and a filter
|
|
1902
1902
|
// that silently drops every table is worse than an unfiltered one. Callers
|
|
1903
1903
|
// with ground truth should build the graph themselves.
|
|
1904
|
-
graph: await buildCodeGraph({ paths, read: read2, data: { ignore: (path) => path.includes(".generated.") } })
|
|
1904
|
+
graph: await buildCodeGraph({ paths: paths2, read: read2, data: { ignore: (path) => path.includes(".generated.") } })
|
|
1905
1905
|
}))();
|
|
1906
1906
|
cache.set(workspaceDir, built);
|
|
1907
1907
|
return built;
|
|
@@ -1963,11 +1963,11 @@ async function read(context, request, options, policy, registry) {
|
|
|
1963
1963
|
if (endLine < startLine || endLine - startLine + 1 > (options.maxReadLines ?? 2e3)) {
|
|
1964
1964
|
throw new TypeError("requested line range exceeds its bound");
|
|
1965
1965
|
}
|
|
1966
|
-
const
|
|
1967
|
-
if (!
|
|
1966
|
+
const paths2 = await registry.files(context.workspaceDir);
|
|
1967
|
+
if (!paths2.includes(path)) {
|
|
1968
1968
|
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
1969
|
}
|
|
1970
|
-
const allowed = await policy.read(policyContext(context, request, options, { paths, path, startLine, endLine }));
|
|
1970
|
+
const allowed = await policy.read(policyContext(context, request, options, { paths: paths2, path, startLine, endLine }));
|
|
1971
1971
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
1972
1972
|
const target = resolveCodePath(context.workspaceDir, path);
|
|
1973
1973
|
const info = await stat(target);
|
|
@@ -1989,16 +1989,16 @@ async function list(context, request, options, policy, registry) {
|
|
|
1989
1989
|
const prefix = typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
1990
1990
|
const maxEntries = optionalInteger(request.input.maxEntries) ?? 1e3;
|
|
1991
1991
|
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 } : {} }));
|
|
1992
|
+
const paths2 = await registry.files(context.workspaceDir);
|
|
1993
|
+
const allowed = await policy.list(policyContext(context, request, options, { paths: paths2, ...prefix ? { prefix } : {} }));
|
|
1994
1994
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
1995
|
-
const entries = listWorkspace(
|
|
1995
|
+
const entries = listWorkspace(paths2, { ...prefix ? { prefix } : {}, maxEntries });
|
|
1996
1996
|
if (!entries.length) {
|
|
1997
1997
|
return response(request, true, prefix ? `No files under "${prefix}".` : "Workspace is empty.", { count: 0 });
|
|
1998
1998
|
}
|
|
1999
|
-
const truncated = entries.length <
|
|
2000
|
-
const hint = !prefix &&
|
|
2001
|
-
\u2026 ${
|
|
1999
|
+
const truncated = entries.length < paths2.length && entries.length === maxEntries;
|
|
2000
|
+
const hint = !prefix && paths2.length > 500 ? `
|
|
2001
|
+
\u2026 ${paths2.length} files total. sandbox.overview is far cheaper for orientation; use a prefix here once you know the area.` : "";
|
|
2002
2002
|
return response(
|
|
2003
2003
|
request,
|
|
2004
2004
|
true,
|
|
@@ -2016,10 +2016,10 @@ async function search(context, request, options, policy, registry) {
|
|
|
2016
2016
|
const maxResults = optionalInteger(request.input.maxResults) ?? 100;
|
|
2017
2017
|
if (maxResults > 500) throw new TypeError("maxResults exceeds its bound");
|
|
2018
2018
|
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 } : {} }));
|
|
2019
|
+
const paths2 = await registry.files(context.workspaceDir);
|
|
2020
|
+
const allowed = await policy.search(policyContext(context, request, options, { paths: paths2, query, ...prefix ? { prefix } : {} }));
|
|
2021
2021
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
2022
|
-
const matches = await searchWorkspace(context.workspaceDir,
|
|
2022
|
+
const matches = await searchWorkspace(context.workspaceDir, paths2, {
|
|
2023
2023
|
query,
|
|
2024
2024
|
maxResults,
|
|
2025
2025
|
caseSensitive,
|
|
@@ -2041,8 +2041,8 @@ async function graphQuery(context, request, options, policy, registry) {
|
|
|
2041
2041
|
selector: query
|
|
2042
2042
|
}));
|
|
2043
2043
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
2044
|
-
const
|
|
2045
|
-
const graphs = await workspaceGraphs(context.workspaceDir,
|
|
2044
|
+
const paths2 = await registry.files(context.workspaceDir);
|
|
2045
|
+
const graphs = await workspaceGraphs(context.workspaceDir, paths2);
|
|
2046
2046
|
if (request.tool === "sandbox.overview") {
|
|
2047
2047
|
return response(request, true, renderOverview(graphs, query || void 0));
|
|
2048
2048
|
}
|
|
@@ -2106,16 +2106,16 @@ function toolFailureMessage(reason) {
|
|
|
2106
2106
|
async function patch(context, request, options, policy, registry) {
|
|
2107
2107
|
exactKeys(request.input, ["patch"]);
|
|
2108
2108
|
const value = stringField(request.input, "patch");
|
|
2109
|
-
const
|
|
2110
|
-
if (
|
|
2109
|
+
const paths2 = validateCodePatch(value, options.maxPatchBytes ?? 256 * 1024);
|
|
2110
|
+
if (paths2.some((path) => options.readOnlyPrefixes?.some((prefix) => path === prefix || path.startsWith(`${prefix}/`)))) {
|
|
2111
2111
|
throw new TypeError("patch targets a read-only reference source");
|
|
2112
2112
|
}
|
|
2113
2113
|
const allowed = await policy.patch(policyContext(context, request, options, { patch: value }));
|
|
2114
2114
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
2115
|
-
await applyCodePatch(context.workspaceDir, value,
|
|
2115
|
+
await applyCodePatch(context.workspaceDir, value, paths2);
|
|
2116
2116
|
registry.invalidate(context.workspaceDir);
|
|
2117
2117
|
forgetWorkspaceGraphs(context.workspaceDir);
|
|
2118
|
-
return response(request, true, `Applied patch to ${
|
|
2118
|
+
return response(request, true, `Applied patch to ${paths2.length} file(s).`, { paths: paths2 });
|
|
2119
2119
|
}
|
|
2120
2120
|
async function recipe(context, request, options, recipes, policy) {
|
|
2121
2121
|
exactKeys(request.input, ["recipeId"]);
|
|
@@ -2499,12 +2499,129 @@ import { createHash as createHash3 } from "crypto";
|
|
|
2499
2499
|
async function appendCodeRuntimeEvent(control, command, event, refs) {
|
|
2500
2500
|
const eventId = `${command.commandId.slice(0, 45)}:${refs.length + 1}`;
|
|
2501
2501
|
refs.push(eventId);
|
|
2502
|
-
const
|
|
2502
|
+
const attributed = { ...event, interactionId: command.commandId };
|
|
2503
|
+
const bounded = attributed.type === "message" ? { ...attributed, body: attributed.body.trim().slice(0, 2e4) || `${attributed.actor} event` } : attributed;
|
|
2503
2504
|
await control.appendSessionEvent(command.sessionId, eventId, bounded);
|
|
2504
2505
|
}
|
|
2505
2506
|
var digestRuntimeValue = (value) => `sha256:${createHash3("sha256").update(value).digest("hex")}`;
|
|
2506
2507
|
var runtimeErrorMessage = (value) => value instanceof Error ? value.message : String(value);
|
|
2507
2508
|
|
|
2509
|
+
// src/code-tool-presentation.ts
|
|
2510
|
+
var text = (value, maximum) => {
|
|
2511
|
+
if (typeof value !== "string") return void 0;
|
|
2512
|
+
const bounded = value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
|
|
2513
|
+
return bounded ? bounded.slice(0, maximum) : void 0;
|
|
2514
|
+
};
|
|
2515
|
+
var integer2 = (value) => Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : void 0;
|
|
2516
|
+
var record3 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
2517
|
+
var excerpt = (value, tail = false) => {
|
|
2518
|
+
if (typeof value !== "string") return void 0;
|
|
2519
|
+
const safe = value.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ");
|
|
2520
|
+
const source = (tail ? safe.slice(-1e4) : safe.slice(0, 1e4)).trim();
|
|
2521
|
+
if (!source) return void 0;
|
|
2522
|
+
const lines = source.split("\n").filter((line) => line.trim()).map((line) => line.slice(0, 240));
|
|
2523
|
+
const selected = tail ? lines.slice(-10) : lines.slice(0, 10);
|
|
2524
|
+
return text(selected.join("\n"), 2400);
|
|
2525
|
+
};
|
|
2526
|
+
var paths = (value) => {
|
|
2527
|
+
if (!Array.isArray(value)) return void 0;
|
|
2528
|
+
const items = value.flatMap((item) => {
|
|
2529
|
+
const path = text(item, 1024);
|
|
2530
|
+
return path ? [path] : [];
|
|
2531
|
+
}).slice(0, 12);
|
|
2532
|
+
return items.length ? items : void 0;
|
|
2533
|
+
};
|
|
2534
|
+
function patchStats(value) {
|
|
2535
|
+
if (typeof value !== "string") return {};
|
|
2536
|
+
let additions = 0;
|
|
2537
|
+
let deletions = 0;
|
|
2538
|
+
for (const line of value.slice(0, 262144).split("\n")) {
|
|
2539
|
+
if (line.startsWith("+++") || line.startsWith("---")) continue;
|
|
2540
|
+
if (line.startsWith("+")) additions += 1;
|
|
2541
|
+
else if (line.startsWith("-")) deletions += 1;
|
|
2542
|
+
}
|
|
2543
|
+
return { ...additions ? { additions } : {}, ...deletions ? { deletions } : {} };
|
|
2544
|
+
}
|
|
2545
|
+
function searchResults(value) {
|
|
2546
|
+
if (typeof value !== "string") return void 0;
|
|
2547
|
+
const results = value.split("\n").flatMap((line) => {
|
|
2548
|
+
const match = /^([^:\n]{1,1024}):(\d+):\s?(.*)$/.exec(line);
|
|
2549
|
+
if (!match) return [];
|
|
2550
|
+
const lineNumber = Number(match[2]);
|
|
2551
|
+
const itemText = text(match[3], 240);
|
|
2552
|
+
if (!Number.isSafeInteger(lineNumber) || lineNumber < 1) return [];
|
|
2553
|
+
return [{ path: match[1], line: lineNumber, ...itemText ? { text: itemText } : {} }];
|
|
2554
|
+
}).slice(0, 5);
|
|
2555
|
+
return results.length ? results : void 0;
|
|
2556
|
+
}
|
|
2557
|
+
function codeToolRequestPresentation(request) {
|
|
2558
|
+
const input = request.input;
|
|
2559
|
+
if (request.tool === "sandbox.read") {
|
|
2560
|
+
const path = text(input.path, 1024);
|
|
2561
|
+
if (!path) return void 0;
|
|
2562
|
+
const startLine = integer2(input.startLine);
|
|
2563
|
+
const endLine = integer2(input.endLine);
|
|
2564
|
+
return { kind: "read", path, ...startLine ? { startLine } : {}, ...endLine ? { endLine } : {} };
|
|
2565
|
+
}
|
|
2566
|
+
if (request.tool === "sandbox.list") {
|
|
2567
|
+
const scope = text(input.prefix, 1024);
|
|
2568
|
+
return { kind: "list", ...scope ? { scope } : {} };
|
|
2569
|
+
}
|
|
2570
|
+
if (request.tool === "sandbox.search" || request.tool === "sandbox.overview" || request.tool === "sandbox.where_is" || request.tool === "sandbox.who_imports" || request.tool === "sandbox.who_touches") {
|
|
2571
|
+
const query = text(input.query, 512);
|
|
2572
|
+
const scope = request.tool === "sandbox.search" ? text(input.prefix, 1024) : void 0;
|
|
2573
|
+
if (request.tool === "sandbox.search" && !query) return void 0;
|
|
2574
|
+
return { kind: "query", ...query ? { query } : {}, ...scope ? { scope } : {} };
|
|
2575
|
+
}
|
|
2576
|
+
if (request.tool === "sandbox.apply_patch") {
|
|
2577
|
+
return { kind: "patch", ...patchStats(input.patch) };
|
|
2578
|
+
}
|
|
2579
|
+
const recipeId = text(input.recipeId, 120);
|
|
2580
|
+
return recipeId ? { kind: "recipe", recipeId } : void 0;
|
|
2581
|
+
}
|
|
2582
|
+
function codeToolResultPresentation(request, response2) {
|
|
2583
|
+
const started = codeToolRequestPresentation(request);
|
|
2584
|
+
if (!started || !response2.ok) return started;
|
|
2585
|
+
const details = record3(response2.details);
|
|
2586
|
+
if (started.kind === "read") {
|
|
2587
|
+
return {
|
|
2588
|
+
...started,
|
|
2589
|
+
...integer2(details?.startLine) ? { startLine: integer2(details?.startLine) } : {},
|
|
2590
|
+
...integer2(details?.endLine) ? { endLine: integer2(details?.endLine) } : {},
|
|
2591
|
+
...excerpt(response2.content) ? { excerpt: excerpt(response2.content) } : {}
|
|
2592
|
+
};
|
|
2593
|
+
}
|
|
2594
|
+
if (started.kind === "list") {
|
|
2595
|
+
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);
|
|
2596
|
+
return {
|
|
2597
|
+
...started,
|
|
2598
|
+
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
2599
|
+
...listed.length ? { paths: listed } : {}
|
|
2600
|
+
};
|
|
2601
|
+
}
|
|
2602
|
+
if (started.kind === "query") {
|
|
2603
|
+
const results = request.tool === "sandbox.search" ? searchResults(response2.content) : void 0;
|
|
2604
|
+
const resultExcerpt = request.tool === "sandbox.search" ? void 0 : excerpt(response2.content);
|
|
2605
|
+
return {
|
|
2606
|
+
...started,
|
|
2607
|
+
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
2608
|
+
...results ? { results } : {},
|
|
2609
|
+
...resultExcerpt ? { excerpt: resultExcerpt } : {}
|
|
2610
|
+
};
|
|
2611
|
+
}
|
|
2612
|
+
if (started.kind === "patch") {
|
|
2613
|
+
return { ...started, ...paths(details?.paths) ? { paths: paths(details?.paths) } : {} };
|
|
2614
|
+
}
|
|
2615
|
+
const output = response2.content.replace(/^Recipe [^\n]*\.?\s*/u, "");
|
|
2616
|
+
return {
|
|
2617
|
+
...started,
|
|
2618
|
+
...integer2(details?.exitCode) !== void 0 ? { exitCode: integer2(details?.exitCode) } : {},
|
|
2619
|
+
...typeof details?.timedOut === "boolean" ? { timedOut: details.timedOut } : {},
|
|
2620
|
+
...typeof details?.outputLimitExceeded === "boolean" ? { outputLimitExceeded: details.outputLimitExceeded } : {},
|
|
2621
|
+
...excerpt(output, true) ? { excerpt: excerpt(output, true) } : {}
|
|
2622
|
+
};
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2508
2625
|
// src/code-runtime-engine.ts
|
|
2509
2626
|
var TheseusRuntimeEngine = class {
|
|
2510
2627
|
constructor(options) {
|
|
@@ -2739,18 +2856,29 @@ var TheseusRuntimeEngine = class {
|
|
|
2739
2856
|
return {
|
|
2740
2857
|
execute: async (context, request) => {
|
|
2741
2858
|
const startedAt = Date.now();
|
|
2859
|
+
const operationId = digestRuntimeValue(`${command.commandId}:${request.requestId}`);
|
|
2860
|
+
const startedPresentation = codeToolRequestPresentation(request);
|
|
2742
2861
|
await this.#event(
|
|
2743
2862
|
command,
|
|
2744
|
-
{
|
|
2863
|
+
{
|
|
2864
|
+
type: "tool",
|
|
2865
|
+
phase: "started",
|
|
2866
|
+
tool: request.tool,
|
|
2867
|
+
operationId,
|
|
2868
|
+
...startedPresentation ? { presentation: startedPresentation } : {}
|
|
2869
|
+
},
|
|
2745
2870
|
active.conversationRefs
|
|
2746
2871
|
).catch(() => void 0);
|
|
2747
2872
|
const response2 = await broker.execute(context, request);
|
|
2873
|
+
const completedPresentation = codeToolResultPresentation(request, response2);
|
|
2748
2874
|
await this.#event(command, {
|
|
2749
2875
|
type: "tool",
|
|
2750
2876
|
phase: "completed",
|
|
2751
2877
|
tool: request.tool,
|
|
2752
2878
|
ok: response2.ok,
|
|
2753
|
-
durationMs: Date.now() - startedAt
|
|
2879
|
+
durationMs: Date.now() - startedAt,
|
|
2880
|
+
operationId,
|
|
2881
|
+
...completedPresentation ? { presentation: completedPresentation } : {}
|
|
2754
2882
|
}, active.conversationRefs).catch(() => void 0);
|
|
2755
2883
|
return response2;
|
|
2756
2884
|
}
|
|
@@ -2826,4 +2954,4 @@ export {
|
|
|
2826
2954
|
runGoal,
|
|
2827
2955
|
TheseusRuntimeEngine
|
|
2828
2956
|
};
|
|
2829
|
-
//# sourceMappingURL=chunk-
|
|
2957
|
+
//# sourceMappingURL=chunk-ISR434K7.js.map
|