@odla-ai/harness 0.9.4 → 0.10.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-XSUUHWNX.js → chunk-WM34GGTK.js} +552 -57
- package/dist/chunk-WM34GGTK.js.map +1 -0
- package/dist/code-runtime-cli.cjs +557 -63
- package/dist/code-runtime-cli.cjs.map +1 -1
- package/dist/code-runtime-cli.js +1 -1
- package/dist/node.cjs +563 -67
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +40 -4
- package/dist/node.d.ts +40 -4
- package/dist/node.js +3 -1
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-XSUUHWNX.js.map +0 -1
|
@@ -87,6 +87,34 @@ function createCodeRuntimeControlClient(options) {
|
|
|
87
87
|
}
|
|
88
88
|
return value;
|
|
89
89
|
};
|
|
90
|
+
const callRaw = async (path, body) => {
|
|
91
|
+
const timeout = AbortSignal.timeout(modelRequestTimeoutMs);
|
|
92
|
+
const signals = [options.signal, timeout].filter((item) => Boolean(item));
|
|
93
|
+
const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
|
|
94
|
+
let response2;
|
|
95
|
+
try {
|
|
96
|
+
response2 = await request(`${endpoint}${path}`, {
|
|
97
|
+
method: "POST",
|
|
98
|
+
headers: { authorization: `Bearer ${options.token}`, "content-type": "application/json" },
|
|
99
|
+
body: JSON.stringify(body),
|
|
100
|
+
redirect: "error",
|
|
101
|
+
signal
|
|
102
|
+
});
|
|
103
|
+
} catch (cause) {
|
|
104
|
+
if (options.signal?.aborted) throw cause;
|
|
105
|
+
throw new CodeRuntimeControlError("Code source archive is unavailable", 503, "transport_unavailable");
|
|
106
|
+
}
|
|
107
|
+
if (!response2.ok) {
|
|
108
|
+
const value = await response2.json().catch(() => null);
|
|
109
|
+
const problem = record(record(value)?.error);
|
|
110
|
+
throw new CodeRuntimeControlError(
|
|
111
|
+
typeof problem?.message === "string" ? problem.message : `Code runtime request failed (${response2.status})`,
|
|
112
|
+
response2.status,
|
|
113
|
+
typeof problem?.code === "string" ? problem.code : void 0
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return response2;
|
|
117
|
+
};
|
|
90
118
|
return {
|
|
91
119
|
heartbeat: async (version, capabilities) => {
|
|
92
120
|
validateHeartbeat(version, capabilities);
|
|
@@ -98,6 +126,14 @@ function createCodeRuntimeControlClient(options) {
|
|
|
98
126
|
source: async (sessionId) => parseSource(
|
|
99
127
|
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source`, {})
|
|
100
128
|
),
|
|
129
|
+
sourceArchive: async (sessionId, alias) => {
|
|
130
|
+
if (!/^[a-z][a-z0-9-]{0,39}$/.test(alias)) throw new TypeError("invalid Code source alias");
|
|
131
|
+
const response2 = await callRaw(
|
|
132
|
+
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source/archive`,
|
|
133
|
+
{ alias }
|
|
134
|
+
);
|
|
135
|
+
return parseSourceArchiveResponse(response2, alias);
|
|
136
|
+
},
|
|
101
137
|
infer: async (sessionId, inference) => {
|
|
102
138
|
const value = record(await call(
|
|
103
139
|
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/inference`,
|
|
@@ -162,9 +198,70 @@ function createCodeRuntimeControlClient(options) {
|
|
|
162
198
|
}
|
|
163
199
|
};
|
|
164
200
|
}
|
|
201
|
+
var ARCHIVE_LIMIT_MAXIMA = {
|
|
202
|
+
maxCompressedBytes: 64 * 1024 * 1024,
|
|
203
|
+
maxDecompressedBytes: 96 * 1024 * 1024,
|
|
204
|
+
maxEntries: 2e5,
|
|
205
|
+
maxFiles: 1e5,
|
|
206
|
+
maxFileBytes: 16 * 1024 * 1024,
|
|
207
|
+
maxTotalFileBytes: 80 * 1024 * 1024,
|
|
208
|
+
maxPathBytes: 4096,
|
|
209
|
+
maxExtendedHeaderBytes: 32 * 1024
|
|
210
|
+
};
|
|
211
|
+
async function parseSourceArchiveResponse(response2, expectedAlias) {
|
|
212
|
+
const alias = response2.headers.get("x-odla-source-alias") ?? "";
|
|
213
|
+
const repository = response2.headers.get("x-odla-source-repository") ?? "";
|
|
214
|
+
const commitSha = response2.headers.get("x-odla-source-commit") ?? "";
|
|
215
|
+
if (alias !== expectedAlias || !/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repository) || !/^[0-9a-f]{40}$/.test(commitSha)) {
|
|
216
|
+
await response2.body?.cancel().catch(() => void 0);
|
|
217
|
+
throw new CodeRuntimeControlError("invalid Code source archive identity", 502, "invalid_response");
|
|
218
|
+
}
|
|
219
|
+
const limits = Object.fromEntries(Object.entries(ARCHIVE_LIMIT_MAXIMA).map(([key, maximum]) => {
|
|
220
|
+
const header = `x-odla-${key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}`;
|
|
221
|
+
const value = response2.headers.get(header) ?? "";
|
|
222
|
+
if (!/^\d+$/.test(value) || !Number.isSafeInteger(Number(value)) || Number(value) < 1 || Number(value) > maximum) {
|
|
223
|
+
throw new CodeRuntimeControlError("invalid Code source archive limits", 502, "invalid_response");
|
|
224
|
+
}
|
|
225
|
+
return [key, Number(value)];
|
|
226
|
+
}));
|
|
227
|
+
const length = response2.headers.get("content-length");
|
|
228
|
+
if (length && (!/^\d+$/.test(length) || Number(length) > limits.maxCompressedBytes)) {
|
|
229
|
+
await response2.body?.cancel().catch(() => void 0);
|
|
230
|
+
throw new CodeRuntimeControlError("Code source archive exceeds its byte bound", 502, "invalid_response");
|
|
231
|
+
}
|
|
232
|
+
const compressed = await readBoundedResponse(response2.body, limits.maxCompressedBytes);
|
|
233
|
+
return { alias, repository, commitSha, compressed, limits };
|
|
234
|
+
}
|
|
235
|
+
async function readBoundedResponse(body, maximum) {
|
|
236
|
+
if (!body) throw new CodeRuntimeControlError("Code source archive body is missing", 502, "invalid_response");
|
|
237
|
+
const reader = body.getReader();
|
|
238
|
+
const chunks = [];
|
|
239
|
+
let size = 0;
|
|
240
|
+
try {
|
|
241
|
+
while (true) {
|
|
242
|
+
const { done, value } = await reader.read();
|
|
243
|
+
if (done) break;
|
|
244
|
+
size += value.byteLength;
|
|
245
|
+
if (size > maximum) {
|
|
246
|
+
await reader.cancel().catch(() => void 0);
|
|
247
|
+
throw new CodeRuntimeControlError("Code source archive exceeds its byte bound", 502, "invalid_response");
|
|
248
|
+
}
|
|
249
|
+
chunks.push(value);
|
|
250
|
+
}
|
|
251
|
+
} finally {
|
|
252
|
+
reader.releaseLock();
|
|
253
|
+
}
|
|
254
|
+
const result = new Uint8Array(size);
|
|
255
|
+
let offset = 0;
|
|
256
|
+
for (const chunk of chunks) {
|
|
257
|
+
result.set(chunk, offset);
|
|
258
|
+
offset += chunk.byteLength;
|
|
259
|
+
}
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
165
262
|
|
|
166
263
|
// src/code-runtime.ts
|
|
167
|
-
var CODE_RUNTIME_PROTOCOL_VERSION =
|
|
264
|
+
var CODE_RUNTIME_PROTOCOL_VERSION = 3;
|
|
168
265
|
async function runCodeRuntimeHeartbeatLoop(options) {
|
|
169
266
|
const heartbeatMs = options.heartbeatMs ?? 15e3;
|
|
170
267
|
if (!Number.isSafeInteger(heartbeatMs) || heartbeatMs < 1e3 || heartbeatMs > 3e5) {
|
|
@@ -233,12 +330,12 @@ function retryableControlFailure(value) {
|
|
|
233
330
|
return failure.status === 408 || failure.status === 425 || failure.status === 429 || failure.status >= 500;
|
|
234
331
|
}
|
|
235
332
|
function wait(ms, signal) {
|
|
236
|
-
return new Promise((
|
|
237
|
-
if (signal?.aborted) return
|
|
238
|
-
const timer = setTimeout(
|
|
333
|
+
return new Promise((resolve6) => {
|
|
334
|
+
if (signal?.aborted) return resolve6();
|
|
335
|
+
const timer = setTimeout(resolve6, ms);
|
|
239
336
|
signal?.addEventListener("abort", () => {
|
|
240
337
|
clearTimeout(timer);
|
|
241
|
-
|
|
338
|
+
resolve6();
|
|
242
339
|
}, { once: true });
|
|
243
340
|
});
|
|
244
341
|
}
|
|
@@ -1080,6 +1177,246 @@ var CodeRuntimeCheckpointManager = class {
|
|
|
1080
1177
|
}
|
|
1081
1178
|
};
|
|
1082
1179
|
|
|
1180
|
+
// src/code-runtime-archive.ts
|
|
1181
|
+
import { gunzipSync } from "zlib";
|
|
1182
|
+
import { mkdir, mkdtemp, rm, writeFile } from "fs/promises";
|
|
1183
|
+
import { tmpdir } from "os";
|
|
1184
|
+
import { dirname, join as join2, resolve as resolve3, sep as sep2 } from "path";
|
|
1185
|
+
var RESERVED2 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
|
|
1186
|
+
var SECRET2 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
|
|
1187
|
+
var MAX_NUL_SHARE = 0.1;
|
|
1188
|
+
async function materializeCodeRuntimeArchive(archive, visiblePaths, tempRoot = tmpdir()) {
|
|
1189
|
+
if (!archive.compressed.byteLength || archive.compressed.byteLength > archive.limits.maxCompressedBytes) {
|
|
1190
|
+
throw new TypeError("Code source archive exceeds its compressed byte bound");
|
|
1191
|
+
}
|
|
1192
|
+
let bytes;
|
|
1193
|
+
try {
|
|
1194
|
+
bytes = archive.compressed[0] === 31 && archive.compressed[1] === 139 ? gunzipSync(archive.compressed, { maxOutputLength: archive.limits.maxDecompressedBytes }) : archive.compressed;
|
|
1195
|
+
} catch {
|
|
1196
|
+
throw new TypeError("Code source archive is not a valid bounded gzip stream");
|
|
1197
|
+
}
|
|
1198
|
+
if (bytes.byteLength > archive.limits.maxDecompressedBytes) {
|
|
1199
|
+
throw new TypeError("Code source archive exceeds its decompressed byte bound");
|
|
1200
|
+
}
|
|
1201
|
+
const entries = parseTar(bytes, archive.limits);
|
|
1202
|
+
const root = await mkdtemp(join2(tempRoot, "odla-code-archive-"));
|
|
1203
|
+
const sourceDir = join2(root, "source");
|
|
1204
|
+
await mkdir(sourceDir);
|
|
1205
|
+
let visible = 0;
|
|
1206
|
+
try {
|
|
1207
|
+
for (const entry of entries) {
|
|
1208
|
+
if (entry.directory || visiblePaths && !visiblePaths.has(entry.path)) continue;
|
|
1209
|
+
if (filteredPath(entry.path)) continue;
|
|
1210
|
+
let content;
|
|
1211
|
+
try {
|
|
1212
|
+
content = new TextDecoder("utf-8", { fatal: true, ignoreBOM: false }).decode(entry.bytes);
|
|
1213
|
+
} catch {
|
|
1214
|
+
continue;
|
|
1215
|
+
}
|
|
1216
|
+
if (nulShare(content) > MAX_NUL_SHARE) continue;
|
|
1217
|
+
const target = resolve3(sourceDir, entry.path);
|
|
1218
|
+
if (!target.startsWith(`${resolve3(sourceDir)}${sep2}`)) throw new TypeError("Code source path escapes its root");
|
|
1219
|
+
await mkdir(dirname(target), { recursive: true });
|
|
1220
|
+
await writeFile(target, content, { flag: "wx", mode: 420 });
|
|
1221
|
+
visible += 1;
|
|
1222
|
+
}
|
|
1223
|
+
if (!visible && !visiblePaths) throw new TypeError("GitHub commit has no Code-visible text source");
|
|
1224
|
+
return { sourceDir, cleanup: () => rm(root, { recursive: true, force: true }) };
|
|
1225
|
+
} catch (cause) {
|
|
1226
|
+
await rm(root, { recursive: true, force: true });
|
|
1227
|
+
throw cause;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
function parseTar(bytes, limits) {
|
|
1231
|
+
const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: false });
|
|
1232
|
+
const entries = [];
|
|
1233
|
+
const archivePaths = /* @__PURE__ */ new Set();
|
|
1234
|
+
let entryCount = 0;
|
|
1235
|
+
let fileCount = 0;
|
|
1236
|
+
let totalFileBytes = 0;
|
|
1237
|
+
let pendingPath;
|
|
1238
|
+
let pendingPaxPath;
|
|
1239
|
+
let offset = 0;
|
|
1240
|
+
let ended = false;
|
|
1241
|
+
while (offset + 512 <= bytes.byteLength) {
|
|
1242
|
+
const header = bytes.subarray(offset, offset + 512);
|
|
1243
|
+
offset += 512;
|
|
1244
|
+
if (zeroBlock(header)) {
|
|
1245
|
+
ended = true;
|
|
1246
|
+
assertZeroTail(bytes, offset);
|
|
1247
|
+
break;
|
|
1248
|
+
}
|
|
1249
|
+
entryCount += 1;
|
|
1250
|
+
if (entryCount > limits.maxEntries) throw new TypeError("Code source archive has too many entries");
|
|
1251
|
+
validateChecksum(header);
|
|
1252
|
+
const type = header[156] ?? 0;
|
|
1253
|
+
const size = tarNumber(header.subarray(124, 136));
|
|
1254
|
+
if (size > limits.maxDecompressedBytes || offset + aligned(size) > bytes.byteLength) {
|
|
1255
|
+
throw new TypeError("Code source archive entry is invalid or too large");
|
|
1256
|
+
}
|
|
1257
|
+
const payload = bytes.subarray(offset, offset + size);
|
|
1258
|
+
offset += aligned(size);
|
|
1259
|
+
if (type === 120 || type === 103) {
|
|
1260
|
+
if (size > limits.maxExtendedHeaderBytes) throw new TypeError("Code source archive extended header is too large");
|
|
1261
|
+
const pax = parsePax(payload, decoder, limits.maxPathBytes);
|
|
1262
|
+
if (pax.linkpath !== void 0 || pax.size !== void 0 || Object.keys(pax).some((key) => key.toLowerCase().includes("sparse"))) {
|
|
1263
|
+
throw new TypeError("Code source archive contains an unsupported entry");
|
|
1264
|
+
}
|
|
1265
|
+
if (type === 103 && pax.path !== void 0) throw new TypeError("Code source archive path is unsafe");
|
|
1266
|
+
if (type === 120 && pax.path !== void 0) {
|
|
1267
|
+
if (pendingPaxPath !== void 0 || pendingPath !== void 0) throw new TypeError("Code source archive is invalid");
|
|
1268
|
+
pendingPaxPath = pax.path;
|
|
1269
|
+
}
|
|
1270
|
+
continue;
|
|
1271
|
+
}
|
|
1272
|
+
if (type === 76) {
|
|
1273
|
+
if (size > limits.maxExtendedHeaderBytes || pendingPath !== void 0 || pendingPaxPath !== void 0) {
|
|
1274
|
+
throw new TypeError("Code source archive long path is invalid");
|
|
1275
|
+
}
|
|
1276
|
+
pendingPath = validateArchivePath(decode(payload, decoder).replace(/\0+$/, "").replace(/\n$/, ""), limits.maxPathBytes);
|
|
1277
|
+
continue;
|
|
1278
|
+
}
|
|
1279
|
+
if (type === 75) throw new TypeError("Code source archive contains an unsupported link");
|
|
1280
|
+
const path = validateArchivePath(pendingPaxPath ?? pendingPath ?? tarPath(header, decoder), limits.maxPathBytes);
|
|
1281
|
+
pendingPaxPath = void 0;
|
|
1282
|
+
pendingPath = void 0;
|
|
1283
|
+
const directory = type === 53;
|
|
1284
|
+
const regular = type === 0 || type === 48;
|
|
1285
|
+
if (!directory && !regular || directory && size !== 0 || archivePaths.has(path)) {
|
|
1286
|
+
throw new TypeError("Code source archive contains an invalid or duplicate entry");
|
|
1287
|
+
}
|
|
1288
|
+
archivePaths.add(path);
|
|
1289
|
+
if (regular) {
|
|
1290
|
+
fileCount += 1;
|
|
1291
|
+
totalFileBytes += size;
|
|
1292
|
+
if (fileCount > limits.maxFiles || size > limits.maxFileBytes || totalFileBytes > limits.maxTotalFileBytes) {
|
|
1293
|
+
throw new TypeError("Code source archive exceeds its file bounds");
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
entries.push({ path, directory, ...regular ? { bytes: payload } : {} });
|
|
1297
|
+
}
|
|
1298
|
+
if (!ended || pendingPath !== void 0 || pendingPaxPath !== void 0 || !entries.length || !fileCount) {
|
|
1299
|
+
throw new TypeError("Code source archive is incomplete");
|
|
1300
|
+
}
|
|
1301
|
+
return unwrapRepository(entries, limits.maxPathBytes);
|
|
1302
|
+
}
|
|
1303
|
+
function unwrapRepository(entries, maxPathBytes) {
|
|
1304
|
+
const root = entries[0].path.split("/")[0];
|
|
1305
|
+
if (!root || entries.some((entry) => entry.path !== root && !entry.path.startsWith(`${root}/`))) {
|
|
1306
|
+
throw new TypeError("Code source archive has no single repository root");
|
|
1307
|
+
}
|
|
1308
|
+
const output = [];
|
|
1309
|
+
const kinds = /* @__PURE__ */ new Map();
|
|
1310
|
+
for (const entry of entries) {
|
|
1311
|
+
if (entry.path === root) {
|
|
1312
|
+
if (!entry.directory) throw new TypeError("Code source archive root is not a directory");
|
|
1313
|
+
continue;
|
|
1314
|
+
}
|
|
1315
|
+
const path = entry.path.slice(root.length + 1);
|
|
1316
|
+
validateRepositoryPath(path, maxPathBytes);
|
|
1317
|
+
const parts = path.split("/");
|
|
1318
|
+
for (let index = 1; index < parts.length; index += 1) {
|
|
1319
|
+
if (kinds.get(parts.slice(0, index).join("/")) === "file") throw new TypeError("Code source archive path conflicts");
|
|
1320
|
+
}
|
|
1321
|
+
if (!entry.directory) {
|
|
1322
|
+
for (const existing of kinds.keys()) {
|
|
1323
|
+
if (existing.startsWith(`${path}/`)) throw new TypeError("Code source archive path conflicts");
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
if (kinds.has(path)) throw new TypeError("Code source archive repeats a path");
|
|
1327
|
+
kinds.set(path, entry.directory ? "directory" : "file");
|
|
1328
|
+
output.push({ ...entry, path });
|
|
1329
|
+
}
|
|
1330
|
+
return output.sort((left, right) => left.path.localeCompare(right.path));
|
|
1331
|
+
}
|
|
1332
|
+
function parsePax(bytes, decoder, maxPathBytes) {
|
|
1333
|
+
const result = {};
|
|
1334
|
+
let offset = 0;
|
|
1335
|
+
while (offset < bytes.byteLength) {
|
|
1336
|
+
const space = bytes.indexOf(32, offset);
|
|
1337
|
+
if (space < 0) throw new TypeError("Code source archive PAX header is invalid");
|
|
1338
|
+
const lengthText = ascii(bytes.subarray(offset, space));
|
|
1339
|
+
if (!/^[1-9][0-9]{0,8}$/.test(lengthText)) throw new TypeError("Code source archive PAX length is invalid");
|
|
1340
|
+
const length = Number(lengthText);
|
|
1341
|
+
const end = offset + length;
|
|
1342
|
+
if (!Number.isSafeInteger(length) || end > bytes.byteLength || bytes[end - 1] !== 10) {
|
|
1343
|
+
throw new TypeError("Code source archive PAX record is invalid");
|
|
1344
|
+
}
|
|
1345
|
+
const record3 = decode(bytes.subarray(space + 1, end - 1), decoder);
|
|
1346
|
+
const equals = record3.indexOf("=");
|
|
1347
|
+
if (equals < 1) throw new TypeError("Code source archive PAX field is invalid");
|
|
1348
|
+
const key = record3.slice(0, equals);
|
|
1349
|
+
if (Object.hasOwn(result, key)) throw new TypeError("Code source archive PAX field repeats");
|
|
1350
|
+
result[key] = record3.slice(equals + 1);
|
|
1351
|
+
offset = end;
|
|
1352
|
+
}
|
|
1353
|
+
if (result.path !== void 0) result.path = validateArchivePath(result.path, maxPathBytes);
|
|
1354
|
+
return result;
|
|
1355
|
+
}
|
|
1356
|
+
var aligned = (size) => Math.ceil(size / 512) * 512;
|
|
1357
|
+
var zeroBlock = (block) => block.every((value) => value === 0);
|
|
1358
|
+
function validateChecksum(header) {
|
|
1359
|
+
const expected = tarNumber(header.subarray(148, 156));
|
|
1360
|
+
let actual = 0;
|
|
1361
|
+
for (let index = 0; index < header.length; index += 1) actual += index >= 148 && index < 156 ? 32 : header[index];
|
|
1362
|
+
if (actual !== expected) throw new TypeError("Code source archive checksum is invalid");
|
|
1363
|
+
}
|
|
1364
|
+
function tarNumber(field) {
|
|
1365
|
+
if ((field[0] ?? 0) & 128) throw new TypeError("Code source archive numeric format is unsupported");
|
|
1366
|
+
const value = ascii(field).replaceAll("\0", "").trim();
|
|
1367
|
+
if (!value) return 0;
|
|
1368
|
+
if (!/^[0-7]+$/.test(value)) throw new TypeError("Code source archive number is invalid");
|
|
1369
|
+
const parsed = Number.parseInt(value, 8);
|
|
1370
|
+
if (!Number.isSafeInteger(parsed) || parsed < 0) throw new TypeError("Code source archive number is too large");
|
|
1371
|
+
return parsed;
|
|
1372
|
+
}
|
|
1373
|
+
function tarPath(header, decoder) {
|
|
1374
|
+
const name = decodeNul(header.subarray(0, 100), decoder);
|
|
1375
|
+
const prefix = decodeNul(header.subarray(345, 500), decoder);
|
|
1376
|
+
return prefix ? `${prefix}/${name}` : name;
|
|
1377
|
+
}
|
|
1378
|
+
function decodeNul(value, decoder) {
|
|
1379
|
+
const end = value.indexOf(0);
|
|
1380
|
+
return decode(end < 0 ? value : value.subarray(0, end), decoder);
|
|
1381
|
+
}
|
|
1382
|
+
function decode(value, decoder) {
|
|
1383
|
+
try {
|
|
1384
|
+
return decoder.decode(value);
|
|
1385
|
+
} catch {
|
|
1386
|
+
throw new TypeError("Code source archive text is invalid UTF-8");
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
function ascii(value) {
|
|
1390
|
+
let output = "";
|
|
1391
|
+
for (const byte of value) output += String.fromCharCode(byte);
|
|
1392
|
+
return output;
|
|
1393
|
+
}
|
|
1394
|
+
function validateArchivePath(input, maximum) {
|
|
1395
|
+
const path = input.endsWith("/") ? input.slice(0, -1) : input;
|
|
1396
|
+
validateRepositoryPath(path, maximum);
|
|
1397
|
+
return path;
|
|
1398
|
+
}
|
|
1399
|
+
function validateRepositoryPath(path, maximum) {
|
|
1400
|
+
if (!path || path.startsWith("/") || path.includes("\\") || /[\u0000-\u001f\u007f]/.test(path) || new TextEncoder().encode(path).byteLength > maximum || path.split("/").some((part) => !part || part === "." || part === "..")) {
|
|
1401
|
+
throw new TypeError("Code source archive path is unsafe");
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
function assertZeroTail(bytes, offset) {
|
|
1405
|
+
for (let index = offset; index < bytes.byteLength; index += 1) {
|
|
1406
|
+
if (bytes[index] !== 0) throw new TypeError("Code source archive has data after its end marker");
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
function filteredPath(path) {
|
|
1410
|
+
const parts = path.split("/");
|
|
1411
|
+
return parts.some((part) => RESERVED2.has(part) || SECRET2.test(part));
|
|
1412
|
+
}
|
|
1413
|
+
function nulShare(content) {
|
|
1414
|
+
if (!content.length) return 0;
|
|
1415
|
+
let count = 0;
|
|
1416
|
+
for (let index = 0; index < content.length; index += 1) if (content.charCodeAt(index) === 0) count += 1;
|
|
1417
|
+
return count / content.length;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1083
1420
|
// src/code-runtime-task.ts
|
|
1084
1421
|
function codeCommandMetadata(payload, resume) {
|
|
1085
1422
|
const trusted = record2(payload.trustedBase);
|
|
@@ -1090,12 +1427,17 @@ function codeCommandMetadata(payload, resume) {
|
|
|
1090
1427
|
if (role !== "coding" && role !== "review" || typeof title !== "string" || typeof prompt !== "string") {
|
|
1091
1428
|
throw new TypeError(`invalid Code ${resume ? "resume" : "start"} metadata`);
|
|
1092
1429
|
}
|
|
1430
|
+
if (payload.readOnly !== void 0 && typeof payload.readOnly !== "boolean") {
|
|
1431
|
+
throw new TypeError(`invalid Code ${resume ? "resume" : "start"} read-only capability`);
|
|
1432
|
+
}
|
|
1433
|
+
const readOnly = role === "review" || payload.readOnly === true;
|
|
1093
1434
|
const planning = trusted?.planningInputDigest;
|
|
1094
1435
|
const attestation = trusted?.attestationDigest;
|
|
1095
1436
|
const repository = trusted?.repository;
|
|
1096
1437
|
const baseCommitSha = trusted?.commitSha;
|
|
1097
1438
|
const sourceTreeDigest = trusted?.treeDigest;
|
|
1098
|
-
|
|
1439
|
+
const hostMaterialized = record2(payload.sourceSet) !== null && sourceTreeDigest === void 0;
|
|
1440
|
+
if (typeof repository !== "string" || !repository.includes("/") || typeof baseCommitSha !== "string" || !/^[0-9a-f]{40}$/.test(baseCommitSha) || !hostMaterialized && (typeof sourceTreeDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(sourceTreeDigest))) {
|
|
1099
1441
|
throw new TypeError(`invalid Code ${resume ? "resume" : "start"} trusted base`);
|
|
1100
1442
|
}
|
|
1101
1443
|
if (!Number.isSafeInteger(maxTokensPerInteraction) || Number(maxTokensPerInteraction) < 4e3 || Number(maxTokensPerInteraction) > 2e5) {
|
|
@@ -1103,6 +1445,7 @@ function codeCommandMetadata(payload, resume) {
|
|
|
1103
1445
|
}
|
|
1104
1446
|
return {
|
|
1105
1447
|
role,
|
|
1448
|
+
readOnly,
|
|
1106
1449
|
title,
|
|
1107
1450
|
prompt,
|
|
1108
1451
|
maxTokensPerInteraction: Number(maxTokensPerInteraction),
|
|
@@ -1110,7 +1453,7 @@ function codeCommandMetadata(payload, resume) {
|
|
|
1110
1453
|
attestationDigest: typeof attestation === "string" ? attestation : "resume",
|
|
1111
1454
|
repository,
|
|
1112
1455
|
baseCommitSha,
|
|
1113
|
-
sourceTreeDigest
|
|
1456
|
+
sourceTreeDigest: typeof sourceTreeDigest === "string" ? sourceTreeDigest : null
|
|
1114
1457
|
};
|
|
1115
1458
|
}
|
|
1116
1459
|
function codeLocalSource(payload) {
|
|
@@ -1175,19 +1518,80 @@ async function prepareRuntimeLocalSource(input) {
|
|
|
1175
1518
|
}
|
|
1176
1519
|
|
|
1177
1520
|
// src/code-runtime-source.ts
|
|
1178
|
-
import { mkdir, mkdtemp, rm, writeFile } from "fs/promises";
|
|
1179
|
-
import { tmpdir } from "os";
|
|
1180
|
-
import { dirname, join as
|
|
1181
|
-
|
|
1182
|
-
|
|
1521
|
+
import { mkdir as mkdir3, mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
1522
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
1523
|
+
import { dirname as dirname3, join as join4, resolve as resolve4, sep as sep3 } from "path";
|
|
1524
|
+
|
|
1525
|
+
// src/code-runtime-selected-source.ts
|
|
1526
|
+
import { chmod, cp, mkdir as mkdir2, readdir as readdir2 } from "fs/promises";
|
|
1527
|
+
import { dirname as dirname2, join as join3 } from "path";
|
|
1528
|
+
function selectedSourceSet(payload) {
|
|
1529
|
+
if (!payload.sourceSet) return null;
|
|
1530
|
+
const set = payload.sourceSet && typeof payload.sourceSet === "object" && !Array.isArray(payload.sourceSet) ? payload.sourceSet : null;
|
|
1531
|
+
if (!set) throw new TypeError("Code selected source set is invalid");
|
|
1532
|
+
const parse = (value, primary2) => {
|
|
1533
|
+
const item = value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
1534
|
+
const alias = item?.alias;
|
|
1535
|
+
const repository = item?.repository;
|
|
1536
|
+
const commitSha = item?.commitSha;
|
|
1537
|
+
const materialization = item?.materialization ?? "registry_snapshot";
|
|
1538
|
+
if (typeof alias !== "string" || alias !== (primary2 ? "primary" : alias) || !/^[a-z][a-z0-9-]{0,39}$/.test(alias) || typeof repository !== "string" || !/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repository) || typeof commitSha !== "string" || !/^[0-9a-f]{40}$/.test(commitSha) || !["registry_snapshot", "local_checkout", "host_archive"].includes(String(materialization))) {
|
|
1539
|
+
throw new TypeError("Code selected source identity is invalid");
|
|
1540
|
+
}
|
|
1541
|
+
const visiblePaths = item?.visiblePaths;
|
|
1542
|
+
if (visiblePaths !== void 0 && (!Array.isArray(visiblePaths) || visiblePaths.length > 2e3 || visiblePaths.some((path) => typeof path !== "string" || path.length > 4096))) {
|
|
1543
|
+
throw new TypeError("Code selected source visibility slice is invalid");
|
|
1544
|
+
}
|
|
1545
|
+
return {
|
|
1546
|
+
alias,
|
|
1547
|
+
repository,
|
|
1548
|
+
commitSha,
|
|
1549
|
+
materialization,
|
|
1550
|
+
...visiblePaths ? { visiblePaths } : {}
|
|
1551
|
+
};
|
|
1552
|
+
};
|
|
1553
|
+
if (!Array.isArray(set.references)) throw new TypeError("Code selected source references are invalid");
|
|
1554
|
+
const primary = parse(set.primary, true);
|
|
1555
|
+
const references = set.references.map((item) => parse(item, false));
|
|
1556
|
+
const aliases = /* @__PURE__ */ new Set([primary.alias, ...references.map((item) => item.alias)]);
|
|
1557
|
+
if (aliases.size !== references.length + 1) throw new TypeError("Code selected source aliases repeat");
|
|
1558
|
+
return { primary, references };
|
|
1559
|
+
}
|
|
1560
|
+
async function attachReferenceDirectories(workspace, references) {
|
|
1561
|
+
for (const reference of references) {
|
|
1562
|
+
validateAlias(reference.alias);
|
|
1563
|
+
for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
|
|
1564
|
+
const target = join3(root, ".odla-references", reference.alias);
|
|
1565
|
+
await mkdir2(dirname2(target), { recursive: true });
|
|
1566
|
+
await cp(reference.sourceDir, target, { recursive: true, errorOnExist: true, force: false });
|
|
1567
|
+
await makeTreeReadOnly(target);
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
function validateAlias(alias) {
|
|
1572
|
+
if (!/^[a-z][a-z0-9-]{0,39}$/.test(alias) || alias === "primary") {
|
|
1573
|
+
throw new TypeError("Code reference alias is invalid");
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
async function makeTreeReadOnly(root) {
|
|
1577
|
+
for (const entry of await readdir2(root, { withFileTypes: true })) {
|
|
1578
|
+
const target = join3(root, entry.name);
|
|
1579
|
+
if (entry.isDirectory()) await makeTreeReadOnly(target);
|
|
1580
|
+
else if (entry.isFile()) await chmod(target, 292);
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
// src/code-runtime-source.ts
|
|
1585
|
+
var RESERVED3 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
|
|
1586
|
+
var SECRET3 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
|
|
1183
1587
|
var SOURCE_MAX_FILES = 1e5;
|
|
1184
1588
|
var SOURCE_MAX_BYTES = 80 * 1024 * 1024;
|
|
1185
1589
|
var SOURCE_SET_MAX_BYTES = 480 * 1024 * 1024;
|
|
1186
|
-
async function materializeCodeRuntimeSource(snapshot, tempRoot =
|
|
1590
|
+
async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir2()) {
|
|
1187
1591
|
if (!snapshot.files.length || snapshot.files.length > SOURCE_MAX_FILES) throw new TypeError("Code source file count is invalid");
|
|
1188
|
-
const root = await
|
|
1189
|
-
const sourceDir =
|
|
1190
|
-
await
|
|
1592
|
+
const root = await mkdtemp2(join4(tempRoot, "odla-code-source-"));
|
|
1593
|
+
const sourceDir = join4(root, "source");
|
|
1594
|
+
await mkdir3(sourceDir);
|
|
1191
1595
|
const seen = /* @__PURE__ */ new Set();
|
|
1192
1596
|
let bytes = 0;
|
|
1193
1597
|
try {
|
|
@@ -1197,13 +1601,13 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir()) {
|
|
|
1197
1601
|
seen.add(file.path);
|
|
1198
1602
|
bytes += Buffer.byteLength(file.path) + Buffer.byteLength(file.content);
|
|
1199
1603
|
if (bytes > SOURCE_MAX_BYTES) throw new TypeError("Code source exceeds its byte bound");
|
|
1200
|
-
const target =
|
|
1201
|
-
if (!target.startsWith(`${
|
|
1202
|
-
await
|
|
1203
|
-
await
|
|
1604
|
+
const target = resolve4(sourceDir, file.path);
|
|
1605
|
+
if (!target.startsWith(`${resolve4(sourceDir)}${sep3}`)) throw new TypeError("Code source path escapes its root");
|
|
1606
|
+
await mkdir3(dirname3(target), { recursive: true });
|
|
1607
|
+
await writeFile2(target, file.content, { flag: "wx", mode: 420 });
|
|
1204
1608
|
}
|
|
1205
1609
|
for (const reference of snapshot.references ?? []) {
|
|
1206
|
-
|
|
1610
|
+
validateAlias2(reference.alias);
|
|
1207
1611
|
if (!reference.files.length || reference.files.length > SOURCE_MAX_FILES) throw new TypeError("Code reference file count is invalid");
|
|
1208
1612
|
for (const file of reference.files) {
|
|
1209
1613
|
validatePath(file.path);
|
|
@@ -1212,19 +1616,19 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir()) {
|
|
|
1212
1616
|
seen.add(path);
|
|
1213
1617
|
bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
|
|
1214
1618
|
if (bytes > SOURCE_SET_MAX_BYTES) throw new TypeError("Code source set exceeds its byte bound");
|
|
1215
|
-
const target =
|
|
1216
|
-
if (!target.startsWith(`${
|
|
1217
|
-
await
|
|
1218
|
-
await
|
|
1619
|
+
const target = resolve4(sourceDir, path);
|
|
1620
|
+
if (!target.startsWith(`${resolve4(sourceDir)}${sep3}`)) throw new TypeError("Code reference path escapes its root");
|
|
1621
|
+
await mkdir3(dirname3(target), { recursive: true });
|
|
1622
|
+
await writeFile2(target, file.content, { flag: "wx", mode: 292 });
|
|
1219
1623
|
}
|
|
1220
1624
|
}
|
|
1221
|
-
return { sourceDir, cleanup: () =>
|
|
1625
|
+
return { sourceDir, cleanup: () => rm2(root, { recursive: true, force: true }) };
|
|
1222
1626
|
} catch (cause) {
|
|
1223
|
-
await
|
|
1627
|
+
await rm2(root, { recursive: true, force: true });
|
|
1224
1628
|
throw cause;
|
|
1225
1629
|
}
|
|
1226
1630
|
}
|
|
1227
|
-
function
|
|
1631
|
+
function validateAlias2(alias) {
|
|
1228
1632
|
if (!/^[a-z][a-z0-9-]{0,39}$/.test(alias) || alias === "primary") {
|
|
1229
1633
|
throw new TypeError("Code reference alias is invalid");
|
|
1230
1634
|
}
|
|
@@ -1232,24 +1636,24 @@ function validateAlias(alias) {
|
|
|
1232
1636
|
async function attachCodeRuntimeReferences(workspace, references) {
|
|
1233
1637
|
let bytes = 0;
|
|
1234
1638
|
for (const reference of references) {
|
|
1235
|
-
|
|
1639
|
+
validateAlias2(reference.alias);
|
|
1236
1640
|
for (const file of reference.files) {
|
|
1237
1641
|
validatePath(file.path);
|
|
1238
1642
|
const path = `.odla-references/${reference.alias}/${file.path}`;
|
|
1239
1643
|
bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
|
|
1240
1644
|
if (bytes > SOURCE_SET_MAX_BYTES - SOURCE_MAX_BYTES) throw new TypeError("Code reference set exceeds its byte bound");
|
|
1241
1645
|
for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
|
|
1242
|
-
const target =
|
|
1243
|
-
if (!target.startsWith(`${
|
|
1244
|
-
await
|
|
1245
|
-
await
|
|
1646
|
+
const target = resolve4(root, path);
|
|
1647
|
+
if (!target.startsWith(`${resolve4(root)}${sep3}`)) throw new TypeError("Code reference path escapes its root");
|
|
1648
|
+
await mkdir3(dirname3(target), { recursive: true });
|
|
1649
|
+
await writeFile2(target, file.content, { flag: "wx", mode: 292 });
|
|
1246
1650
|
}
|
|
1247
1651
|
}
|
|
1248
1652
|
}
|
|
1249
1653
|
}
|
|
1250
1654
|
function validatePath(path) {
|
|
1251
1655
|
const parts = path.split("/");
|
|
1252
|
-
if (!path || path.startsWith("/") || path.includes("\\") || path.includes("\0") || parts.some((part) => !part || part === "." || part === ".." ||
|
|
1656
|
+
if (!path || path.startsWith("/") || path.includes("\\") || path.includes("\0") || parts.some((part) => !part || part === "." || part === ".." || RESERVED3.has(part) || SECRET3.test(part))) {
|
|
1253
1657
|
throw new TypeError("Code source contains an unsafe path");
|
|
1254
1658
|
}
|
|
1255
1659
|
}
|
|
@@ -1277,12 +1681,12 @@ async function materializeCommandWorkspace(input) {
|
|
|
1277
1681
|
throw new TypeError("Code selected source set is invalid");
|
|
1278
1682
|
}
|
|
1279
1683
|
if (references.length) {
|
|
1280
|
-
const
|
|
1281
|
-
if (
|
|
1684
|
+
const selected2 = await input.control.source(command.sessionId);
|
|
1685
|
+
if (selected2.repository !== metadata.repository || selected2.commitSha !== metadata.baseCommitSha || selected2.treeDigest !== metadata.sourceTreeDigest) {
|
|
1282
1686
|
await prepared.workspace.cleanup();
|
|
1283
1687
|
throw new TypeError("Code local source does not match the selected GitHub primary source");
|
|
1284
1688
|
}
|
|
1285
|
-
await attachCodeRuntimeReferences(prepared.workspace,
|
|
1689
|
+
await attachCodeRuntimeReferences(prepared.workspace, selected2.references ?? []);
|
|
1286
1690
|
}
|
|
1287
1691
|
}
|
|
1288
1692
|
return {
|
|
@@ -1292,6 +1696,63 @@ async function materializeCommandWorkspace(input) {
|
|
|
1292
1696
|
requestedLocal
|
|
1293
1697
|
};
|
|
1294
1698
|
}
|
|
1699
|
+
const selected = selectedSourceSet(command.payload);
|
|
1700
|
+
if (selected?.primary.materialization === "host_archive") {
|
|
1701
|
+
if (!input.control.sourceArchive) {
|
|
1702
|
+
throw new TypeError("Code runtime protocol does not support host source materialization");
|
|
1703
|
+
}
|
|
1704
|
+
const sources = [selected.primary, ...selected.references];
|
|
1705
|
+
const materialized2 = [];
|
|
1706
|
+
try {
|
|
1707
|
+
for (const descriptor2 of sources) {
|
|
1708
|
+
if (descriptor2.materialization !== "host_archive") {
|
|
1709
|
+
throw new TypeError("Code selected source set mixes incompatible materialization modes");
|
|
1710
|
+
}
|
|
1711
|
+
const archive = await input.control.sourceArchive(command.sessionId, descriptor2.alias);
|
|
1712
|
+
if (archive.repository !== descriptor2.repository || archive.commitSha !== descriptor2.commitSha) {
|
|
1713
|
+
throw new TypeError("Code source archive does not match its selected repository and commit");
|
|
1714
|
+
}
|
|
1715
|
+
const source2 = await materializeCodeRuntimeArchive(
|
|
1716
|
+
archive,
|
|
1717
|
+
descriptor2.visiblePaths ? new Set(descriptor2.visiblePaths) : void 0
|
|
1718
|
+
);
|
|
1719
|
+
const treeDigest = await digestStagedWorkspace(source2.sourceDir, {
|
|
1720
|
+
maxFiles: archive.limits.maxFiles,
|
|
1721
|
+
maxBytes: archive.limits.maxTotalFileBytes
|
|
1722
|
+
});
|
|
1723
|
+
materialized2.push({ descriptor: descriptor2, source: source2, treeDigest });
|
|
1724
|
+
}
|
|
1725
|
+
const primary = materialized2[0];
|
|
1726
|
+
const workspace = resume ? (await restoreCodeWorkspaceCheckpoint({
|
|
1727
|
+
trustedBaseDir: primary.source.sourceDir,
|
|
1728
|
+
trustedBaseCommitSha: primary.descriptor.commitSha,
|
|
1729
|
+
checkpoint: codeCheckpointPayload(command.payload)
|
|
1730
|
+
})).workspace : await stageWorkspace(primary.source.sourceDir, {
|
|
1731
|
+
maxFiles: SOURCE_MAX_FILES,
|
|
1732
|
+
maxBytes: SOURCE_SET_MAX_BYTES
|
|
1733
|
+
});
|
|
1734
|
+
try {
|
|
1735
|
+
await attachReferenceDirectories(workspace, materialized2.slice(1).map((item) => ({
|
|
1736
|
+
alias: item.descriptor.alias,
|
|
1737
|
+
sourceDir: item.source.sourceDir
|
|
1738
|
+
})));
|
|
1739
|
+
} catch (cause) {
|
|
1740
|
+
await workspace.cleanup();
|
|
1741
|
+
throw cause;
|
|
1742
|
+
}
|
|
1743
|
+
return {
|
|
1744
|
+
workspace,
|
|
1745
|
+
sourceDigest: primary.treeDigest,
|
|
1746
|
+
requestedLocal: null,
|
|
1747
|
+
sourceDigests: materialized2.map((item) => ({
|
|
1748
|
+
alias: item.descriptor.alias,
|
|
1749
|
+
treeDigest: item.treeDigest
|
|
1750
|
+
}))
|
|
1751
|
+
};
|
|
1752
|
+
} finally {
|
|
1753
|
+
await Promise.allSettled(materialized2.map((item) => item.source.cleanup()));
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1295
1756
|
const source = await input.control.source(command.sessionId);
|
|
1296
1757
|
const materialized = await materializeCodeRuntimeSource(source);
|
|
1297
1758
|
try {
|
|
@@ -1628,14 +2089,40 @@ async function sessionSkillsFor(options, command) {
|
|
|
1628
2089
|
}
|
|
1629
2090
|
|
|
1630
2091
|
// src/code-runtime-inference.ts
|
|
2092
|
+
var OVERLOAD_RETRY_DELAYS_MS = [2e3, 4e3, 8e3, 16e3];
|
|
2093
|
+
var RETRYABLE_CODES = /* @__PURE__ */ new Set(["control_plane_overloaded", "registry_overloaded", "transport_unavailable"]);
|
|
2094
|
+
function overloadedControlFailure(cause) {
|
|
2095
|
+
return cause instanceof CodeRuntimeControlError && cause.status === 503 && RETRYABLE_CODES.has(cause.code);
|
|
2096
|
+
}
|
|
2097
|
+
async function inferWithBackoff(infer, wait2, onRetry) {
|
|
2098
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
2099
|
+
try {
|
|
2100
|
+
return await infer();
|
|
2101
|
+
} catch (cause) {
|
|
2102
|
+
const delayMs = OVERLOAD_RETRY_DELAYS_MS[attempt];
|
|
2103
|
+
if (delayMs === void 0 || !overloadedControlFailure(cause)) throw cause;
|
|
2104
|
+
await onRetry(cause, delayMs);
|
|
2105
|
+
await wait2(delayMs);
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
1631
2109
|
async function handleCodeRuntimeInference(input) {
|
|
1632
2110
|
const { command, request, state } = input;
|
|
1633
2111
|
const startedAt = Date.now();
|
|
1634
|
-
const
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
2112
|
+
const wait2 = input.wait ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
|
|
2113
|
+
const response2 = await inferWithBackoff(
|
|
2114
|
+
() => input.control.infer(command.sessionId, {
|
|
2115
|
+
requestId: request.requestId,
|
|
2116
|
+
interactionId: command.commandId,
|
|
2117
|
+
call: request.call
|
|
2118
|
+
}),
|
|
2119
|
+
wait2,
|
|
2120
|
+
(cause, delayMs) => input.event({
|
|
2121
|
+
type: "diagnostic",
|
|
2122
|
+
level: "error",
|
|
2123
|
+
message: `Code control plane overloaded (${cause.code}); retrying the model call in ${delayMs / 1e3}s`
|
|
2124
|
+
}).catch(() => void 0)
|
|
2125
|
+
);
|
|
1639
2126
|
state.tokens += response2.receipt.inputTokens + response2.receipt.outputTokens;
|
|
1640
2127
|
const { costUsd } = response2.receipt;
|
|
1641
2128
|
if (costUsd === void 0) state.costKnown = false;
|
|
@@ -1691,8 +2178,8 @@ function createCodeRuntimeInference(options) {
|
|
|
1691
2178
|
|
|
1692
2179
|
// src/code-tool-discovery.ts
|
|
1693
2180
|
import { spawn as spawn3 } from "child_process";
|
|
1694
|
-
import { readFile as readFile2, readdir as
|
|
1695
|
-
import { relative as relative2, resolve as
|
|
2181
|
+
import { readFile as readFile2, readdir as readdir3 } from "fs/promises";
|
|
2182
|
+
import { relative as relative2, resolve as resolve5 } from "path";
|
|
1696
2183
|
var DEFAULT_MAX_FILES = 2e4;
|
|
1697
2184
|
var DEFAULT_MAX_RESULTS = 100;
|
|
1698
2185
|
var DEFAULT_MAX_FILE_BYTES = 512 * 1024;
|
|
@@ -1717,10 +2204,10 @@ function createWorkspaceFileRegistry(limit = DEFAULT_MAX_FILES, enumerate = regi
|
|
|
1717
2204
|
async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
1718
2205
|
const paths = [];
|
|
1719
2206
|
const walk = async (directory) => {
|
|
1720
|
-
for (const entry of await
|
|
2207
|
+
for (const entry of await readdir3(directory, { withFileTypes: true })) {
|
|
1721
2208
|
if (SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
|
|
1722
2209
|
if (entry.isSymbolicLink()) throw new TypeError("workspace contains a symbolic link");
|
|
1723
|
-
const target =
|
|
2210
|
+
const target = resolve5(directory, entry.name);
|
|
1724
2211
|
if (entry.isDirectory()) await walk(target);
|
|
1725
2212
|
else if (entry.isFile()) {
|
|
1726
2213
|
const path = relative2(root, target).split("\\").join("/");
|
|
@@ -1734,7 +2221,7 @@ async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
|
1734
2221
|
}
|
|
1735
2222
|
}
|
|
1736
2223
|
};
|
|
1737
|
-
await walk(
|
|
2224
|
+
await walk(resolve5(root));
|
|
1738
2225
|
return paths.sort();
|
|
1739
2226
|
}
|
|
1740
2227
|
function listWorkspace(paths, options = {}) {
|
|
@@ -1848,7 +2335,7 @@ async function fallbackSearch(root, scoped, options) {
|
|
|
1848
2335
|
if (matches.length >= options.maxResults) break;
|
|
1849
2336
|
let source;
|
|
1850
2337
|
try {
|
|
1851
|
-
source = await readFile2(
|
|
2338
|
+
source = await readFile2(resolve5(root, path));
|
|
1852
2339
|
} catch {
|
|
1853
2340
|
continue;
|
|
1854
2341
|
}
|
|
@@ -2126,7 +2613,7 @@ import { readFile as readFile4, stat } from "fs/promises";
|
|
|
2126
2613
|
|
|
2127
2614
|
// src/code-tool-graph.ts
|
|
2128
2615
|
import { readFile as readFile3 } from "fs/promises";
|
|
2129
|
-
import { join as
|
|
2616
|
+
import { join as join5 } from "path";
|
|
2130
2617
|
import {
|
|
2131
2618
|
hubs,
|
|
2132
2619
|
incident,
|
|
@@ -2140,7 +2627,7 @@ var cache = /* @__PURE__ */ new Map();
|
|
|
2140
2627
|
function workspaceGraphs(workspaceDir, paths) {
|
|
2141
2628
|
const existing = cache.get(workspaceDir);
|
|
2142
2629
|
if (existing) return existing;
|
|
2143
|
-
const read2 = (path) => readFile3(
|
|
2630
|
+
const read2 = (path) => readFile3(join5(workspaceDir, path), "utf8");
|
|
2144
2631
|
const built = (async () => ({
|
|
2145
2632
|
// No knownTables: a staged workspace may not carry migrations, and a filter
|
|
2146
2633
|
// that silently drops every table is worse than an unfiltered one. Callers
|
|
@@ -2763,8 +3250,8 @@ var runtimeErrorMessage = (value) => value instanceof Error ? value.message : St
|
|
|
2763
3250
|
function codeRuntimeAcknowledgementGate(signal) {
|
|
2764
3251
|
let settle;
|
|
2765
3252
|
let settled = false;
|
|
2766
|
-
const ready = new Promise((
|
|
2767
|
-
settle =
|
|
3253
|
+
const ready = new Promise((resolve6) => {
|
|
3254
|
+
settle = resolve6;
|
|
2768
3255
|
});
|
|
2769
3256
|
const release = (run) => {
|
|
2770
3257
|
if (settled) return;
|
|
@@ -2873,7 +3360,7 @@ var TheseusRuntimeEngine = class {
|
|
|
2873
3360
|
async #start(command, resume) {
|
|
2874
3361
|
if (this.#active.has(command.sessionId)) throw new TypeError("Code session is already active on this runtime");
|
|
2875
3362
|
const metadata = codeCommandMetadata(command.payload, resume);
|
|
2876
|
-
const { workspace, sourceDigest, localTrustedBaseDigest, requestedLocal } = await materializeCommandWorkspace({
|
|
3363
|
+
const { workspace, sourceDigest, sourceDigests, localTrustedBaseDigest, requestedLocal } = await materializeCommandWorkspace({
|
|
2877
3364
|
command,
|
|
2878
3365
|
metadata,
|
|
2879
3366
|
resume,
|
|
@@ -2889,11 +3376,12 @@ var TheseusRuntimeEngine = class {
|
|
|
2889
3376
|
acknowledged: false,
|
|
2890
3377
|
startGate,
|
|
2891
3378
|
role: metadata.role,
|
|
3379
|
+
readOnly: metadata.readOnly,
|
|
2892
3380
|
title: metadata.title,
|
|
2893
3381
|
maxTokensPerInteraction: metadata.maxTokensPerInteraction,
|
|
2894
3382
|
baseCommitSha: metadata.baseCommitSha,
|
|
2895
3383
|
repository: metadata.repository,
|
|
2896
|
-
sourceTreeDigest: metadata.sourceTreeDigest,
|
|
3384
|
+
sourceTreeDigest: metadata.sourceTreeDigest ?? sourceDigest,
|
|
2897
3385
|
trustedBaseDigest: requestedLocal ? localTrustedBaseDigest : await digestStagedWorkspace(workspace.baselineDir, {
|
|
2898
3386
|
maxFiles: 2e4,
|
|
2899
3387
|
maxBytes: 512 * 1024 * 1024
|
|
@@ -2919,7 +3407,11 @@ var TheseusRuntimeEngine = class {
|
|
|
2919
3407
|
await this.#failure(command, active, detail);
|
|
2920
3408
|
return null;
|
|
2921
3409
|
});
|
|
2922
|
-
return {
|
|
3410
|
+
return {
|
|
3411
|
+
status: "running",
|
|
3412
|
+
message: resume ? "Theseus resumed from a portable checkpoint" : "Theseus started",
|
|
3413
|
+
...sourceDigests ? { sourceDigests } : {}
|
|
3414
|
+
};
|
|
2923
3415
|
}
|
|
2924
3416
|
/**
|
|
2925
3417
|
* Pursue a goal: attempt, judge with the clean verifier, re-prompt from what
|
|
@@ -2945,6 +3437,7 @@ var TheseusRuntimeEngine = class {
|
|
|
2945
3437
|
event: (event) => this.#event(command, event, active.conversationRefs).then(() => void 0, () => void 0),
|
|
2946
3438
|
attempt: (prompt) => this.#runAttempt(command, {
|
|
2947
3439
|
role: active.role,
|
|
3440
|
+
readOnly: active.readOnly,
|
|
2948
3441
|
title: active.title,
|
|
2949
3442
|
prompt,
|
|
2950
3443
|
maxTokensPerInteraction: active.maxTokensPerInteraction,
|
|
@@ -2987,6 +3480,7 @@ var TheseusRuntimeEngine = class {
|
|
|
2987
3480
|
await this.#takeOver(command, "prompt requires an active Code session");
|
|
2988
3481
|
active.done = this.#runAttempt(command, {
|
|
2989
3482
|
role: active.role,
|
|
3483
|
+
readOnly: active.readOnly,
|
|
2990
3484
|
title: active.title,
|
|
2991
3485
|
prompt,
|
|
2992
3486
|
maxTokensPerInteraction: active.maxTokensPerInteraction,
|
|
@@ -3034,7 +3528,7 @@ var TheseusRuntimeEngine = class {
|
|
|
3034
3528
|
workspaceDir: active.workspace.workspaceDir,
|
|
3035
3529
|
prompt: metadata.prompt,
|
|
3036
3530
|
signal: active.abort.signal,
|
|
3037
|
-
readOnly: metadata.
|
|
3531
|
+
readOnly: metadata.readOnly,
|
|
3038
3532
|
recipeIds: this.options.recipes.map((recipe2) => recipe2.id),
|
|
3039
3533
|
...extraSkills.length ? { extraSkills } : {}
|
|
3040
3534
|
});
|
|
@@ -3123,6 +3617,7 @@ export {
|
|
|
3123
3617
|
verifyCodeCandidate,
|
|
3124
3618
|
prepareRuntimeCheckpoint,
|
|
3125
3619
|
CodeRuntimeCheckpointManager,
|
|
3620
|
+
materializeCodeRuntimeArchive,
|
|
3126
3621
|
materializeCodeRuntimeSource,
|
|
3127
3622
|
attachCodeRuntimeReferences,
|
|
3128
3623
|
materializeCommandWorkspace,
|
|
@@ -3146,4 +3641,4 @@ export {
|
|
|
3146
3641
|
runGoal,
|
|
3147
3642
|
TheseusRuntimeEngine
|
|
3148
3643
|
};
|
|
3149
|
-
//# sourceMappingURL=chunk-
|
|
3644
|
+
//# sourceMappingURL=chunk-WM34GGTK.js.map
|