@ls-stack/agent-eval 0.55.1 → 0.55.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-BD0D9-7k.mjs → app-NI4to6lp.mjs} +4 -4
- package/dist/apps/web/dist/assets/{index-CvsPmlHl.js → index-C7QjETk8.js} +54 -54
- package/dist/apps/web/dist/index.html +1 -1
- package/dist/bin.mjs +1 -1
- package/dist/caseChild.mjs +1 -1
- package/dist/{cli-BR3wMZMx.mjs → cli-Bu9347r1.mjs} +60 -13
- package/dist/index.d.mts +37 -24
- package/dist/index.mjs +3 -3
- package/dist/runChild.mjs +2 -2
- package/dist/{runExecution-Sw38bCaq.mjs → runExecution-C31dpemR.mjs} +363 -114
- package/dist/{runOrchestration-DJsdLYeZ.mjs → runOrchestration-3RoHLW4U.mjs} +1 -1
- package/dist/{runner-72rsqJRq.mjs → runner-B4EfMn1d.mjs} +2 -2
- package/dist/{runner-dB69WsnM.mjs → runner-CTp9zHbM.mjs} +1 -1
- package/dist/{src-hBGtzWuA.mjs → src-FR60ZR_4.mjs} +2 -2
- package/package.json +3 -3
- package/skills/agent-eval/SKILL.md +19 -8
|
@@ -275,12 +275,15 @@ const traceCacheRefSchema = z.object({
|
|
|
275
275
|
z.object({
|
|
276
276
|
key: z.string(),
|
|
277
277
|
namespace: z.string(),
|
|
278
|
-
operationType: cacheOperationTypeSchema,
|
|
279
|
-
operationName: z.string(),
|
|
280
|
-
spanName: z.string().optional(),
|
|
281
|
-
spanKind: traceSpanKindSchema.optional(),
|
|
282
278
|
storedAt: z.string(),
|
|
283
|
-
|
|
279
|
+
lastAccessedAt: z.string()
|
|
280
|
+
});
|
|
281
|
+
z.object({
|
|
282
|
+
removedCacheFiles: z.number(),
|
|
283
|
+
removedDebugFiles: z.number(),
|
|
284
|
+
removedBlobFiles: z.number(),
|
|
285
|
+
removedIndexRows: z.number(),
|
|
286
|
+
rewrittenIndexes: z.number()
|
|
284
287
|
});
|
|
285
288
|
/** Zod schema for `SerializedCacheSpan`, defined lazily for recursion. */
|
|
286
289
|
const serializedCacheSpanSchema = z.object({
|
|
@@ -1377,6 +1380,7 @@ const agentEvalsConfigSchema = z.object({
|
|
|
1377
1380
|
dir: z.string().optional(),
|
|
1378
1381
|
maxEntriesPerNamespace: z.preprocess((value) => typeof value === "number" && Number.isFinite(value) ? value : void 0, z.number().optional()),
|
|
1379
1382
|
maxEntriesByNamespace: z.record(z.string(), z.number()).optional(),
|
|
1383
|
+
pruneIdleDelayMs: z.preprocess((value) => typeof value === "number" && Number.isFinite(value) ? value : void 0, z.number().optional()),
|
|
1380
1384
|
maxEntriesPerEval: z.preprocess((value) => typeof value === "number" && Number.isFinite(value) ? value : void 0, z.number().optional())
|
|
1381
1385
|
}).optional()
|
|
1382
1386
|
});
|
|
@@ -4926,8 +4930,10 @@ const cacheSerializationMarker = "__aecs";
|
|
|
4926
4930
|
const supportedCacheSerializationPrefix = "v1:";
|
|
4927
4931
|
const externalJsonCacheSerializationMarker = "v1:ExternalJson";
|
|
4928
4932
|
const externalJsonBlobExtension = ".json.br";
|
|
4933
|
+
const externalJsonBlobDirName = "cache-blobs";
|
|
4929
4934
|
const cacheEntryExtension = ".json.br";
|
|
4930
4935
|
const debugEntryExtension = ".json";
|
|
4936
|
+
const cacheIndexFilePrefix = ".index-";
|
|
4931
4937
|
async function commitPendingCacheWrites(params) {
|
|
4932
4938
|
for (const pendingWrite of params.pendingWrites) await params.backingStore.write(pendingWrite.entry, pendingWrite.debugKey);
|
|
4933
4939
|
}
|
|
@@ -4941,8 +4947,14 @@ async function commitPendingCacheWrites(params) {
|
|
|
4941
4947
|
function createFsCacheStore(options) {
|
|
4942
4948
|
const cacheDir = resolve(options.workspaceRoot, options.dir ?? ".agent-evals/cache");
|
|
4943
4949
|
const debugDir = resolve(options.workspaceRoot, options.debugDir ?? ".agent-evals/cache-debug");
|
|
4944
|
-
const blobDir = resolve(options.workspaceRoot, options.blobDir
|
|
4945
|
-
const
|
|
4950
|
+
const blobDir = options.blobDir === void 0 ? join(cacheDir, externalJsonBlobDirName) : resolve(options.workspaceRoot, options.blobDir);
|
|
4951
|
+
const legacyBlobDir = resolve(options.workspaceRoot, ".agent-evals/cache-blobs");
|
|
4952
|
+
const fallbackBlobDirs = options.blobDir === void 0 && legacyBlobDir !== blobDir ? [legacyBlobDir] : [];
|
|
4953
|
+
const blobDirs = [blobDir, ...fallbackBlobDirs];
|
|
4954
|
+
const externalJsonStore = createExternalJsonBlobStore({
|
|
4955
|
+
fallbackDirs: fallbackBlobDirs,
|
|
4956
|
+
primaryDir: blobDir
|
|
4957
|
+
});
|
|
4946
4958
|
const defaultMaxEntries = normalizeMaxEntries(options.maxEntriesPerNamespace);
|
|
4947
4959
|
return {
|
|
4948
4960
|
externalJsonStore,
|
|
@@ -4956,11 +4968,22 @@ function createFsCacheStore(options) {
|
|
|
4956
4968
|
return blobDir;
|
|
4957
4969
|
},
|
|
4958
4970
|
async lookup(namespace, keyHash) {
|
|
4959
|
-
const entry = await
|
|
4960
|
-
|
|
4971
|
+
const entry = await readIndexedCacheEntry({
|
|
4972
|
+
cacheDir,
|
|
4973
|
+
key: keyHash,
|
|
4974
|
+
namespace
|
|
4975
|
+
});
|
|
4976
|
+
if (entry === null) return null;
|
|
4977
|
+
const materialized = await materializeExternalJsonCacheEntryOrNull(entry, externalJsonStore);
|
|
4978
|
+
if (materialized !== null) await updateCacheIndexLastAccessedAt(cacheDir, namespace, keyHash);
|
|
4979
|
+
return materialized;
|
|
4961
4980
|
},
|
|
4962
4981
|
async lookupWithDebug(namespace, keyHash) {
|
|
4963
|
-
const rawEntry = await
|
|
4982
|
+
const rawEntry = await readIndexedCacheEntry({
|
|
4983
|
+
cacheDir,
|
|
4984
|
+
key: keyHash,
|
|
4985
|
+
namespace
|
|
4986
|
+
});
|
|
4964
4987
|
if (rawEntry === null) return null;
|
|
4965
4988
|
const entry = await materializeExternalJsonCacheEntryOrNull(rawEntry, externalJsonStore);
|
|
4966
4989
|
if (entry === null) return null;
|
|
@@ -4975,8 +4998,17 @@ function createFsCacheStore(options) {
|
|
|
4975
4998
|
};
|
|
4976
4999
|
},
|
|
4977
5000
|
async write(entry, debugKey) {
|
|
4978
|
-
|
|
4979
|
-
|
|
5001
|
+
await withCacheFileLock(namespaceLockPath(cacheDir, entry.namespace), async () => {
|
|
5002
|
+
await writeCompressedCacheEntry(cacheDir, entry);
|
|
5003
|
+
if (!usesSupportedCacheSerialization(entry.recording)) return;
|
|
5004
|
+
const index = await readNamespaceIndex(cacheDir, entry.namespace);
|
|
5005
|
+
index.entries[entry.key] = {
|
|
5006
|
+
storedAt: entry.storedAt,
|
|
5007
|
+
lastAccessedAt: entry.storedAt,
|
|
5008
|
+
blobRefs: await collectExternalJsonBlobRefs(entry, blobDirs)
|
|
5009
|
+
};
|
|
5010
|
+
await writeNamespaceIndex(cacheDir, index);
|
|
5011
|
+
});
|
|
4980
5012
|
if (debugKey !== void 0) {
|
|
4981
5013
|
if ((await resultify(() => writeDebugKeyEntry({
|
|
4982
5014
|
debugDir,
|
|
@@ -4987,36 +5019,11 @@ function createFsCacheStore(options) {
|
|
|
4987
5019
|
key: entry.key
|
|
4988
5020
|
}));
|
|
4989
5021
|
}
|
|
4990
|
-
await pruneEntriesForNamespace({
|
|
4991
|
-
cacheDir,
|
|
4992
|
-
debugDir,
|
|
4993
|
-
namespace: entry.namespace,
|
|
4994
|
-
maxEntries,
|
|
4995
|
-
protectedKey: entry.key
|
|
4996
|
-
});
|
|
4997
|
-
await pruneExternalJsonBlobs(cacheDir, blobDir);
|
|
4998
5022
|
},
|
|
4999
5023
|
async list() {
|
|
5000
|
-
const files = await listCacheEntryFiles(cacheDir);
|
|
5001
5024
|
const items = [];
|
|
5002
|
-
for (const
|
|
5003
|
-
|
|
5004
|
-
if (fileEntry === null || !entryMatchesPath(filePath, fileEntry.entry)) continue;
|
|
5005
|
-
const entry = fileEntry.entry;
|
|
5006
|
-
const operationType = entry.operationType ?? "span";
|
|
5007
|
-
const operationName = entry.operationName ?? entry.spanName ?? entry.namespace;
|
|
5008
|
-
items.push({
|
|
5009
|
-
key: entry.key,
|
|
5010
|
-
namespace: entry.namespace,
|
|
5011
|
-
operationType,
|
|
5012
|
-
operationName,
|
|
5013
|
-
spanName: entry.spanName,
|
|
5014
|
-
spanKind: entry.spanKind,
|
|
5015
|
-
storedAt: entry.storedAt,
|
|
5016
|
-
sizeBytes: fileEntry.sizeBytes
|
|
5017
|
-
});
|
|
5018
|
-
}
|
|
5019
|
-
items.sort((a, b) => a.storedAt < b.storedAt ? 1 : -1);
|
|
5025
|
+
for (const index of await listCacheIndexes(cacheDir)) for (const [key, entry] of Object.entries(index.entries)) items.push(toCacheListItem(index.namespace, key, entry));
|
|
5026
|
+
items.sort((a, b) => a.lastAccessedAt < b.lastAccessedAt ? 1 : -1);
|
|
5020
5027
|
return items;
|
|
5021
5028
|
},
|
|
5022
5029
|
async clear(filter) {
|
|
@@ -5029,21 +5036,46 @@ function createFsCacheStore(options) {
|
|
|
5029
5036
|
recursive: true,
|
|
5030
5037
|
force: true
|
|
5031
5038
|
});
|
|
5032
|
-
await rm(
|
|
5039
|
+
await Promise.all(blobDirs.map((dir) => rm(dir, {
|
|
5033
5040
|
recursive: true,
|
|
5034
5041
|
force: true
|
|
5035
|
-
});
|
|
5042
|
+
})));
|
|
5036
5043
|
return;
|
|
5037
5044
|
}
|
|
5038
5045
|
if (filter.namespace !== void 0) {
|
|
5039
5046
|
await clearCacheEntries(cacheDir, filter);
|
|
5040
5047
|
await clearDebugEntries(debugDir, filter);
|
|
5041
|
-
await
|
|
5048
|
+
await pruneUnreferencedExternalJsonBlobs(cacheDir, blobDirs);
|
|
5042
5049
|
return;
|
|
5043
5050
|
}
|
|
5044
5051
|
await clearCacheEntries(cacheDir, filter);
|
|
5045
5052
|
await clearDebugEntries(debugDir, filter);
|
|
5046
|
-
await
|
|
5053
|
+
await pruneUnreferencedExternalJsonBlobs(cacheDir, blobDirs);
|
|
5054
|
+
},
|
|
5055
|
+
async pruneExternalJsonBlobs() {
|
|
5056
|
+
await pruneUnreferencedExternalJsonBlobs(cacheDir, blobDirs);
|
|
5057
|
+
},
|
|
5058
|
+
async pruneRetention() {
|
|
5059
|
+
for (const index_ of await listCacheIndexes(cacheDir)) {
|
|
5060
|
+
const namespace = index_.namespace;
|
|
5061
|
+
const maxEntries = maxEntriesForNamespace(namespace, defaultMaxEntries, options.maxEntriesByNamespace);
|
|
5062
|
+
const keptKeys = await withCacheFileLock(namespaceLockPath(cacheDir, namespace), async () => {
|
|
5063
|
+
return pruneCacheEntriesForNamespace({
|
|
5064
|
+
cacheDir,
|
|
5065
|
+
index: await readNamespaceIndex(cacheDir, namespace),
|
|
5066
|
+
maxEntries
|
|
5067
|
+
});
|
|
5068
|
+
});
|
|
5069
|
+
await withCacheFileLock(namespaceLockPath(debugDir, namespace), () => pruneDebugEntriesForNamespace(debugDir, namespace, keptKeys));
|
|
5070
|
+
}
|
|
5071
|
+
await pruneUnreferencedExternalJsonBlobs(cacheDir, blobDirs);
|
|
5072
|
+
},
|
|
5073
|
+
async repair() {
|
|
5074
|
+
return repairIndexedCache({
|
|
5075
|
+
blobDirs,
|
|
5076
|
+
cacheDir,
|
|
5077
|
+
debugDir
|
|
5078
|
+
});
|
|
5047
5079
|
}
|
|
5048
5080
|
};
|
|
5049
5081
|
}
|
|
@@ -5126,11 +5158,31 @@ function entryPath(params) {
|
|
|
5126
5158
|
if (filePath !== namespaceDir && !filePath.startsWith(`${namespaceDir}${sep}`)) throw new Error(`Cache entry key escapes namespace directory: ${params.key}`);
|
|
5127
5159
|
return filePath;
|
|
5128
5160
|
}
|
|
5129
|
-
|
|
5130
|
-
return (
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5161
|
+
function cacheIndexPath(cacheDir, namespace) {
|
|
5162
|
+
return join(namespaceDirPath(cacheDir, namespace), `${cacheIndexFilePrefix}${hashNamespace(namespace)}${debugEntryExtension}`);
|
|
5163
|
+
}
|
|
5164
|
+
async function readIndexedCacheEntry(params) {
|
|
5165
|
+
return withCacheFileLock(namespaceLockPath(params.cacheDir, params.namespace), async () => {
|
|
5166
|
+
if ((await readNamespaceIndex(params.cacheDir, params.namespace)).entries[params.key] === void 0) return null;
|
|
5167
|
+
const fileEntry = await readCacheEntryFilePath(cacheEntryPath(params.cacheDir, params.namespace, params.key), {
|
|
5168
|
+
namespace: params.namespace,
|
|
5169
|
+
key: params.key
|
|
5170
|
+
});
|
|
5171
|
+
if (fileEntry === null) return null;
|
|
5172
|
+
return fileEntry.entry;
|
|
5173
|
+
});
|
|
5174
|
+
}
|
|
5175
|
+
async function updateCacheIndexLastAccessedAt(cacheDir, namespace, key) {
|
|
5176
|
+
await withCacheFileLock(namespaceLockPath(cacheDir, namespace), async () => {
|
|
5177
|
+
const index = await readNamespaceIndex(cacheDir, namespace);
|
|
5178
|
+
const entry = index.entries[key];
|
|
5179
|
+
if (entry === void 0) return;
|
|
5180
|
+
index.entries[key] = {
|
|
5181
|
+
...entry,
|
|
5182
|
+
lastAccessedAt: new Date(getRealDateNowMs()).toISOString()
|
|
5183
|
+
};
|
|
5184
|
+
await writeNamespaceIndex(cacheDir, index);
|
|
5185
|
+
});
|
|
5134
5186
|
}
|
|
5135
5187
|
async function readCacheEntryFilePath(filePath, expected) {
|
|
5136
5188
|
if (!existsSync(filePath)) return null;
|
|
@@ -5145,10 +5197,7 @@ async function readCacheEntryFilePath(filePath, expected) {
|
|
|
5145
5197
|
const entry = parsed.data;
|
|
5146
5198
|
if (!usesSupportedCacheSerialization(entry.recording)) return null;
|
|
5147
5199
|
if (expected !== void 0 && (entry.namespace !== expected.namespace || entry.key !== expected.key)) return null;
|
|
5148
|
-
return {
|
|
5149
|
-
entry,
|
|
5150
|
-
sizeBytes: compressedResult.value.byteLength
|
|
5151
|
-
};
|
|
5200
|
+
return { entry };
|
|
5152
5201
|
}
|
|
5153
5202
|
async function writeCompressedCacheEntry(cacheDir, entry) {
|
|
5154
5203
|
const filePath = cacheEntryPath(cacheDir, entry.namespace, entry.key);
|
|
@@ -5197,23 +5246,132 @@ async function writeAtomicFile(filePath, contents) {
|
|
|
5197
5246
|
await writeFile(tmpPath, contents);
|
|
5198
5247
|
await rename(tmpPath, filePath);
|
|
5199
5248
|
}
|
|
5249
|
+
const emptyCacheIndex = (namespace) => ({
|
|
5250
|
+
version: 1,
|
|
5251
|
+
namespace,
|
|
5252
|
+
entries: {}
|
|
5253
|
+
});
|
|
5254
|
+
async function readNamespaceIndex(cacheDir, namespace) {
|
|
5255
|
+
const indexPath = cacheIndexPath(cacheDir, namespace);
|
|
5256
|
+
if (!existsSync(indexPath)) return emptyCacheIndex(namespace);
|
|
5257
|
+
const rawResult = await resultify(() => readFile(indexPath, "utf8"));
|
|
5258
|
+
if (rawResult.error) return emptyCacheIndex(namespace);
|
|
5259
|
+
return parseCacheIndexFile(safeJsonParse(rawResult.value), namespace) ?? emptyCacheIndex(namespace);
|
|
5260
|
+
}
|
|
5261
|
+
async function writeNamespaceIndex(cacheDir, index) {
|
|
5262
|
+
const entries = Object.entries(index.entries);
|
|
5263
|
+
if (entries.length === 0) {
|
|
5264
|
+
await rm(cacheIndexPath(cacheDir, index.namespace), { force: true });
|
|
5265
|
+
await removeDirIfEmpty(namespaceDirPath(cacheDir, index.namespace));
|
|
5266
|
+
return;
|
|
5267
|
+
}
|
|
5268
|
+
const sortedEntries = entries.toSorted(([a], [b]) => a < b ? -1 : 1);
|
|
5269
|
+
const normalizedEntries = Object.fromEntries(sortedEntries.map(([key, entry]) => [key, entry]));
|
|
5270
|
+
await writeAtomicFile(cacheIndexPath(cacheDir, index.namespace), JSON.stringify({
|
|
5271
|
+
...index,
|
|
5272
|
+
entries: normalizedEntries
|
|
5273
|
+
}, null, 2));
|
|
5274
|
+
}
|
|
5275
|
+
async function listCacheIndexes(cacheDir) {
|
|
5276
|
+
if (!existsSync(cacheDir)) return [];
|
|
5277
|
+
const entriesResult = await resultify(() => readdir(cacheDir, { withFileTypes: true }));
|
|
5278
|
+
if (entriesResult.error) return [];
|
|
5279
|
+
const records = [];
|
|
5280
|
+
for (const entry of entriesResult.value) {
|
|
5281
|
+
if (!entry.isDirectory()) continue;
|
|
5282
|
+
const namespaceDir = join(cacheDir, entry.name);
|
|
5283
|
+
for (const indexFilePath of await listCacheIndexFiles(namespaceDir)) {
|
|
5284
|
+
const rawResult = await resultify(() => readFile(indexFilePath, "utf8"));
|
|
5285
|
+
if (rawResult.error) continue;
|
|
5286
|
+
const parsed = parseCacheIndexFile(safeJsonParse(rawResult.value));
|
|
5287
|
+
if (parsed === null) continue;
|
|
5288
|
+
records.push(parsed);
|
|
5289
|
+
}
|
|
5290
|
+
}
|
|
5291
|
+
return records;
|
|
5292
|
+
}
|
|
5293
|
+
function hashNamespace(namespace) {
|
|
5294
|
+
return createHash("sha256").update(namespace).digest("hex");
|
|
5295
|
+
}
|
|
5296
|
+
function parseCacheIndexFile(value, expectedNamespace) {
|
|
5297
|
+
if (!isRecordLike(value)) return null;
|
|
5298
|
+
if (value.version !== 1 || typeof value.namespace !== "string") return null;
|
|
5299
|
+
if (expectedNamespace !== void 0 && value.namespace !== expectedNamespace) return null;
|
|
5300
|
+
if (!isRecordLike(value.entries)) return null;
|
|
5301
|
+
const entries = {};
|
|
5302
|
+
for (const [key, entryValue] of Object.entries(value.entries)) {
|
|
5303
|
+
const entry = parseCacheIndexEntry(entryValue);
|
|
5304
|
+
if (entry === null) return null;
|
|
5305
|
+
entries[key] = entry;
|
|
5306
|
+
}
|
|
5307
|
+
return {
|
|
5308
|
+
version: 1,
|
|
5309
|
+
namespace: value.namespace,
|
|
5310
|
+
entries
|
|
5311
|
+
};
|
|
5312
|
+
}
|
|
5313
|
+
function parseCacheIndexEntry(value) {
|
|
5314
|
+
if (!isRecordLike(value)) return null;
|
|
5315
|
+
if (typeof value.storedAt !== "string" || typeof value.lastAccessedAt !== "string") return null;
|
|
5316
|
+
if (!Array.isArray(value.blobRefs)) return null;
|
|
5317
|
+
const blobRefs = [];
|
|
5318
|
+
for (const blobRef of value.blobRefs) {
|
|
5319
|
+
if (typeof blobRef !== "string") return null;
|
|
5320
|
+
blobRefs.push(blobRef);
|
|
5321
|
+
}
|
|
5322
|
+
return {
|
|
5323
|
+
storedAt: value.storedAt,
|
|
5324
|
+
lastAccessedAt: value.lastAccessedAt,
|
|
5325
|
+
blobRefs
|
|
5326
|
+
};
|
|
5327
|
+
}
|
|
5328
|
+
function toCacheListItem(namespace, key, entry) {
|
|
5329
|
+
return {
|
|
5330
|
+
key,
|
|
5331
|
+
namespace,
|
|
5332
|
+
storedAt: entry.storedAt,
|
|
5333
|
+
lastAccessedAt: entry.lastAccessedAt
|
|
5334
|
+
};
|
|
5335
|
+
}
|
|
5336
|
+
function keyFromEntryFilePath(filePath, extension) {
|
|
5337
|
+
const name = basename(filePath);
|
|
5338
|
+
if (!name.endsWith(extension)) return null;
|
|
5339
|
+
return name.slice(0, -extension.length);
|
|
5340
|
+
}
|
|
5341
|
+
function debugNamespaceFromPath(debugDir, filePath) {
|
|
5342
|
+
return basename(dirname(resolve(debugDir, relative(debugDir, filePath))));
|
|
5343
|
+
}
|
|
5200
5344
|
async function clearCacheEntries(cacheDir, filter) {
|
|
5201
|
-
const
|
|
5202
|
-
for (const
|
|
5203
|
-
const
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5345
|
+
const indexes = filter.namespace === void 0 ? await listCacheIndexes(cacheDir) : [await readNamespaceIndex(cacheDir, filter.namespace)];
|
|
5346
|
+
for (const record of indexes) {
|
|
5347
|
+
const namespace = record.namespace;
|
|
5348
|
+
await withCacheFileLock(namespaceLockPath(cacheDir, namespace), async () => {
|
|
5349
|
+
const index = await readNamespaceIndex(cacheDir, namespace);
|
|
5350
|
+
const matchingKeys = Object.keys(index.entries).filter((key) => {
|
|
5351
|
+
return index.entries[key] !== void 0 && entryMatchesFilter({
|
|
5352
|
+
namespace,
|
|
5353
|
+
key
|
|
5354
|
+
}, filter);
|
|
5355
|
+
});
|
|
5356
|
+
for (const key of matchingKeys) {
|
|
5357
|
+
await rm(cacheEntryPath(cacheDir, namespace, key), { force: true });
|
|
5358
|
+
delete index.entries[key];
|
|
5359
|
+
}
|
|
5360
|
+
await writeNamespaceIndex(cacheDir, index);
|
|
5361
|
+
});
|
|
5208
5362
|
}
|
|
5209
|
-
if (filter.namespace !== void 0) await removeDirIfEmpty(namespaceDirPath(cacheDir, filter.namespace));
|
|
5210
5363
|
}
|
|
5211
5364
|
async function clearDebugEntries(debugDir, filter) {
|
|
5212
|
-
const files = filter.namespace === void 0 ?
|
|
5365
|
+
const files = await listDebugEntryFiles(filter.namespace === void 0 ? debugDir : namespaceDirPath(debugDir, filter.namespace));
|
|
5213
5366
|
for (const filePath of files) {
|
|
5214
|
-
const
|
|
5215
|
-
|
|
5216
|
-
|
|
5367
|
+
const namespace = filter.namespace === void 0 ? debugNamespaceFromPath(debugDir, filePath) : filter.namespace;
|
|
5368
|
+
const key = keyFromEntryFilePath(filePath, debugEntryExtension);
|
|
5369
|
+
if (key === null) continue;
|
|
5370
|
+
if (!entryMatchesFilter({
|
|
5371
|
+
namespace,
|
|
5372
|
+
key
|
|
5373
|
+
}, filter)) continue;
|
|
5374
|
+
await withCacheFileLock(namespaceLockPath(debugDir, namespace), () => rm(filePath, { force: true }));
|
|
5217
5375
|
}
|
|
5218
5376
|
if (filter.namespace !== void 0) await removeDirIfEmpty(namespaceDirPath(debugDir, filter.namespace));
|
|
5219
5377
|
}
|
|
@@ -5221,49 +5379,86 @@ function entryMatchesFilter(entry, filter) {
|
|
|
5221
5379
|
if (filter.namespace !== void 0 && entry.namespace !== filter.namespace) return false;
|
|
5222
5380
|
return filter.key === void 0 || entry.key === filter.key;
|
|
5223
5381
|
}
|
|
5224
|
-
async function
|
|
5225
|
-
const { cacheDir,
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
await withCacheFileLock(namespaceLockPath(debugDir, namespace), () => pruneDebugEntriesForNamespace(debugDir, namespace, keptKeys));
|
|
5229
|
-
});
|
|
5230
|
-
}
|
|
5231
|
-
async function pruneCacheEntriesForNamespace(cacheDir, namespace, maxEntries, protectedKey) {
|
|
5232
|
-
const entries = await listCacheEntriesForNamespace(cacheDir, namespace);
|
|
5233
|
-
const sorted = entries.toSorted((a, b) => a.entry.storedAt < b.entry.storedAt ? 1 : -1);
|
|
5382
|
+
async function pruneCacheEntriesForNamespace(params) {
|
|
5383
|
+
const { cacheDir, index, maxEntries } = params;
|
|
5384
|
+
const entries = Object.entries(index.entries);
|
|
5385
|
+
const sorted = entries.toSorted(([, a], [, b]) => a.lastAccessedAt < b.lastAccessedAt ? 1 : -1);
|
|
5234
5386
|
const keptKeys = /* @__PURE__ */ new Set();
|
|
5235
|
-
|
|
5236
|
-
if (protectedEntry !== void 0) keptKeys.add(protectedEntry.entry.key);
|
|
5237
|
-
for (const item of sorted) {
|
|
5387
|
+
for (const [key] of sorted) {
|
|
5238
5388
|
if (keptKeys.size >= maxEntries) break;
|
|
5239
|
-
keptKeys.add(
|
|
5389
|
+
keptKeys.add(key);
|
|
5240
5390
|
}
|
|
5241
|
-
for (const
|
|
5242
|
-
|
|
5391
|
+
for (const [key] of entries) if (!keptKeys.has(key)) {
|
|
5392
|
+
await rm(cacheEntryPath(cacheDir, index.namespace, key), { force: true });
|
|
5393
|
+
delete index.entries[key];
|
|
5394
|
+
}
|
|
5395
|
+
await writeNamespaceIndex(cacheDir, index);
|
|
5243
5396
|
return keptKeys;
|
|
5244
5397
|
}
|
|
5245
5398
|
async function pruneDebugEntriesForNamespace(debugDir, namespace, keptKeys) {
|
|
5246
5399
|
const files = await listDebugEntryFiles(namespaceDirPath(debugDir, namespace));
|
|
5247
5400
|
for (const filePath of files) {
|
|
5248
|
-
const
|
|
5249
|
-
if (
|
|
5401
|
+
const key = keyFromEntryFilePath(filePath, debugEntryExtension);
|
|
5402
|
+
if (key !== null && !keptKeys.has(key)) await rm(filePath, { force: true });
|
|
5250
5403
|
}
|
|
5251
5404
|
await removeDirIfEmpty(namespaceDirPath(debugDir, namespace));
|
|
5252
5405
|
}
|
|
5253
|
-
async function
|
|
5254
|
-
const
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5406
|
+
async function repairIndexedCache(params) {
|
|
5407
|
+
const summary = {
|
|
5408
|
+
removedCacheFiles: 0,
|
|
5409
|
+
removedDebugFiles: 0,
|
|
5410
|
+
removedBlobFiles: 0,
|
|
5411
|
+
removedIndexRows: 0,
|
|
5412
|
+
rewrittenIndexes: 0
|
|
5413
|
+
};
|
|
5414
|
+
for (const index_ of await listCacheIndexes(params.cacheDir)) {
|
|
5415
|
+
const result = await withCacheFileLock(namespaceLockPath(params.cacheDir, index_.namespace), async () => {
|
|
5416
|
+
const index = await readNamespaceIndex(params.cacheDir, index_.namespace);
|
|
5417
|
+
let removedRows = 0;
|
|
5418
|
+
for (const key of Object.keys(index.entries)) if (!existsSync(cacheEntryPath(params.cacheDir, index.namespace, key))) {
|
|
5419
|
+
delete index.entries[key];
|
|
5420
|
+
removedRows++;
|
|
5421
|
+
}
|
|
5422
|
+
if (removedRows === 0) return {
|
|
5423
|
+
removedRows,
|
|
5424
|
+
rewritten: false
|
|
5425
|
+
};
|
|
5426
|
+
await writeNamespaceIndex(params.cacheDir, index);
|
|
5427
|
+
return {
|
|
5428
|
+
removedRows,
|
|
5429
|
+
rewritten: true
|
|
5430
|
+
};
|
|
5261
5431
|
});
|
|
5432
|
+
summary.removedIndexRows += result.removedRows;
|
|
5433
|
+
if (result.rewritten) summary.rewrittenIndexes++;
|
|
5262
5434
|
}
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5435
|
+
const indexes = await listCacheIndexes(params.cacheDir);
|
|
5436
|
+
const indexedCacheFiles = /* @__PURE__ */ new Set();
|
|
5437
|
+
const indexedDebugFiles = /* @__PURE__ */ new Set();
|
|
5438
|
+
const indexedBlobRefs = /* @__PURE__ */ new Set();
|
|
5439
|
+
for (const index_ of indexes) for (const [key, entry] of Object.entries(index_.entries)) {
|
|
5440
|
+
indexedCacheFiles.add(cacheEntryPath(params.cacheDir, index_.namespace, key));
|
|
5441
|
+
indexedDebugFiles.add(debugEntryPath(params.debugDir, index_.namespace, key));
|
|
5442
|
+
for (const blobRef of entry.blobRefs) indexedBlobRefs.add(blobRef);
|
|
5443
|
+
}
|
|
5444
|
+
for (const filePath of await listCacheEntryFiles(params.cacheDir, "allNamespaces")) if (!indexedCacheFiles.has(filePath)) {
|
|
5445
|
+
await rm(filePath, { force: true });
|
|
5446
|
+
summary.removedCacheFiles++;
|
|
5447
|
+
await removeDirIfEmpty(dirname(filePath));
|
|
5448
|
+
}
|
|
5449
|
+
for (const filePath of await listDebugEntryFiles(params.debugDir)) if (!indexedDebugFiles.has(filePath)) {
|
|
5450
|
+
await rm(filePath, { force: true });
|
|
5451
|
+
summary.removedDebugFiles++;
|
|
5452
|
+
await removeDirIfEmpty(dirname(filePath));
|
|
5453
|
+
}
|
|
5454
|
+
for (const blobDir of params.blobDirs) {
|
|
5455
|
+
if (!existsSync(blobDir)) continue;
|
|
5456
|
+
for (const blobRef of await listExternalJsonBlobPaths(blobDir)) if (!indexedBlobRefs.has(blobRef)) {
|
|
5457
|
+
await rm(resolveStorePath(blobDir, blobRef), { force: true });
|
|
5458
|
+
summary.removedBlobFiles++;
|
|
5459
|
+
}
|
|
5460
|
+
}
|
|
5461
|
+
return summary;
|
|
5267
5462
|
}
|
|
5268
5463
|
function usesSupportedCacheSerialization(value) {
|
|
5269
5464
|
if (Array.isArray(value)) return value.every(usesSupportedCacheSerialization);
|
|
@@ -5271,14 +5466,14 @@ function usesSupportedCacheSerialization(value) {
|
|
|
5271
5466
|
if (Object.hasOwn(value, cacheSerializationMarker) && (typeof value[cacheSerializationMarker] !== "string" || !value[cacheSerializationMarker].startsWith(supportedCacheSerializationPrefix))) return false;
|
|
5272
5467
|
return Object.values(value).every(usesSupportedCacheSerialization);
|
|
5273
5468
|
}
|
|
5274
|
-
function createExternalJsonBlobStore(
|
|
5469
|
+
function createExternalJsonBlobStore(params) {
|
|
5275
5470
|
return {
|
|
5276
5471
|
async write(rawJson) {
|
|
5277
5472
|
const rawBytes = Buffer.from(rawJson, "utf8");
|
|
5278
5473
|
const hash = hashExternalJson(rawBytes);
|
|
5279
5474
|
const path = externalJsonBlobPath(hash);
|
|
5280
5475
|
const compressed = brotliCompressSync(rawBytes);
|
|
5281
|
-
const filePath = resolveStorePath(
|
|
5476
|
+
const filePath = resolveStorePath(params.primaryDir, path);
|
|
5282
5477
|
if (!existsSync(filePath)) await writeAtomicFile(filePath, compressed);
|
|
5283
5478
|
return {
|
|
5284
5479
|
compressedLength: compressed.byteLength,
|
|
@@ -5288,10 +5483,15 @@ function createExternalJsonBlobStore(blobDir) {
|
|
|
5288
5483
|
};
|
|
5289
5484
|
},
|
|
5290
5485
|
async read(ref) {
|
|
5291
|
-
const
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5486
|
+
for (const dir of [params.primaryDir, ...params.fallbackDirs]) {
|
|
5487
|
+
const compressedResult = await resultify(() => readFile(resolveStorePath(dir, ref.path)));
|
|
5488
|
+
if (compressedResult.error) continue;
|
|
5489
|
+
const rawBytesResult = resultify(() => brotliDecompressSync(compressedResult.value));
|
|
5490
|
+
if (rawBytesResult.error) continue;
|
|
5491
|
+
const rawBytes = rawBytesResult.value;
|
|
5492
|
+
if (rawBytes.byteLength === ref.length && hashExternalJson(rawBytes) === ref.hash) return rawBytes.toString("utf8");
|
|
5493
|
+
}
|
|
5494
|
+
throw new Error(`External cache blob failed integrity check: ${ref.hash}`);
|
|
5295
5495
|
}
|
|
5296
5496
|
};
|
|
5297
5497
|
}
|
|
@@ -5317,28 +5517,55 @@ async function materializeExternalJsonCacheEntryOrNull(entry, store) {
|
|
|
5317
5517
|
const result = await resultify(() => materializeExternalJsonCacheEntry(entry, store));
|
|
5318
5518
|
return result.error ? null : result.value;
|
|
5319
5519
|
}
|
|
5320
|
-
async function
|
|
5321
|
-
if (!existsSync(blobDir)) return;
|
|
5520
|
+
async function pruneUnreferencedExternalJsonBlobs(cacheDir, blobDirs) {
|
|
5322
5521
|
const referenced = await collectReferencedExternalJsonBlobPaths(cacheDir);
|
|
5323
|
-
for (const
|
|
5522
|
+
for (const blobDir of blobDirs) {
|
|
5523
|
+
if (!existsSync(blobDir)) continue;
|
|
5524
|
+
for (const path of await listExternalJsonBlobPaths(blobDir)) if (!referenced.has(path)) await rm(resolveStorePath(blobDir, path), { force: true });
|
|
5525
|
+
}
|
|
5324
5526
|
}
|
|
5325
5527
|
async function collectReferencedExternalJsonBlobPaths(cacheDir) {
|
|
5326
5528
|
const paths = /* @__PURE__ */ new Set();
|
|
5327
|
-
for (const
|
|
5328
|
-
const fileEntry = await readCacheEntryFilePath(filePath);
|
|
5329
|
-
if (fileEntry === null || !entryMatchesPath(filePath, fileEntry.entry)) continue;
|
|
5330
|
-
collectExternalJsonBlobPaths(fileEntry.entry, paths);
|
|
5331
|
-
}
|
|
5529
|
+
for (const index_ of await listCacheIndexes(cacheDir)) for (const entry of Object.values(index_.entries)) for (const blobRef of entry.blobRefs) paths.add(blobRef);
|
|
5332
5530
|
return paths;
|
|
5333
5531
|
}
|
|
5334
|
-
function
|
|
5532
|
+
async function collectExternalJsonBlobRefs(value, blobDirs) {
|
|
5533
|
+
const paths = /* @__PURE__ */ new Set();
|
|
5534
|
+
const pendingBlobPaths = [];
|
|
5535
|
+
collectExternalJsonBlobPaths(value, paths, pendingBlobPaths);
|
|
5536
|
+
while (pendingBlobPaths.length > 0) {
|
|
5537
|
+
const blobPath = pendingBlobPaths.pop();
|
|
5538
|
+
if (blobPath === void 0) continue;
|
|
5539
|
+
const rawJson = await readExternalJsonBlobByPath(blobDirs, blobPath);
|
|
5540
|
+
if (rawJson === null) continue;
|
|
5541
|
+
const json = safeJsonParse(rawJson);
|
|
5542
|
+
if (json === null) continue;
|
|
5543
|
+
collectExternalJsonBlobPaths(json, paths, pendingBlobPaths);
|
|
5544
|
+
}
|
|
5545
|
+
return [...paths].sort();
|
|
5546
|
+
}
|
|
5547
|
+
function collectExternalJsonBlobPaths(value, paths, pendingBlobPaths) {
|
|
5335
5548
|
if (Array.isArray(value)) {
|
|
5336
|
-
for (const item of value) collectExternalJsonBlobPaths(item, paths);
|
|
5549
|
+
for (const item of value) collectExternalJsonBlobPaths(item, paths, pendingBlobPaths);
|
|
5337
5550
|
return;
|
|
5338
5551
|
}
|
|
5339
5552
|
if (!isRecordLike(value)) return;
|
|
5340
|
-
if (value[cacheSerializationMarker] === externalJsonCacheSerializationMarker && typeof value.path === "string")
|
|
5341
|
-
|
|
5553
|
+
if (value[cacheSerializationMarker] === externalJsonCacheSerializationMarker && typeof value.path === "string") {
|
|
5554
|
+
if (!paths.has(value.path)) {
|
|
5555
|
+
paths.add(value.path);
|
|
5556
|
+
pendingBlobPaths.push(value.path);
|
|
5557
|
+
}
|
|
5558
|
+
}
|
|
5559
|
+
for (const entryValue of Object.values(value)) collectExternalJsonBlobPaths(entryValue, paths, pendingBlobPaths);
|
|
5560
|
+
}
|
|
5561
|
+
async function readExternalJsonBlobByPath(blobDirs, path) {
|
|
5562
|
+
for (const blobDir of blobDirs) {
|
|
5563
|
+
const compressedResult = await resultify(() => readFile(resolveStorePath(blobDir, path)));
|
|
5564
|
+
if (compressedResult.error) continue;
|
|
5565
|
+
const rawResult = resultify(() => brotliDecompressSync(compressedResult.value).toString("utf8"));
|
|
5566
|
+
if (!rawResult.error) return rawResult.value;
|
|
5567
|
+
}
|
|
5568
|
+
return null;
|
|
5342
5569
|
}
|
|
5343
5570
|
async function listExternalJsonBlobPaths(blobDir) {
|
|
5344
5571
|
const paths = [];
|
|
@@ -5357,12 +5584,33 @@ async function collectExternalJsonBlobFilePaths(root, dir, paths) {
|
|
|
5357
5584
|
if (entry.isFile() && entry.name.endsWith(externalJsonBlobExtension)) paths.push(relative(root, path));
|
|
5358
5585
|
}
|
|
5359
5586
|
}
|
|
5360
|
-
async function listCacheEntryFiles(rootDir) {
|
|
5361
|
-
return
|
|
5587
|
+
async function listCacheEntryFiles(rootDir, scope) {
|
|
5588
|
+
if (scope === "namespace") return listDirectFilesWithExtension(rootDir, cacheEntryExtension);
|
|
5589
|
+
if (!existsSync(rootDir)) return [];
|
|
5590
|
+
const entriesResult = await resultify(() => readdir(rootDir, { withFileTypes: true }));
|
|
5591
|
+
if (entriesResult.error) return [];
|
|
5592
|
+
const files = [];
|
|
5593
|
+
for (const entry of entriesResult.value) {
|
|
5594
|
+
if (!entry.isDirectory()) continue;
|
|
5595
|
+
files.push(...await listDirectFilesWithExtension(join(rootDir, entry.name), cacheEntryExtension));
|
|
5596
|
+
}
|
|
5597
|
+
return files;
|
|
5362
5598
|
}
|
|
5363
5599
|
async function listDebugEntryFiles(rootDir) {
|
|
5364
5600
|
return listFilesWithExtension(rootDir, debugEntryExtension);
|
|
5365
5601
|
}
|
|
5602
|
+
async function listCacheIndexFiles(rootDir) {
|
|
5603
|
+
if (!existsSync(rootDir)) return [];
|
|
5604
|
+
const entriesResult = await resultify(() => readdir(rootDir, { withFileTypes: true }));
|
|
5605
|
+
if (entriesResult.error) return [];
|
|
5606
|
+
return entriesResult.value.filter((entry) => entry.isFile() && entry.name.startsWith(cacheIndexFilePrefix) && entry.name.endsWith(debugEntryExtension)).map((entry) => join(rootDir, entry.name));
|
|
5607
|
+
}
|
|
5608
|
+
async function listDirectFilesWithExtension(rootDir, extension) {
|
|
5609
|
+
if (!existsSync(rootDir)) return [];
|
|
5610
|
+
const entriesResult = await resultify(() => readdir(rootDir, { withFileTypes: true }));
|
|
5611
|
+
if (entriesResult.error) return [];
|
|
5612
|
+
return entriesResult.value.filter((entry) => entry.isFile() && entry.name.endsWith(extension)).map((entry) => join(rootDir, entry.name));
|
|
5613
|
+
}
|
|
5366
5614
|
async function listFilesWithExtension(rootDir, extension) {
|
|
5367
5615
|
if (!existsSync(rootDir)) return [];
|
|
5368
5616
|
const entriesResult = await resultify(() => readdir(rootDir, { withFileTypes: true }));
|
|
@@ -5396,6 +5644,7 @@ async function withCacheFileLock(filePath, fn) {
|
|
|
5396
5644
|
force: true
|
|
5397
5645
|
});
|
|
5398
5646
|
if (result.error) throw result.error;
|
|
5647
|
+
return result.value;
|
|
5399
5648
|
}
|
|
5400
5649
|
async function acquireLock(lockPath) {
|
|
5401
5650
|
const startedAt = Date.now();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Et as caseRowSchema, Pt as runWithEvalRegistry, Tt as caseDetailSchema, X as runWithEvalClock, _t as validateEvalTagName, bt as runSummarySchema, d as loadEvalModule, f as resolveEvalDefaultConfig, ft as deriveScopedSummaryFromCases, g as commitPendingCacheWrites, gt as matchesTagsFilter, ht as dedupeEvalTags, i as isCaseChildMessage, m as buildDeclaredColumnDefs, mt as deriveStatusFromChildStatuses, n as resolveRunnableEvalCases, o as stripTerminalControlCodes, pt as deriveStatusFromCaseRows, q as runInEvalRuntimeScope, t as filterEvalCases, u as runWithModuleIsolation, vt as validateTagsFilterExpression, wt as getCaseRowCaseKey, yt as runManifestSchema } from "./runExecution-
|
|
1
|
+
import { Et as caseRowSchema, Pt as runWithEvalRegistry, Tt as caseDetailSchema, X as runWithEvalClock, _t as validateEvalTagName, bt as runSummarySchema, d as loadEvalModule, f as resolveEvalDefaultConfig, ft as deriveScopedSummaryFromCases, g as commitPendingCacheWrites, gt as matchesTagsFilter, ht as dedupeEvalTags, i as isCaseChildMessage, m as buildDeclaredColumnDefs, mt as deriveStatusFromChildStatuses, n as resolveRunnableEvalCases, o as stripTerminalControlCodes, pt as deriveStatusFromCaseRows, q as runInEvalRuntimeScope, t as filterEvalCases, u as runWithModuleIsolation, vt as validateTagsFilterExpression, wt as getCaseRowCaseKey, yt as runManifestSchema } from "./runExecution-C31dpemR.mjs";
|
|
2
2
|
import { readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as createRunner } from "./cli-
|
|
2
|
-
import "./src-
|
|
1
|
+
import { n as createRunner } from "./cli-Bu9347r1.mjs";
|
|
2
|
+
import "./src-FR60ZR_4.mjs";
|
|
3
3
|
//#region ../../apps/server/src/runner.ts
|
|
4
4
|
let runnerInstance = null;
|
|
5
5
|
function getRunnerInstance() {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as initRunner, t as getRunnerInstance } from "./runner-
|
|
1
|
+
import { n as initRunner, t as getRunnerInstance } from "./runner-B4EfMn1d.mjs";
|
|
2
2
|
export { getRunnerInstance, initRunner };
|