oh-my-promptfoo 1.11.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/cli.js +56 -8
- package/dist/cli.js.map +1 -1
- package/dist/delegate-runner.js +1 -1
- package/dist/delegate-runner.js.map +1 -1
- package/dist/index.cjs +101 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +101 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,6 +25,12 @@ Set the agent credential through `delegate.env` in authored evals. Set
|
|
|
25
25
|
require ORAS 1.x through `ALLAGENTS_ORAS_PATH`. A trusted runner must provide
|
|
26
26
|
`ALLAGENTS_LOCAL_SOURCE_ROOT` before acquiring `type: local` sources.
|
|
27
27
|
|
|
28
|
+
Several `type: local` sources may name the same `destination` and merge into one
|
|
29
|
+
tree, so N plugins can stage their skills into a single `.agents/skills`. They
|
|
30
|
+
must agree on `permissions`, and no two of them may contribute the same relative
|
|
31
|
+
path; a collision names both sources and fails. Git and OCI sources keep
|
|
32
|
+
exclusive destinations, and nested destinations remain rejected.
|
|
33
|
+
|
|
28
34
|
`llmAssert` batches named criteria into one judge request. A component passes
|
|
29
35
|
when its judge verdict is not false, and `config.threshold` adds a numeric bar
|
|
30
36
|
on top of that verdict when you state one — the same contract as Promptfoo's
|
package/dist/cli.js
CHANGED
|
@@ -898,7 +898,7 @@ async function prepareProtectedCopy(source, path2, viewMode = "auto", caseIndex,
|
|
|
898
898
|
init_esm_shims();
|
|
899
899
|
import { isAbsolute as isAbsolute2, posix } from "path";
|
|
900
900
|
var DEFAULT_LIMITS = {
|
|
901
|
-
maxSources:
|
|
901
|
+
maxSources: 64,
|
|
902
902
|
maxDownloadBytes: 268435456,
|
|
903
903
|
maxExtractedBytes: 536870912,
|
|
904
904
|
timeoutMs: 12e4
|
|
@@ -2077,6 +2077,11 @@ import { isAbsolute as isAbsolute6, join as join8, relative as relative3, resolv
|
|
|
2077
2077
|
import { Transform } from "stream";
|
|
2078
2078
|
import { pipeline } from "stream/promises";
|
|
2079
2079
|
var snapshots = /* @__PURE__ */ new WeakMap();
|
|
2080
|
+
function localEntries(source) {
|
|
2081
|
+
const entries = snapshots.get(source);
|
|
2082
|
+
if (!entries) throw new Error("Local source snapshot is not available to this provider");
|
|
2083
|
+
return entries;
|
|
2084
|
+
}
|
|
2080
2085
|
var ordered = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
2081
2086
|
var READ_FLAGS = constants5.O_RDONLY | constants5.O_NOFOLLOW | constants5.O_NONBLOCK;
|
|
2082
2087
|
function sourceRoot(source, channels) {
|
|
@@ -2417,9 +2422,41 @@ async function logicalBytes(root) {
|
|
|
2417
2422
|
}
|
|
2418
2423
|
return total;
|
|
2419
2424
|
}
|
|
2425
|
+
function assertMergeable(destination2, group) {
|
|
2426
|
+
const owners = /* @__PURE__ */ new Map();
|
|
2427
|
+
for (const source of group) {
|
|
2428
|
+
if (source.type !== "local")
|
|
2429
|
+
throw new Error(
|
|
2430
|
+
`Only local sources may share destination ${JSON.stringify(destination2)}; ${source.type} source ${JSON.stringify(source.repository)} cannot merge`
|
|
2431
|
+
);
|
|
2432
|
+
for (const entry of localEntries(source)) {
|
|
2433
|
+
const owner = owners.get(entry.path);
|
|
2434
|
+
if (!owner) {
|
|
2435
|
+
owners.set(entry.path, { path: source.path, entry });
|
|
2436
|
+
continue;
|
|
2437
|
+
}
|
|
2438
|
+
if (owner.entry.size !== void 0 || entry.size !== void 0)
|
|
2439
|
+
throw new Error(
|
|
2440
|
+
`Sources sharing destination ${JSON.stringify(destination2)} both provide ${JSON.stringify(entry.path)}: ${owner.path} and ${source.path}`
|
|
2441
|
+
);
|
|
2442
|
+
if (owner.entry.mode !== entry.mode)
|
|
2443
|
+
throw new Error(
|
|
2444
|
+
`Sources sharing destination ${JSON.stringify(destination2)} disagree on the mode of directory ${JSON.stringify(entry.path)}: ${owner.path} and ${source.path}`
|
|
2445
|
+
);
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2420
2449
|
async function materializeSources(resolved, staging, limits, channels, signal, caseIndex) {
|
|
2421
2450
|
signal?.throwIfAborted();
|
|
2422
2451
|
if (resolved.length > limits.maxSources) throw new Error("Too many resolved sources");
|
|
2452
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2453
|
+
for (const source of resolved) {
|
|
2454
|
+
const group = groups.get(source.destination);
|
|
2455
|
+
if (group) group.push(source);
|
|
2456
|
+
else groups.set(source.destination, [source]);
|
|
2457
|
+
}
|
|
2458
|
+
for (const [destination2, group] of groups)
|
|
2459
|
+
if (group.length > 1) assertMergeable(destination2, group);
|
|
2423
2460
|
staging = resolve9(staging);
|
|
2424
2461
|
const writer = await PhysicalWriter.create(
|
|
2425
2462
|
staging,
|
|
@@ -2489,13 +2526,20 @@ function acquisitionIdentity(source) {
|
|
|
2489
2526
|
materializerVersion: source.materializerVersion ?? 1
|
|
2490
2527
|
};
|
|
2491
2528
|
}
|
|
2492
|
-
function
|
|
2493
|
-
|
|
2529
|
+
function sortedIdentities(sources) {
|
|
2530
|
+
return sources.map((source) => {
|
|
2531
|
+
const identity = acquisitionIdentity(source);
|
|
2532
|
+
return { destination: source.destination, identity, text: canonicalJson(identity) };
|
|
2533
|
+
}).sort(
|
|
2534
|
+
(a, b) => a.destination < b.destination ? -1 : a.destination > b.destination ? 1 : a.text < b.text ? -1 : a.text > b.text ? 1 : 0
|
|
2535
|
+
).map((entry) => entry.identity);
|
|
2536
|
+
}
|
|
2537
|
+
function protectedCheckoutKey(sources, viewMode = "auto") {
|
|
2538
|
+
const identity = sources.length === 1 ? canonicalJson(acquisitionIdentity(sources[0])) : canonicalJson({ schemaVersion: 1, merged: sortedIdentities(sources) });
|
|
2494
2539
|
return hash(viewMode === "copy-only" ? `${identity}\0copy-only` : identity);
|
|
2495
2540
|
}
|
|
2496
2541
|
function manifestDigest(sources) {
|
|
2497
|
-
|
|
2498
|
-
return `sha256:${hash(canonicalJson({ schemaVersion: 1, sources: identities }))}`;
|
|
2542
|
+
return `sha256:${hash(canonicalJson({ schemaVersion: 1, sources: sortedIdentities(sources) }))}`;
|
|
2499
2543
|
}
|
|
2500
2544
|
var MAX_INVENTORY_JSON_BYTES = 512 * 1024 ** 2;
|
|
2501
2545
|
var LAST_USED_REFRESH_MS = 60 * 60 * 1e3;
|
|
@@ -2794,8 +2838,12 @@ var SeedCache = class {
|
|
|
2794
2838
|
}
|
|
2795
2839
|
});
|
|
2796
2840
|
}
|
|
2797
|
-
|
|
2798
|
-
|
|
2841
|
+
/** `sources` is every source contributing to one destination, merged or alone. */
|
|
2842
|
+
async protectedSource(record, sources, signal, viewMode = "auto", caseIndex, sourceIndex, sourceCount) {
|
|
2843
|
+
const destination2 = sources[0]?.destination;
|
|
2844
|
+
if (destination2 === void 0 || sources.some((source) => source.destination !== destination2))
|
|
2845
|
+
throw new Error("Protected checkout requires sources sharing one destination");
|
|
2846
|
+
const key = protectedCheckoutKey(sources, viewMode);
|
|
2799
2847
|
const path2 = join10(this.root, "checkouts", key, "tree");
|
|
2800
2848
|
return this.lockAcquisition(
|
|
2801
2849
|
join10(this.root, "locks", "admission"),
|
|
@@ -2818,7 +2866,7 @@ var SeedCache = class {
|
|
|
2818
2866
|
);
|
|
2819
2867
|
}
|
|
2820
2868
|
} else {
|
|
2821
|
-
const seedSource = join10(this.seedPath(record.digest),
|
|
2869
|
+
const seedSource = join10(this.seedPath(record.digest), destination2);
|
|
2822
2870
|
await measuredPhase(
|
|
2823
2871
|
"checkout-admission",
|
|
2824
2872
|
async () => this.admit(
|