@odla-ai/harness 0.7.0 → 0.7.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-QZXCQSPZ.js → chunk-GYWQM76X.js} +177 -28
- package/dist/chunk-GYWQM76X.js.map +1 -0
- package/dist/code-runtime-cli.cjs +176 -27
- package/dist/code-runtime-cli.cjs.map +1 -1
- package/dist/code-runtime-cli.js +1 -1
- package/dist/node.cjs +176 -27
- 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 +1 -1
- package/package.json +1 -1
- package/dist/chunk-QZXCQSPZ.js.map +0 -1
|
@@ -1128,7 +1128,10 @@ Never claim a build or test passed unless odla_run_recipe returned that result.`
|
|
|
1128
1128
|
var V2_SYSTEM_PROMPT = `You are the coding agent inside an odla Code harness.
|
|
1129
1129
|
Start by orienting: odla_list shows the files in the workspace and odla_search
|
|
1130
1130
|
finds a literal string across them. Prefer those over guessing a path.
|
|
1131
|
-
Then odla_read a bounded range, and odla_apply_git_diff to mutate.
|
|
1131
|
+
Then odla_read a bounded range, and odla_apply_git_diff to mutate. When you
|
|
1132
|
+
need several independent searches or file ranges, issue those read-only calls
|
|
1133
|
+
together in one turn; their results stay ordered and the harness overlaps them.
|
|
1134
|
+
Never issue odla_apply_git_diff or odla_run_recipe alongside another tool call.
|
|
1132
1135
|
For mutations, call odla_apply_git_diff with raw git diff text. It must start
|
|
1133
1136
|
with "diff --git a/<path> b/<path>", include matching "---" and "+++" file
|
|
1134
1137
|
headers and numbered "@@" hunks, and never use "*** Begin Patch" wrappers.
|
|
@@ -1159,18 +1162,26 @@ var SYSTEM_PROMPT_FOR = {
|
|
|
1159
1162
|
};
|
|
1160
1163
|
function codeSkill(opts) {
|
|
1161
1164
|
let seq = 0;
|
|
1165
|
+
let nextCompletion = 1;
|
|
1166
|
+
const completed = /* @__PURE__ */ new Map();
|
|
1162
1167
|
const call = async (tool, input, signal) => {
|
|
1168
|
+
const sequence = ++seq;
|
|
1163
1169
|
const startedAt = Date.now();
|
|
1164
1170
|
const response2 = await opts.broker.execute(
|
|
1165
1171
|
{ lease: opts.lease, workspaceDir: opts.workspaceDir, signal },
|
|
1166
|
-
{ requestId: `bench-${tool}-${
|
|
1172
|
+
{ requestId: `bench-${tool}-${sequence}`, tool, input }
|
|
1167
1173
|
);
|
|
1168
|
-
|
|
1174
|
+
completed.set(sequence, {
|
|
1169
1175
|
tool,
|
|
1170
1176
|
ok: response2.ok,
|
|
1171
1177
|
durationMs: Date.now() - startedAt,
|
|
1172
1178
|
...response2.ok ? {} : { error: String(response2.content).slice(0, 300) }
|
|
1173
1179
|
});
|
|
1180
|
+
while (completed.has(nextCompletion)) {
|
|
1181
|
+
const completion = completed.get(nextCompletion);
|
|
1182
|
+
completed.delete(nextCompletion++);
|
|
1183
|
+
opts.onToolCall?.(completion);
|
|
1184
|
+
}
|
|
1174
1185
|
return { content: response2.content, isError: !response2.ok };
|
|
1175
1186
|
};
|
|
1176
1187
|
const read2 = {
|
|
@@ -1186,6 +1197,7 @@ function codeSkill(opts) {
|
|
|
1186
1197
|
},
|
|
1187
1198
|
additionalProperties: false
|
|
1188
1199
|
},
|
|
1200
|
+
concurrency: "parallel",
|
|
1189
1201
|
handler: (input, ctx) => call("sandbox.read", input, ctx.signal)
|
|
1190
1202
|
};
|
|
1191
1203
|
const applyPatch = {
|
|
@@ -1227,6 +1239,7 @@ function codeSkill(opts) {
|
|
|
1227
1239
|
},
|
|
1228
1240
|
additionalProperties: false
|
|
1229
1241
|
},
|
|
1242
|
+
concurrency: "parallel",
|
|
1230
1243
|
handler: (input, ctx) => call("sandbox.list", input, ctx.signal)
|
|
1231
1244
|
};
|
|
1232
1245
|
const searchFiles = {
|
|
@@ -1243,11 +1256,13 @@ function codeSkill(opts) {
|
|
|
1243
1256
|
},
|
|
1244
1257
|
additionalProperties: false
|
|
1245
1258
|
},
|
|
1259
|
+
concurrency: "parallel",
|
|
1246
1260
|
handler: (input, ctx) => call("sandbox.search", input, ctx.signal)
|
|
1247
1261
|
};
|
|
1248
1262
|
const graphTool = (name, tool, description, required) => ({
|
|
1249
1263
|
name,
|
|
1250
1264
|
description,
|
|
1265
|
+
concurrency: "parallel",
|
|
1251
1266
|
inputSchema: {
|
|
1252
1267
|
type: "object",
|
|
1253
1268
|
...required ? { required: ["query"] } : {},
|
|
@@ -1437,11 +1452,30 @@ function createCodeRuntimeInference(options) {
|
|
|
1437
1452
|
}
|
|
1438
1453
|
|
|
1439
1454
|
// src/code-tool-discovery.ts
|
|
1455
|
+
import { spawn as spawn3 } from "child_process";
|
|
1440
1456
|
import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
|
|
1441
1457
|
import { relative as relative2, resolve as resolve4 } from "path";
|
|
1442
1458
|
var DEFAULT_MAX_FILES = 2e4;
|
|
1443
1459
|
var DEFAULT_MAX_RESULTS = 100;
|
|
1444
1460
|
var DEFAULT_MAX_FILE_BYTES = 512 * 1024;
|
|
1461
|
+
function createWorkspaceFileRegistry(limit = DEFAULT_MAX_FILES, enumerate = registeredFiles) {
|
|
1462
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
1463
|
+
return {
|
|
1464
|
+
files(root) {
|
|
1465
|
+
const existing = cache2.get(root);
|
|
1466
|
+
if (existing) return existing;
|
|
1467
|
+
const pending = enumerate(root, limit).then((paths) => Object.freeze(paths));
|
|
1468
|
+
cache2.set(root, pending);
|
|
1469
|
+
void pending.catch(() => {
|
|
1470
|
+
if (cache2.get(root) === pending) cache2.delete(root);
|
|
1471
|
+
});
|
|
1472
|
+
return pending;
|
|
1473
|
+
},
|
|
1474
|
+
invalidate(root) {
|
|
1475
|
+
cache2.delete(root);
|
|
1476
|
+
}
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1445
1479
|
async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
1446
1480
|
const paths = [];
|
|
1447
1481
|
const walk = async (directory) => {
|
|
@@ -1472,28 +1506,122 @@ function listWorkspace(paths, options = {}) {
|
|
|
1472
1506
|
return scoped.slice(0, max);
|
|
1473
1507
|
}
|
|
1474
1508
|
async function searchWorkspace(root, paths, options) {
|
|
1475
|
-
|
|
1476
|
-
if (!query) throw new TypeError("search query must be a non-empty string");
|
|
1509
|
+
options.signal?.throwIfAborted();
|
|
1510
|
+
if (!options.query) throw new TypeError("search query must be a non-empty string");
|
|
1477
1511
|
const maxResults = options.maxResults ?? DEFAULT_MAX_RESULTS;
|
|
1478
1512
|
const maxFileBytes = options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
|
|
1479
1513
|
const scoped = listWorkspace(paths, { ...options.prefix ? { prefix: options.prefix } : {}, maxEntries: paths.length });
|
|
1514
|
+
if (scoped.length === 0) return [];
|
|
1515
|
+
try {
|
|
1516
|
+
return await nativeSearch(root, scoped, { ...options, maxResults, maxFileBytes });
|
|
1517
|
+
} catch (error) {
|
|
1518
|
+
options.signal?.throwIfAborted();
|
|
1519
|
+
return fallbackSearch(root, scoped, { ...options, maxResults, maxFileBytes });
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
var MAX_NATIVE_ARG_BYTES = 96 * 1024;
|
|
1523
|
+
async function nativeSearch(root, paths, options) {
|
|
1524
|
+
const batches = [];
|
|
1525
|
+
let batch = [];
|
|
1526
|
+
let bytes = 0;
|
|
1527
|
+
for (const path of paths) {
|
|
1528
|
+
const size = Buffer.byteLength(path) + 1;
|
|
1529
|
+
if (batch.length > 0 && bytes + size > MAX_NATIVE_ARG_BYTES) {
|
|
1530
|
+
batches.push(batch);
|
|
1531
|
+
batch = [];
|
|
1532
|
+
bytes = 0;
|
|
1533
|
+
}
|
|
1534
|
+
batch.push(path);
|
|
1535
|
+
bytes += size;
|
|
1536
|
+
}
|
|
1537
|
+
if (batch.length > 0) batches.push(batch);
|
|
1538
|
+
const matches = [];
|
|
1539
|
+
for (const files of batches) {
|
|
1540
|
+
const remaining = options.maxResults - matches.length;
|
|
1541
|
+
if (remaining <= 0) break;
|
|
1542
|
+
matches.push(...await nativeSearchBatch(root, files, options, remaining));
|
|
1543
|
+
}
|
|
1544
|
+
return matches;
|
|
1545
|
+
}
|
|
1546
|
+
function nativeSearchBatch(root, paths, options, remaining) {
|
|
1547
|
+
return new Promise((resolveMatches, reject) => {
|
|
1548
|
+
const args = [
|
|
1549
|
+
"--fixed-strings",
|
|
1550
|
+
"--json",
|
|
1551
|
+
"--no-messages",
|
|
1552
|
+
"--sort=path",
|
|
1553
|
+
`--max-filesize=${options.maxFileBytes}`,
|
|
1554
|
+
"--max-columns=4096",
|
|
1555
|
+
"--max-columns-preview",
|
|
1556
|
+
options.caseSensitive === false ? "--ignore-case" : "--case-sensitive",
|
|
1557
|
+
"--",
|
|
1558
|
+
options.query,
|
|
1559
|
+
...paths
|
|
1560
|
+
];
|
|
1561
|
+
const child = spawn3("rg", args, {
|
|
1562
|
+
cwd: root,
|
|
1563
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1564
|
+
...options.signal ? { signal: options.signal } : {}
|
|
1565
|
+
});
|
|
1566
|
+
const matches = [];
|
|
1567
|
+
let carry = "";
|
|
1568
|
+
let stopped = false;
|
|
1569
|
+
const consume = (line) => {
|
|
1570
|
+
if (matches.length >= remaining) return;
|
|
1571
|
+
let event;
|
|
1572
|
+
try {
|
|
1573
|
+
event = JSON.parse(line);
|
|
1574
|
+
} catch {
|
|
1575
|
+
return;
|
|
1576
|
+
}
|
|
1577
|
+
const path = event.data?.path?.text;
|
|
1578
|
+
const lineNumber = event.data?.line_number;
|
|
1579
|
+
const source = event.data?.lines?.text;
|
|
1580
|
+
if (event.type !== "match" || path === void 0 || lineNumber === void 0 || source === void 0) return;
|
|
1581
|
+
matches.push({ path, line: lineNumber, text: source.trim().slice(0, 240) });
|
|
1582
|
+
if (matches.length >= remaining) {
|
|
1583
|
+
stopped = true;
|
|
1584
|
+
child.kill();
|
|
1585
|
+
}
|
|
1586
|
+
};
|
|
1587
|
+
child.stdout.setEncoding("utf8");
|
|
1588
|
+
child.stdout.on("data", (chunk) => {
|
|
1589
|
+
carry += chunk;
|
|
1590
|
+
let newline = carry.indexOf("\n");
|
|
1591
|
+
while (newline >= 0) {
|
|
1592
|
+
consume(carry.slice(0, newline));
|
|
1593
|
+
carry = carry.slice(newline + 1);
|
|
1594
|
+
newline = carry.indexOf("\n");
|
|
1595
|
+
}
|
|
1596
|
+
});
|
|
1597
|
+
child.once("error", reject);
|
|
1598
|
+
child.once("close", (code) => {
|
|
1599
|
+
if (carry) consume(carry);
|
|
1600
|
+
if (stopped || code === 0 || code === 1) resolveMatches(matches);
|
|
1601
|
+
else reject(new Error(`native search exited with status ${code ?? "unknown"}`));
|
|
1602
|
+
});
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
async function fallbackSearch(root, scoped, options) {
|
|
1606
|
+
const query = options.caseSensitive === false ? options.query.toLowerCase() : options.query;
|
|
1480
1607
|
const matches = [];
|
|
1481
1608
|
for (const path of scoped) {
|
|
1482
|
-
|
|
1609
|
+
options.signal?.throwIfAborted();
|
|
1610
|
+
if (matches.length >= options.maxResults) break;
|
|
1483
1611
|
let source;
|
|
1484
1612
|
try {
|
|
1485
1613
|
source = await readFile2(resolve4(root, path));
|
|
1486
1614
|
} catch {
|
|
1487
1615
|
continue;
|
|
1488
1616
|
}
|
|
1489
|
-
if (source.byteLength > maxFileBytes || source.includes(0)) continue;
|
|
1617
|
+
if (source.byteLength > options.maxFileBytes || source.includes(0)) continue;
|
|
1490
1618
|
const lines = source.toString("utf8").split("\n");
|
|
1491
1619
|
for (let index = 0; index < lines.length; index += 1) {
|
|
1492
1620
|
const raw = lines[index];
|
|
1493
1621
|
const haystack = options.caseSensitive === false ? raw.toLowerCase() : raw;
|
|
1494
1622
|
if (!haystack.includes(query)) continue;
|
|
1495
1623
|
matches.push({ path, line: index + 1, text: raw.trim().slice(0, 240) });
|
|
1496
|
-
if (matches.length >= maxResults) break;
|
|
1624
|
+
if (matches.length >= options.maxResults) break;
|
|
1497
1625
|
}
|
|
1498
1626
|
}
|
|
1499
1627
|
return matches;
|
|
@@ -1778,6 +1906,9 @@ function workspaceGraphs(workspaceDir, paths) {
|
|
|
1778
1906
|
cache.set(workspaceDir, built);
|
|
1779
1907
|
return built;
|
|
1780
1908
|
}
|
|
1909
|
+
function forgetWorkspaceGraphs(workspaceDir) {
|
|
1910
|
+
cache.delete(workspaceDir);
|
|
1911
|
+
}
|
|
1781
1912
|
var shortId = (id) => id.slice(id.indexOf(":") + 1);
|
|
1782
1913
|
function renderOverview(graphs, prefix) {
|
|
1783
1914
|
const rows = rollup(graphs.graph, FILE, prefix === void 0 ? {} : { prefix });
|
|
@@ -1824,7 +1955,7 @@ var GRAPH_TOOLS = /* @__PURE__ */ new Set([
|
|
|
1824
1955
|
"sandbox.who_imports",
|
|
1825
1956
|
"sandbox.who_touches"
|
|
1826
1957
|
]);
|
|
1827
|
-
async function read(context, request, options, policy) {
|
|
1958
|
+
async function read(context, request, options, policy, registry) {
|
|
1828
1959
|
exactKeys(request.input, ["path", "startLine", "endLine"]);
|
|
1829
1960
|
const path = stringField(request.input, "path");
|
|
1830
1961
|
const startLine = optionalInteger(request.input.startLine) ?? 1;
|
|
@@ -1832,7 +1963,7 @@ async function read(context, request, options, policy) {
|
|
|
1832
1963
|
if (endLine < startLine || endLine - startLine + 1 > (options.maxReadLines ?? 2e3)) {
|
|
1833
1964
|
throw new TypeError("requested line range exceeds its bound");
|
|
1834
1965
|
}
|
|
1835
|
-
const paths = await
|
|
1966
|
+
const paths = await registry.files(context.workspaceDir);
|
|
1836
1967
|
if (!paths.includes(path)) {
|
|
1837
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.`);
|
|
1838
1969
|
}
|
|
@@ -1852,13 +1983,13 @@ async function read(context, request, options, policy) {
|
|
|
1852
1983
|
}
|
|
1853
1984
|
return response(request, true, content, { path, startLine, endLine: Math.min(endLine, lines.length) });
|
|
1854
1985
|
}
|
|
1855
|
-
async function list(context, request, options, policy) {
|
|
1986
|
+
async function list(context, request, options, policy, registry) {
|
|
1856
1987
|
exactKeys(request.input, ["prefix", "maxEntries"]);
|
|
1857
1988
|
const raw = request.input.prefix;
|
|
1858
1989
|
const prefix = typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
1859
1990
|
const maxEntries = optionalInteger(request.input.maxEntries) ?? 1e3;
|
|
1860
1991
|
if (maxEntries > 5e3) throw new TypeError("maxEntries exceeds its bound");
|
|
1861
|
-
const paths = await
|
|
1992
|
+
const paths = await registry.files(context.workspaceDir);
|
|
1862
1993
|
const allowed = await policy.list(policyContext(context, request, options, { paths, ...prefix ? { prefix } : {} }));
|
|
1863
1994
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
1864
1995
|
const entries = listWorkspace(paths, { ...prefix ? { prefix } : {}, maxEntries });
|
|
@@ -1876,7 +2007,7 @@ async function list(context, request, options, policy) {
|
|
|
1876
2007
|
{ count: entries.length, truncated }
|
|
1877
2008
|
);
|
|
1878
2009
|
}
|
|
1879
|
-
async function search(context, request, options, policy) {
|
|
2010
|
+
async function search(context, request, options, policy, registry) {
|
|
1880
2011
|
exactKeys(request.input, ["query", "prefix", "maxResults", "caseSensitive"]);
|
|
1881
2012
|
const query = stringField(request.input, "query");
|
|
1882
2013
|
if (query.length > 512) throw new TypeError("search query exceeds its bound");
|
|
@@ -1885,21 +2016,22 @@ async function search(context, request, options, policy) {
|
|
|
1885
2016
|
const maxResults = optionalInteger(request.input.maxResults) ?? 100;
|
|
1886
2017
|
if (maxResults > 500) throw new TypeError("maxResults exceeds its bound");
|
|
1887
2018
|
const caseSensitive = request.input.caseSensitive === void 0 ? true : request.input.caseSensitive === true;
|
|
1888
|
-
const paths = await
|
|
2019
|
+
const paths = await registry.files(context.workspaceDir);
|
|
1889
2020
|
const allowed = await policy.search(policyContext(context, request, options, { paths, query, ...prefix ? { prefix } : {} }));
|
|
1890
2021
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
1891
2022
|
const matches = await searchWorkspace(context.workspaceDir, paths, {
|
|
1892
2023
|
query,
|
|
1893
2024
|
maxResults,
|
|
1894
2025
|
caseSensitive,
|
|
1895
|
-
...prefix ? { prefix } : {}
|
|
2026
|
+
...prefix ? { prefix } : {},
|
|
2027
|
+
...context.signal ? { signal: context.signal } : {}
|
|
1896
2028
|
});
|
|
1897
2029
|
if (!matches.length) return response(request, true, `No match for "${query}".`, { count: 0 });
|
|
1898
2030
|
return response(request, true, matches.map((match) => `${match.path}:${match.line}: ${match.text}`).join("\n"), {
|
|
1899
2031
|
count: matches.length
|
|
1900
2032
|
});
|
|
1901
2033
|
}
|
|
1902
|
-
async function graphQuery(context, request, options, policy) {
|
|
2034
|
+
async function graphQuery(context, request, options, policy, registry) {
|
|
1903
2035
|
exactKeys(request.input, ["query"]);
|
|
1904
2036
|
const raw = request.input.query;
|
|
1905
2037
|
const query = typeof raw === "string" ? raw : "";
|
|
@@ -1909,7 +2041,7 @@ async function graphQuery(context, request, options, policy) {
|
|
|
1909
2041
|
selector: query
|
|
1910
2042
|
}));
|
|
1911
2043
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
1912
|
-
const paths = await
|
|
2044
|
+
const paths = await registry.files(context.workspaceDir);
|
|
1913
2045
|
const graphs = await workspaceGraphs(context.workspaceDir, paths);
|
|
1914
2046
|
if (request.tool === "sandbox.overview") {
|
|
1915
2047
|
return response(request, true, renderOverview(graphs, query || void 0));
|
|
@@ -1925,23 +2057,38 @@ function createCodeToolBroker(options) {
|
|
|
1925
2057
|
validateOptions(options);
|
|
1926
2058
|
const recipes = new Map(options.recipes.map((recipe2) => [recipe2.id, recipe2]));
|
|
1927
2059
|
const policy = createCodePolicyGate(options);
|
|
1928
|
-
|
|
2060
|
+
const registry = createWorkspaceFileRegistry();
|
|
2061
|
+
let barrier = Promise.resolve();
|
|
2062
|
+
const activeReads = /* @__PURE__ */ new Set();
|
|
1929
2063
|
return {
|
|
1930
2064
|
execute(context, request) {
|
|
1931
|
-
|
|
1932
|
-
|
|
2065
|
+
if (isReadTool(request.tool)) {
|
|
2066
|
+
const result2 = barrier.then(() => route(context, request, options, recipes, policy, registry));
|
|
2067
|
+
const settled = result2.then(() => void 0, () => void 0);
|
|
2068
|
+
activeReads.add(settled);
|
|
2069
|
+
void settled.then(() => {
|
|
2070
|
+
activeReads.delete(settled);
|
|
2071
|
+
});
|
|
2072
|
+
return result2;
|
|
2073
|
+
}
|
|
2074
|
+
const earlierReads = [...activeReads];
|
|
2075
|
+
const result = barrier.then(() => Promise.all(earlierReads)).then(() => route(context, request, options, recipes, policy, registry));
|
|
2076
|
+
barrier = result.then(() => void 0, () => void 0);
|
|
1933
2077
|
return result;
|
|
1934
2078
|
}
|
|
1935
2079
|
};
|
|
1936
2080
|
}
|
|
1937
|
-
|
|
2081
|
+
function isReadTool(tool) {
|
|
2082
|
+
return tool === "sandbox.read" || tool === "sandbox.list" || tool === "sandbox.search" || GRAPH_TOOLS.has(tool);
|
|
2083
|
+
}
|
|
2084
|
+
async function route(context, request, options, recipes, policy, registry) {
|
|
1938
2085
|
try {
|
|
1939
2086
|
if (context.signal?.aborted) throw new TypeError("tool request was cancelled");
|
|
1940
|
-
if (request.tool === "sandbox.read") return await read(context, request, options, policy);
|
|
1941
|
-
if (request.tool === "sandbox.list") return await list(context, request, options, policy);
|
|
1942
|
-
if (request.tool === "sandbox.search") return await search(context, request, options, policy);
|
|
1943
|
-
if (GRAPH_TOOLS.has(request.tool)) return await graphQuery(context, request, options, policy);
|
|
1944
|
-
if (request.tool === "sandbox.apply_patch") return await patch(context, request, options, policy);
|
|
2087
|
+
if (request.tool === "sandbox.read") return await read(context, request, options, policy, registry);
|
|
2088
|
+
if (request.tool === "sandbox.list") return await list(context, request, options, policy, registry);
|
|
2089
|
+
if (request.tool === "sandbox.search") return await search(context, request, options, policy, registry);
|
|
2090
|
+
if (GRAPH_TOOLS.has(request.tool)) return await graphQuery(context, request, options, policy, registry);
|
|
2091
|
+
if (request.tool === "sandbox.apply_patch") return await patch(context, request, options, policy, registry);
|
|
1945
2092
|
return await recipe(context, request, options, recipes, policy);
|
|
1946
2093
|
} catch (reason) {
|
|
1947
2094
|
return response(request, false, toolFailureMessage(reason));
|
|
@@ -1956,7 +2103,7 @@ function toolFailureMessage(reason) {
|
|
|
1956
2103
|
if (code === "EACCES" || code === "EPERM") return "that path is not readable through this tool";
|
|
1957
2104
|
return "tool failed closed";
|
|
1958
2105
|
}
|
|
1959
|
-
async function patch(context, request, options, policy) {
|
|
2106
|
+
async function patch(context, request, options, policy, registry) {
|
|
1960
2107
|
exactKeys(request.input, ["patch"]);
|
|
1961
2108
|
const value = stringField(request.input, "patch");
|
|
1962
2109
|
const paths = validateCodePatch(value, options.maxPatchBytes ?? 256 * 1024);
|
|
@@ -1966,6 +2113,8 @@ async function patch(context, request, options, policy) {
|
|
|
1966
2113
|
const allowed = await policy.patch(policyContext(context, request, options, { patch: value }));
|
|
1967
2114
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
1968
2115
|
await applyCodePatch(context.workspaceDir, value, paths);
|
|
2116
|
+
registry.invalidate(context.workspaceDir);
|
|
2117
|
+
forgetWorkspaceGraphs(context.workspaceDir);
|
|
1969
2118
|
return response(request, true, `Applied patch to ${paths.length} file(s).`, { paths });
|
|
1970
2119
|
}
|
|
1971
2120
|
async function recipe(context, request, options, recipes, policy) {
|
|
@@ -2677,4 +2826,4 @@ export {
|
|
|
2677
2826
|
runGoal,
|
|
2678
2827
|
TheseusRuntimeEngine
|
|
2679
2828
|
};
|
|
2680
|
-
//# sourceMappingURL=chunk-
|
|
2829
|
+
//# sourceMappingURL=chunk-GYWQM76X.js.map
|