anpord 0.1.11 → 0.1.13

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.
Files changed (58) hide show
  1. package/README.md +56 -78
  2. package/dist/bin.cjs +211 -204
  3. package/dist/bin.mjs +211 -204
  4. package/dist/cli-runtime.cjs +2 -2
  5. package/dist/cli-runtime.d.cts +1 -1
  6. package/dist/cli-runtime.d.mts +1 -1
  7. package/dist/cli-runtime.mjs +2 -2
  8. package/dist/client-BfdjFtlV.mjs +305 -0
  9. package/dist/{client-C9m0uLCz.d.cts → client-BgRs8JvW.d.mts} +2150 -174
  10. package/dist/{client-TFCLuMv8.d.mts → client-BirFO3jz.d.cts} +2150 -174
  11. package/dist/client-C-P2I00z.cjs +346 -0
  12. package/dist/{compiler-DXlpU_In.mjs → compiler-D96wI7UP.mjs} +108 -75
  13. package/dist/{compiler-Ap8DM-JU.cjs → compiler-uwb1pkdK.cjs} +116 -83
  14. package/dist/config.cjs +1 -1
  15. package/dist/config.d.cts +1 -1
  16. package/dist/config.d.mts +1 -1
  17. package/dist/config.mjs +1 -1
  18. package/dist/{errors-C9bQUcA3.mjs → errors-B0YknR5V.mjs} +3 -19
  19. package/dist/{errors-Boum34f5.cjs → errors-BX1wry8K.cjs} +3 -19
  20. package/dist/{errors-BK3f7Rdr.d.cts → errors-aGrd21jy.d.cts} +0 -3
  21. package/dist/{errors-BK3f7Rdr.d.mts → errors-aGrd21jy.d.mts} +0 -3
  22. package/dist/eval-judges-BkmiVLLq.cjs +59 -0
  23. package/dist/eval-judges-CwTvTq7N.mjs +42 -0
  24. package/dist/eval-judges-DPPUftbh.d.cts +48 -0
  25. package/dist/eval-judges-DPPUftbh.d.mts +48 -0
  26. package/dist/eval-validations-BRdoZrTQ.mjs +113 -0
  27. package/dist/eval-validations-_MlnQ_uv.cjs +148 -0
  28. package/dist/eval.cjs +1 -1
  29. package/dist/eval.d.cts +60 -6
  30. package/dist/eval.d.mts +60 -6
  31. package/dist/eval.mjs +1 -1
  32. package/dist/evals-B6aT3xgH.d.cts +1364 -0
  33. package/dist/evals-B6aT3xgH.d.mts +1364 -0
  34. package/dist/evals-Dcc_x6jW.mjs +582 -0
  35. package/dist/evals-DouMsrsM.cjs +737 -0
  36. package/dist/index.cjs +23 -99
  37. package/dist/index.d.cts +138 -34
  38. package/dist/index.d.mts +138 -34
  39. package/dist/index.mjs +23 -99
  40. package/dist/source.d.cts +1 -1
  41. package/dist/source.d.mts +1 -1
  42. package/dist/{types-oaMF6-E1.d.mts → types-Codjaq-o.d.mts} +146 -45
  43. package/dist/{types-H9L450Iu.d.cts → types-ISsteq95.d.cts} +146 -45
  44. package/dist/validator-runtime.cjs +238 -0
  45. package/dist/validator-runtime.d.cts +9 -0
  46. package/dist/validator-runtime.d.mts +9 -0
  47. package/dist/validator-runtime.mjs +237 -0
  48. package/dist/validators.cjs +10 -0
  49. package/dist/validators.d.cts +8 -0
  50. package/dist/validators.d.mts +8 -0
  51. package/dist/validators.mjs +9 -0
  52. package/package.json +11 -1
  53. package/dist/client-5K3dMI5Q.mjs +0 -849
  54. package/dist/client-Ci0woWLW.cjs +0 -890
  55. package/dist/evals-BEgEmfiO.d.cts +0 -603
  56. package/dist/evals-BEgEmfiO.d.mts +0 -603
  57. package/dist/harness-profile-BMjsN460.mjs +0 -43
  58. package/dist/harness-profile-CDQpPLPd.cjs +0 -78
@@ -1,4 +1,5 @@
1
- const require_harness_profile = require("./harness-profile-CDQpPLPd.cjs");
1
+ const require_evals = require("./evals-DouMsrsM.cjs");
2
+ const require_eval_judges = require("./eval-judges-BkmiVLLq.cjs");
2
3
  const require_source = require("./source-DvlGawEw.cjs");
3
4
  let effect = require("effect");
4
5
  let node_path = require("node:path");
@@ -11,12 +12,58 @@ let node_os = require("node:os");
11
12
  let node_child_process = require("node:child_process");
12
13
  let node_util = require("node:util");
13
14
  let smol_toml = require("smol-toml");
15
+ //#region src/evals/source-files.ts
16
+ const CODE_FILE = /\.(?:[cm]?[jt]s|[jt]sx)$/;
17
+ const sdkRoot = (0, node_path.resolve)((0, node_path.dirname)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href)), "..");
18
+ const SourceMap = effect.Schema.parseJson(effect.Schema.Struct({
19
+ version: effect.Schema.Literal(3),
20
+ sources: effect.Schema.Array(effect.Schema.String),
21
+ sourcesContent: effect.Schema.Array(effect.Schema.NullOr(effect.Schema.String))
22
+ }));
23
+ const projectRoot = (entry) => {
24
+ let directory = (0, node_path.dirname)(entry);
25
+ while (!(0, node_fs.existsSync)((0, node_path.resolve)(directory, "package.json"))) {
26
+ const parent = (0, node_path.dirname)(directory);
27
+ if (parent === directory) return (0, node_path.dirname)(entry);
28
+ directory = parent;
29
+ }
30
+ return directory;
31
+ };
32
+ const sourceFiles = (map, entry) => {
33
+ const decoded = effect.Schema.decodeUnknownSync(SourceMap)(map);
34
+ const root = (0, node_fs.realpathSync)(projectRoot(entry));
35
+ const entryPath = (0, node_path.relative)(root, (0, node_fs.realpathSync)(entry)).split(node_path.sep).join("/");
36
+ const files = decoded.sources.flatMap((source, index) => {
37
+ const absolute = (0, node_path.resolve)((0, node_path.dirname)(entry), source);
38
+ const path = (0, node_path.relative)(root, absolute).split(node_path.sep).join("/");
39
+ const content = decoded.sourcesContent[index];
40
+ if (content == null || path.startsWith("../") || path.split("/").includes("node_modules") || absolute.startsWith(`${sdkRoot}${node_path.sep}`) || !CODE_FILE.test(path) || source.includes(":") || (0, node_path.basename)(path) === "anpord-eval-entry.ts") return [];
41
+ return [{
42
+ path,
43
+ content
44
+ }];
45
+ });
46
+ return effect.Schema.decodeUnknownSync(require_evals.EvalSourceFiles)(files.toSorted((a, b) => Number(b.path === entryPath) - Number(a.path === entryPath) || a.path.localeCompare(b.path)));
47
+ };
48
+ //#endregion
14
49
  //#region src/evals/eval-bundle.ts
15
50
  const authoringExports = ["export const defineEval = value => value;", `export { empty, files, repo } from "./source";`].join("\n");
16
51
  const authoringDir = (0, node_path.dirname)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
17
52
  const ANPORD_MODULE = /^anpord$/;
18
53
  const ANY_MODULE = /.*/;
19
54
  const localModules = [
55
+ {
56
+ built: "validator-runtime.mjs",
57
+ filter: /^anpord\/validators\/runtime$/,
58
+ namespace: "anpord-validator-runtime",
59
+ source: "validator-runtime.ts"
60
+ },
61
+ {
62
+ built: "validators.mjs",
63
+ filter: /^anpord\/validators$/,
64
+ namespace: "anpord-validators",
65
+ source: "../validators.ts"
66
+ },
20
67
  {
21
68
  built: "cli.mjs",
22
69
  filter: /^anpord\/cli$/,
@@ -84,6 +131,8 @@ const bundle = (contents, entry, options = {}) => effect.Effect.tryPromise({
84
131
  format: "esm",
85
132
  metafile: true,
86
133
  minify: options.minify,
134
+ outfile: (0, node_path.resolve)((0, node_path.dirname)(entry), "anpord-bundle.mjs"),
135
+ sourcemap: options.captureSource ? "external" : false,
87
136
  platform: "node",
88
137
  resolveExtensions: [
89
138
  ".ts",
@@ -103,7 +152,8 @@ const bundle = (contents, entry, options = {}) => effect.Effect.tryPromise({
103
152
  write: false
104
153
  }).then((result) => ({
105
154
  inputs: Object.keys(result.metafile?.inputs ?? {}).map((path) => (0, node_path.resolve)(path)),
106
- source: result.outputFiles[0]?.text ?? ""
155
+ source: result.outputFiles.find((file) => file.path.endsWith(".mjs"))?.text ?? "",
156
+ ...options.captureSource ? { sourceFiles: sourceFiles(result.outputFiles.find((file) => file.path.endsWith(".map"))?.text ?? "", entry) } : {}
107
157
  })),
108
158
  catch: (cause) => new Error(`Could not compile ${entry}`, { cause })
109
159
  });
@@ -152,51 +202,12 @@ const packageProgram = (root, entry, source) => {
152
202
  };
153
203
  //#endregion
154
204
  //#region src/evals/runner-source.ts
155
- const runtime = `
156
- import { access, readFile } from "node:fs/promises";
157
- import { spawn } from "node:child_process";
158
-
159
- const exec = (command) => new Promise((resolve, reject) => {
160
- const child = spawn("/bin/sh", ["-lc", command], { cwd: process.cwd() });
161
- let stderr = "";
162
- let stdout = "";
163
- child.stderr.on("data", chunk => { stderr += chunk; });
164
- child.stdout.on("data", chunk => { stdout += chunk; });
165
- child.on("error", reject);
166
- child.on("close", code => resolve({ exitCode: code ?? 1, stderr, stdout }));
167
- });
168
-
169
- const readOrEmpty = (path) =>
170
- path === undefined ? Promise.resolve("") : readFile(path, "utf8").then(text => text, () => "");
171
-
172
- const calls = async (path, key, value) => {
173
- const text = await readOrEmpty(path);
174
- const calls = text.trim() === "" ? [] : text.trim().split("\\n").map((line) => JSON.parse(line));
175
- return value === undefined ? calls : calls.filter((call) => call[key] === value);
176
- };
177
-
178
- const context = {
179
- answer: () => readOrEmpty(process.env.ANPORD_ANSWER_FILE),
180
- cli: { calls: cli => calls(".anpord/cli-calls.jsonl", "cli", cli) },
181
- exec,
182
- exists: path => access(path).then(() => true, () => false),
183
- mcp: { calls: server => calls(".anpord/mcp-calls.jsonl", "server", server) },
184
- readText: path => readFile(path, "utf8"),
185
- prepared: JSON.parse(process.env.ANPORD_PREPARE_VALUE ?? "{}"),
186
- transcript: () => readOrEmpty(process.env.ANPORD_TRANSCRIPT_FILE),
187
- };
188
-
189
- try {
190
- const raw = await validate(context);
191
- const result = typeof raw === "boolean" ? { passed: raw } : raw;
192
- if (typeof result?.passed !== "boolean") throw new Error("A validator must return a boolean or { passed, message? }");
193
- console.log("ANPORD_VALIDATOR_RESULT=" + JSON.stringify(result));
194
- } catch (error) {
195
- console.error(error instanceof Error ? error.stack : String(error));
196
- process.exitCode = 1;
197
- }
198
- `;
199
- const validatorEntry = (module, name) => `import { ${name} as validate } from ${JSON.stringify(module)};\n${runtime}`;
205
+ const validatorCaseEntry = (entry, index, names = []) => `import definition from ${JSON.stringify(entry)};
206
+ import { runValidators } from "anpord/validators/runtime";
207
+ const selected = definition.cases[${index}].validate;
208
+ const names = ${JSON.stringify(names)};
209
+ const checks = (Array.isArray(selected) ? selected : [selected]).flatMap((validate, index) => typeof validate === "function" ? [{ index, name: names.find(check => check.index === index)?.name ?? "Validator " + (index + 1), validate }] : []);
210
+ await runValidators(checks, definition.captureValidation !== false);`;
200
211
  const prepareRuntime = `
201
212
  import { access, readFile } from "node:fs/promises";
202
213
  import { spawn } from "node:child_process";
@@ -247,11 +258,12 @@ import { runMcpServer } from "anpord/mcp/runtime";
247
258
  const server = definition.mcp?.[${index}];
248
259
  if (server === undefined) throw new Error("MCP server ${index} is missing");
249
260
  runMcpServer(server);`;
250
- const cliEntry = (entry, index) => `import definition from ${JSON.stringify(entry)};
261
+ const cliEntry = (entry, index) => `import { fileURLToPath } from "node:url";
262
+ import definition from ${JSON.stringify(entry)};
251
263
  import { runCli } from "anpord/cli/runtime";
252
264
  const cli = definition.cli?.[${index}];
253
265
  if (cli === undefined) throw new Error("CLI ${index} is missing");
254
- await runCli(cli);`;
266
+ await runCli(cli, fileURLToPath(new URL("../../cli-calls.jsonl", import.meta.url)));`;
255
267
  //#endregion
256
268
  //#region src/evals/cli-profile.ts
257
269
  const compileClis = (entry, definitions) => effect.Effect.gen(function* () {
@@ -287,6 +299,40 @@ const withClis = (task, clis) => {
287
299
  };
288
300
  };
289
301
  //#endregion
302
+ //#region src/evals/compile-validator.ts
303
+ const compileValidator = (entry, subject, caseIndex, captureSource, captureValidation = true) => effect.Effect.gen(function* () {
304
+ if (subject.validate === void 0) return null;
305
+ const validations = Array.isArray(subject.validate) ? subject.validate : [subject.validate];
306
+ if (validations.length === 0 || validations.length > 20) return yield* effect.Effect.fail(/* @__PURE__ */ new Error("Use between 1 and 20 validators"));
307
+ const judges = yield* effect.Effect.forEach(validations.filter((value) => typeof value !== "function"), (value) => effect.Schema.decodeUnknown(require_eval_judges.EvalJudge)(value, { onExcessProperty: "error" }));
308
+ if (new Set(judges.map(({ name }) => name)).size !== judges.length) return yield* effect.Effect.fail(/* @__PURE__ */ new Error("Judge names must be unique within a case"));
309
+ const manifest = validations.flatMap((value, index) => typeof value === "function" ? [{
310
+ index,
311
+ name: value.name || `Validator ${index + 1}`
312
+ }] : []);
313
+ const hasCode = manifest.length > 0;
314
+ const compiled = yield* bundle(hasCode ? validatorCaseEntry(entry, caseIndex, manifest) : definitionEntry(entry), entry, {
315
+ minify: true,
316
+ captureSource
317
+ });
318
+ const checks = hasCode ? [{
319
+ name: typeof subject.validate === "function" ? subject.validate.name || subject.name : subject.name,
320
+ source: compiled.source,
321
+ manifest,
322
+ capture: captureValidation
323
+ }] : [];
324
+ return yield* effect.Schema.decodeUnknown(require_evals.EvalValidator)({
325
+ ...judges.length > 0 ? {
326
+ kind: "judged",
327
+ name: subject.name,
328
+ checks,
329
+ judges
330
+ } : checks[0],
331
+ sourceFiles: compiled.sourceFiles,
332
+ capture: captureValidation
333
+ });
334
+ });
335
+ //#endregion
290
336
  //#region src/evals/definition-loader.ts
291
337
  const loadDefinition = (entry) => bundle(definitionEntry(entry), entry).pipe(effect.Effect.flatMap(({ inputs, source }) => effect.Effect.acquireUseRelease(effect.Effect.tryPromise(() => (0, node_fs_promises.mkdtemp)((0, node_path.join)((0, node_os.tmpdir)(), "anpord-eval-"))), (directory) => effect.Effect.tryPromise({
292
338
  try: async () => {
@@ -315,26 +361,25 @@ const isDefinition = (value) => effect.Option.isSome(decodeShape(value));
315
361
  //#region src/evals/local-repo.ts
316
362
  const run = (0, node_util.promisify)(node_child_process.execFile);
317
363
  const git = (args, cwd) => effect.Effect.tryPromise(() => run("git", [...args], { cwd })).pipe(effect.Effect.map(({ stdout }) => stdout.trim()), effect.Effect.orElseSucceed(() => ""));
318
- const environment = () => globalThis.process?.env ?? {};
319
364
  const remoteUrl = (cwd) => effect.Effect.gen(function* () {
320
- const { GITHUB_REPOSITORY } = environment();
365
+ const repository = yield* effect.Config.string("GITHUB_REPOSITORY").pipe(effect.Config.withDefault(""));
321
366
  const origin = yield* git([
322
367
  "remote",
323
368
  "get-url",
324
369
  "origin"
325
370
  ], cwd);
326
- return origin === "" && GITHUB_REPOSITORY !== void 0 ? GITHUB_REPOSITORY : origin;
371
+ return origin === "" ? repository : origin;
327
372
  });
328
373
  const fetchableRef = (cwd) => effect.Effect.gen(function* () {
329
- const { GITHUB_HEAD_REF } = environment();
330
- if (GITHUB_HEAD_REF !== void 0 && GITHUB_HEAD_REF !== "") return GITHUB_HEAD_REF;
374
+ const ci = yield* effect.Config.boolean("GITHUB_ACTIONS").pipe(effect.Config.withDefault(false));
331
375
  const [head, onRemote] = yield* effect.Effect.all([git(["rev-parse", "HEAD"], cwd), git([
332
376
  "branch",
333
377
  "--remotes",
334
378
  "--contains",
335
379
  "HEAD"
336
380
  ], cwd)], { concurrency: 2 });
337
- return head === "" || onRemote === "" ? null : head;
381
+ if (ci && head === "") return yield* effect.Effect.fail(/* @__PURE__ */ new Error("Check out the commit before running evals in GitHub Actions."));
382
+ return head === "" || !ci && onRemote === "" ? null : head;
338
383
  });
339
384
  const localRepo = (cwd) => effect.Effect.gen(function* () {
340
385
  const [remote, ref] = yield* effect.Effect.all([remoteUrl(cwd), fetchableRef(cwd)], { concurrency: 2 });
@@ -442,7 +487,7 @@ const adapters = {
442
487
  const applyMcpHarness = (harness, profile, servers) => {
443
488
  const adapter = adapters[harness];
444
489
  if (adapter === null) throw new Error(`MCP mock servers are not supported by ${harness}`);
445
- return effect.Schema.decodeUnknownSync(require_harness_profile.HarnessProfile)(adapter.apply(profile, servers));
490
+ return effect.Schema.decodeUnknownSync(require_evals.HarnessProfile)(adapter.apply(profile, servers));
446
491
  };
447
492
  //#endregion
448
493
  //#region src/evals/mcp-profile.ts
@@ -488,17 +533,17 @@ var ProfilePathInvalid = class extends effect.Data.TaggedError("ProfilePathInval
488
533
  };
489
534
  var ProfileFileTooLarge = class extends effect.Data.TaggedError("ProfileFileTooLarge") {
490
535
  get message() {
491
- return `${this.path} is ${this.chars} characters; a profile file holds at most ${require_harness_profile.PROFILE_LIMITS.fileChars}`;
536
+ return `${this.path} is ${this.chars} characters; a profile file holds at most ${require_evals.PROFILE_LIMITS.fileChars}`;
492
537
  }
493
538
  };
494
539
  var ProfileTooLarge = class extends effect.Data.TaggedError("ProfileTooLarge") {
495
540
  get message() {
496
- return `The profile's files hold ${this.chars} characters; at most ${require_harness_profile.PROFILE_LIMITS.totalChars} fit`;
541
+ return `The profile's files hold ${this.chars} characters; at most ${require_evals.PROFILE_LIMITS.totalChars} fit`;
497
542
  }
498
543
  };
499
544
  var ProfileTooManyFiles = class extends effect.Data.TaggedError("ProfileTooManyFiles") {
500
545
  get message() {
501
- return `The profile has ${this.count} files; at most ${require_harness_profile.PROFILE_LIMITS.files} fit`;
546
+ return `The profile has ${this.count} files; at most ${require_evals.PROFILE_LIMITS.files} fit`;
502
547
  }
503
548
  };
504
549
  var ProfileManifestInvalid = class extends effect.Data.TaggedError("ProfileManifestInvalid") {
@@ -525,7 +570,7 @@ var ProfileStepNotSupported = class extends effect.Data.TaggedError("ProfileStep
525
570
  //#region src/evals/profile-manifest.ts
526
571
  const Manifest = effect.Schema.Struct({
527
572
  env: effect.Schema.optional(effect.Schema.Record({
528
- key: require_harness_profile.EnvName,
573
+ key: require_evals.EnvName,
529
574
  value: effect.Schema.String
530
575
  })),
531
576
  install: effect.Schema.optional(effect.Schema.String),
@@ -542,14 +587,6 @@ const decodeManifest = (dir, text) => effect.Schema.decodeUnknown(ManifestJson)(
542
587
  dir,
543
588
  reason: issue.message
544
589
  })));
545
- /**
546
- * A manifest value that names a regular file is replaced by that file's
547
- * content; anything else is kept as written. Env values are always literal.
548
- *
549
- * A file outside the profile directory is refused rather than read: the
550
- * manifest is shipped with the profile, and a prompt that only exists on one
551
- * machine is not part of it.
552
- */
553
590
  const inlined = (dir, value) => effect.Effect.gen(function* () {
554
591
  const target = (0, node_path.resolve)(dir, value);
555
592
  const position = (0, node_path.relative)(dir, target);
@@ -589,7 +626,6 @@ const readProfileManifest = (dir) => effect.Effect.gen(function* () {
589
626
  const SHIPPED_ROOTS = ["home", "workspace"];
590
627
  const SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", ".git"]);
591
628
  const isMissing = (cause) => cause instanceof Error && "code" in cause && cause.code === "ENOENT";
592
- /** Every regular file below `current`, as a posix path relative to `dir`. */
593
629
  const walk = async (dir, current) => {
594
630
  const entries = await (0, node_fs_promises.readdir)(current, { withFileTypes: true });
595
631
  return (await Promise.all(entries.map((entry) => {
@@ -611,8 +647,8 @@ const shippedPaths = (dir) => effect.Effect.tryPromise({
611
647
  return (await Promise.all(SHIPPED_ROOTS.map((root) => walkRoot(dir, root)))).flat().sort();
612
648
  }
613
649
  });
614
- const validPath = (path) => effect.Schema.decodeUnknown(require_harness_profile.ProfilePath)(path).pipe(effect.Effect.mapError(() => new ProfilePathInvalid({ path })));
615
- /** The file's text, or nothing when it holds a NUL byte and is not text. */
650
+ const validPath = (path) => effect.Schema.decodeUnknown(require_evals.ProfilePath)(path).pipe(effect.Effect.mapError(() => new ProfilePathInvalid({ path })));
651
+ /** Nothing when the file holds a NUL byte and so is not text. */
616
652
  const readShipped = (dir, path) => effect.Effect.gen(function* () {
617
653
  const bytes = yield* effect.Effect.tryPromise({
618
654
  catch: (cause) => new ProfileDirectoryUnreadable({
@@ -623,7 +659,7 @@ const readShipped = (dir, path) => effect.Effect.gen(function* () {
623
659
  });
624
660
  if (bytes.includes(0)) return [];
625
661
  const content = bytes.toString("utf8");
626
- if (content.length > require_harness_profile.PROFILE_LIMITS.fileChars) return yield* new ProfileFileTooLarge({
662
+ if (content.length > require_evals.PROFILE_LIMITS.fileChars) return yield* new ProfileFileTooLarge({
627
663
  chars: content.length,
628
664
  path
629
665
  });
@@ -631,14 +667,14 @@ const readShipped = (dir, path) => effect.Effect.gen(function* () {
631
667
  });
632
668
  const shippedFiles = (dir) => effect.Effect.gen(function* () {
633
669
  const paths = yield* shippedPaths(dir);
634
- if (paths.length > require_harness_profile.PROFILE_LIMITS.files) return yield* new ProfileTooManyFiles({ count: paths.length });
670
+ if (paths.length > require_evals.PROFILE_LIMITS.files) return yield* new ProfileTooManyFiles({ count: paths.length });
635
671
  const entries = yield* effect.Effect.forEach(paths, (path) => readShipped(dir, path), { concurrency: 8 }).pipe(effect.Effect.map((read) => read.flat()));
636
672
  const chars = entries.reduce((sum, [, content]) => sum + content.length, 0);
637
- if (chars > require_harness_profile.PROFILE_LIMITS.totalChars) return yield* new ProfileTooLarge({ chars });
673
+ if (chars > require_evals.PROFILE_LIMITS.totalChars) return yield* new ProfileTooLarge({ chars });
638
674
  return Object.fromEntries(entries);
639
675
  });
640
676
  const fittingHarness = (base, profile) => {
641
- if (require_harness_profile.profileFitsHarness({
677
+ if (require_evals.profileFitsHarness({
642
678
  harness: base,
643
679
  profile
644
680
  })) return effect.Effect.succeed(profile);
@@ -648,8 +684,6 @@ const fittingHarness = (base, profile) => {
648
684
  step: "run"
649
685
  });
650
686
  };
651
- /** The task a definition's `{ base, profile }` harness compiles to, with the
652
- * profile directory read relative to the eval file. */
653
687
  const profileTask = (entry, task) => effect.Effect.gen(function* () {
654
688
  const dir = (0, node_path.resolve)((0, node_path.dirname)(entry), task.harness.profile.dir);
655
689
  yield* effect.Effect.tryPromise({
@@ -669,7 +703,7 @@ const profileTask = (entry, task) => effect.Effect.gen(function* () {
669
703
  harness: task.harness.base,
670
704
  model: task.model,
671
705
  profile,
672
- provider: task.provider
706
+ sandbox: task.sandbox
673
707
  };
674
708
  }).pipe(effect.Effect.withSpan("Eval.profileTask", { attributes: { profile: task.harness.profile.name } }));
675
709
  //#endregion
@@ -678,7 +712,7 @@ const taskOf = (entry, task, clis, mcp) => effect.Effect.gen(function* () {
678
712
  const compiled = typeof task.harness === "string" ? {
679
713
  harness: task.harness,
680
714
  model: task.model,
681
- provider: task.provider
715
+ sandbox: task.sandbox
682
716
  } : yield* profileTask(entry, {
683
717
  ...task,
684
718
  harness: task.harness
@@ -701,10 +735,9 @@ const compileEvalEffect = (path) => effect.Effect.gen(function* () {
701
735
  const clis = yield* compileClis(entry, definition.cli ?? []);
702
736
  const mcp = yield* compileMcpServers(entry, definition.mcp ?? []);
703
737
  const fallback = definition.source === void 0 && definition.cases.some((subject) => subject.source === void 0) ? yield* localRepo((0, node_path.dirname)(entry)) : effect.Option.none();
704
- const cases = yield* effect.Effect.forEach(definition.cases, (subject) => effect.Effect.gen(function* () {
705
- const hasValidator = typeof subject.validate === "function";
706
- if (hasValidator === (typeof subject.verify === "string")) return yield* effect.Effect.fail(/* @__PURE__ */ new Error(`${subject.name} must have exactly one of validate or verify`));
707
- const validator = hasValidator ? yield* bundledCaseModule(entry, loaded.inputs, subject.validate?.name || subject.name, validatorEntry) : null;
738
+ const cases = yield* effect.Effect.forEach(definition.cases, (subject, caseIndex) => effect.Effect.gen(function* () {
739
+ if (subject.validate !== void 0 === (typeof subject.verify === "string")) return yield* effect.Effect.fail(/* @__PURE__ */ new Error(`${subject.name} must have exactly one of validate or verify`));
740
+ const validator = yield* compileValidator(entry, subject, caseIndex, definition.captureSource !== false, definition.captureValidation !== false);
708
741
  const prepare = typeof subject.prepare === "function" ? yield* bundledCaseModule(entry, loaded.inputs, subject.prepare.name || `${subject.name}-prepare`, prepareEntry) : null;
709
742
  return {
710
743
  ...subject.cache === void 0 ? {} : { cache: subject.cache },
package/dist/config.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_client = require("./client-Ci0woWLW.cjs");
2
+ const require_client = require("./client-C-P2I00z.cjs");
3
3
  let effect = require("effect");
4
4
  //#region src/client/config.ts
5
5
  const apiKeyConfig = effect.Config.redacted("ANPORD_API_KEY");
package/dist/config.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as AnpordApi } from "./client-C9m0uLCz.cjs";
1
+ import { t as AnpordApi } from "./client-BirFO3jz.cjs";
2
2
  import { Config, Layer } from "effect";
3
3
  //#region src/client/config.d.ts
4
4
  declare const apiKeyConfig: Config.Config<import("effect/Redacted").Redacted<string>>;
package/dist/config.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as AnpordApi } from "./client-TFCLuMv8.mjs";
1
+ import { t as AnpordApi } from "./client-BgRs8JvW.mjs";
2
2
  import { Config, Layer } from "effect";
3
3
  //#region src/client/config.d.ts
4
4
  declare const apiKeyConfig: Config.Config<import("effect/Redacted").Redacted<string>>;
package/dist/config.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as DEFAULT_BASE_URL, r as layer } from "./client-5K3dMI5Q.mjs";
1
+ import { n as DEFAULT_BASE_URL, r as layer } from "./client-BfdjFtlV.mjs";
2
2
  import { Config, Effect, Layer } from "effect";
3
3
  //#region src/client/config.ts
4
4
  const apiKeyConfig = Config.redacted("ANPORD_API_KEY");
@@ -1,25 +1,9 @@
1
1
  import { ArrayFormatter } from "effect/ParseResult";
2
2
  //#region ../template/src/syntax.ts
3
- /**
4
- * What a variable looks like, defined once.
5
- *
6
- * The editor's atom node, the extractor and the renderer have to agree
7
- * exactly: a name the editor draws as a chip but the renderer does not fill
8
- * reaches the model as literal braces, and nobody notices until the output is
9
- * wrong.
10
- */
11
3
  const VARIABLE_PATTERN = String.raw`\{\{\s*([\w.-]+)\s*\}\}`;
12
- /**
13
- * Escapes and variables together, escapes first.
14
- *
15
- * They cannot be two passes: `{{{{name}}}}` contains `{{name}}` starting at
16
- * its third brace, so a substitution pass run on its own would fill the very
17
- * text the escape exists to protect. Reading both in one sweep is what keeps
18
- * the extractor and the renderer agreeing on what counts as a variable.
19
- *
20
- * A fresh instance each call, because a global regular expression carries its
21
- * own `lastIndex` and a shared one silently skips matches on a second pass.
22
- */
4
+ /** One sweep, escapes first: `{{{{name}}}}` contains `{{name}}` from its third
5
+ * brace, so a separate substitution pass would fill the escaped text. Fresh
6
+ * instance each call because a global regex carries its own `lastIndex`. */
23
7
  const tokenMatcher = () => new RegExp(String.raw`(\{\{\{\{)|(\}\}\}\})|${VARIABLE_PATTERN}`, "g");
24
8
  //#endregion
25
9
  //#region src/client/errors.ts
@@ -1,25 +1,9 @@
1
1
  let effect_ParseResult = require("effect/ParseResult");
2
2
  //#region ../template/src/syntax.ts
3
- /**
4
- * What a variable looks like, defined once.
5
- *
6
- * The editor's atom node, the extractor and the renderer have to agree
7
- * exactly: a name the editor draws as a chip but the renderer does not fill
8
- * reaches the model as literal braces, and nobody notices until the output is
9
- * wrong.
10
- */
11
3
  const VARIABLE_PATTERN = String.raw`\{\{\s*([\w.-]+)\s*\}\}`;
12
- /**
13
- * Escapes and variables together, escapes first.
14
- *
15
- * They cannot be two passes: `{{{{name}}}}` contains `{{name}}` starting at
16
- * its third brace, so a substitution pass run on its own would fill the very
17
- * text the escape exists to protect. Reading both in one sweep is what keeps
18
- * the extractor and the renderer agreeing on what counts as a variable.
19
- *
20
- * A fresh instance each call, because a global regular expression carries its
21
- * own `lastIndex` and a shared one silently skips matches on a second pass.
22
- */
4
+ /** One sweep, escapes first: `{{{{name}}}}` contains `{{name}}` from its third
5
+ * brace, so a separate substitution pass would fill the escaped text. Fresh
6
+ * instance each call because a global regex carries its own `lastIndex`. */
23
7
  const tokenMatcher = () => new RegExp(String.raw`(\{\{\{\{)|(\}\}\}\})|${VARIABLE_PATTERN}`, "g");
24
8
  //#endregion
25
9
  //#region src/client/errors.ts
@@ -29,9 +29,6 @@ declare const Forbidden_base: Schema.TaggedErrorClass<Forbidden, "Forbidden", {
29
29
  } & {
30
30
  message: typeof Schema.String;
31
31
  }>;
32
- /** Distinct from Unauthorized: the caller is who they say they are, and the
33
- * answer is still no. Signing in again cannot help, so the client must not
34
- * treat this as an expired session. */
35
32
  declare class Forbidden extends Forbidden_base {}
36
33
  //#endregion
37
34
  export { Unauthorized as a, NotFound as i, Conflict as n, Forbidden as r, BadRequest as t };
@@ -29,9 +29,6 @@ declare const Forbidden_base: Schema.TaggedErrorClass<Forbidden, "Forbidden", {
29
29
  } & {
30
30
  message: typeof Schema.String;
31
31
  }>;
32
- /** Distinct from Unauthorized: the caller is who they say they are, and the
33
- * answer is still no. Signing in again cannot help, so the client must not
34
- * treat this as an expired session. */
35
32
  declare class Forbidden extends Forbidden_base {}
36
33
  //#endregion
37
34
  export { Unauthorized as a, NotFound as i, Conflict as n, Forbidden as r, BadRequest as t };
@@ -0,0 +1,59 @@
1
+ let effect = require("effect");
2
+ //#region ../schema/src/domain/harness.ts
3
+ const EvalHarness = effect.Schema.Literal("codex", "opencode", "pi", "fx", "claude", "gemini", "qwen", "cursor", "command");
4
+ //#endregion
5
+ //#region ../schema/src/domain/eval-judges.ts
6
+ const text = effect.Schema.String.pipe(effect.Schema.minLength(1));
7
+ const EvalScore = effect.Schema.Number.pipe(effect.Schema.between(0, 1));
8
+ const fields = {
9
+ kind: effect.Schema.Literal("judge"),
10
+ name: text.pipe(effect.Schema.maxLength(100)),
11
+ model: text.pipe(effect.Schema.maxLength(200)),
12
+ rubric: text.pipe(effect.Schema.maxLength(32e3)),
13
+ expected: effect.Schema.optional(effect.Schema.String.pipe(effect.Schema.maxLength(32e3))),
14
+ choices: effect.Schema.Record({
15
+ key: text,
16
+ value: EvalScore
17
+ }).pipe(effect.Schema.filter((value) => Object.keys(value).length > 0 && Object.keys(value).length <= 20, { message: () => "A judge needs between 1 and 20 scored choices" })),
18
+ threshold: effect.Schema.optionalWith(EvalScore, { default: () => 1 }),
19
+ timeoutMs: effect.Schema.optionalWith(effect.Schema.Int.pipe(effect.Schema.between(1e3, 3e5)), { default: () => 12e4 })
20
+ };
21
+ const EvalJudge = effect.Schema.Union(effect.Schema.Struct({
22
+ ...fields,
23
+ provider: effect.Schema.Literal("openai"),
24
+ harness: effect.Schema.optional(effect.Schema.Never)
25
+ }), effect.Schema.Struct({
26
+ ...fields,
27
+ provider: effect.Schema.optional(effect.Schema.Never),
28
+ harness: effect.Schema.Literal(...EvalHarness.literals.filter((harness) => harness !== "command"))
29
+ }));
30
+ const EvalJudgment = effect.Schema.Struct({
31
+ name: effect.Schema.String,
32
+ model: effect.Schema.String,
33
+ evaluator: effect.Schema.String,
34
+ score: effect.Schema.NullOr(EvalScore),
35
+ choice: effect.Schema.NullOr(effect.Schema.String),
36
+ reason: effect.Schema.String,
37
+ threshold: EvalScore,
38
+ durationMs: effect.Schema.NonNegativeInt,
39
+ error: effect.Schema.NullOr(effect.Schema.String)
40
+ });
41
+ //#endregion
42
+ Object.defineProperty(exports, "EvalHarness", {
43
+ enumerable: true,
44
+ get: function() {
45
+ return EvalHarness;
46
+ }
47
+ });
48
+ Object.defineProperty(exports, "EvalJudge", {
49
+ enumerable: true,
50
+ get: function() {
51
+ return EvalJudge;
52
+ }
53
+ });
54
+ Object.defineProperty(exports, "EvalJudgment", {
55
+ enumerable: true,
56
+ get: function() {
57
+ return EvalJudgment;
58
+ }
59
+ });
@@ -0,0 +1,42 @@
1
+ import { Schema } from "effect";
2
+ //#region ../schema/src/domain/harness.ts
3
+ const EvalHarness = Schema.Literal("codex", "opencode", "pi", "fx", "claude", "gemini", "qwen", "cursor", "command");
4
+ //#endregion
5
+ //#region ../schema/src/domain/eval-judges.ts
6
+ const text = Schema.String.pipe(Schema.minLength(1));
7
+ const EvalScore = Schema.Number.pipe(Schema.between(0, 1));
8
+ const fields = {
9
+ kind: Schema.Literal("judge"),
10
+ name: text.pipe(Schema.maxLength(100)),
11
+ model: text.pipe(Schema.maxLength(200)),
12
+ rubric: text.pipe(Schema.maxLength(32e3)),
13
+ expected: Schema.optional(Schema.String.pipe(Schema.maxLength(32e3))),
14
+ choices: Schema.Record({
15
+ key: text,
16
+ value: EvalScore
17
+ }).pipe(Schema.filter((value) => Object.keys(value).length > 0 && Object.keys(value).length <= 20, { message: () => "A judge needs between 1 and 20 scored choices" })),
18
+ threshold: Schema.optionalWith(EvalScore, { default: () => 1 }),
19
+ timeoutMs: Schema.optionalWith(Schema.Int.pipe(Schema.between(1e3, 3e5)), { default: () => 12e4 })
20
+ };
21
+ const EvalJudge = Schema.Union(Schema.Struct({
22
+ ...fields,
23
+ provider: Schema.Literal("openai"),
24
+ harness: Schema.optional(Schema.Never)
25
+ }), Schema.Struct({
26
+ ...fields,
27
+ provider: Schema.optional(Schema.Never),
28
+ harness: Schema.Literal(...EvalHarness.literals.filter((harness) => harness !== "command"))
29
+ }));
30
+ const EvalJudgment = Schema.Struct({
31
+ name: Schema.String,
32
+ model: Schema.String,
33
+ evaluator: Schema.String,
34
+ score: Schema.NullOr(EvalScore),
35
+ choice: Schema.NullOr(Schema.String),
36
+ reason: Schema.String,
37
+ threshold: EvalScore,
38
+ durationMs: Schema.NonNegativeInt,
39
+ error: Schema.NullOr(Schema.String)
40
+ });
41
+ //#endregion
42
+ export { EvalJudgment as n, EvalHarness as r, EvalJudge as t };
@@ -0,0 +1,48 @@
1
+ import { Schema } from "effect";
2
+ //#region ../schema/src/domain/eval-judges.d.ts
3
+ declare const EvalJudge: Schema.Union<[Schema.Struct<{
4
+ provider: Schema.Literal<["openai"]>;
5
+ harness: Schema.optional<typeof Schema.Never>;
6
+ kind: Schema.Literal<["judge"]>;
7
+ name: Schema.filter<Schema.filter<typeof Schema.String>>;
8
+ model: Schema.filter<Schema.filter<typeof Schema.String>>;
9
+ rubric: Schema.filter<Schema.filter<typeof Schema.String>>;
10
+ expected: Schema.optional<Schema.filter<typeof Schema.String>>;
11
+ choices: Schema.filter<Schema.Record$<Schema.filter<typeof Schema.String>, Schema.filter<typeof Schema.Number>>>;
12
+ threshold: Schema.optionalWith<Schema.filter<typeof Schema.Number>, {
13
+ default: () => number;
14
+ }>;
15
+ timeoutMs: Schema.optionalWith<Schema.filter<typeof Schema.Int>, {
16
+ default: () => number;
17
+ }>;
18
+ }>, Schema.Struct<{
19
+ provider: Schema.optional<typeof Schema.Never>;
20
+ harness: Schema.SchemaClass<"codex" | "opencode" | "pi" | "fx" | "claude" | "gemini" | "qwen" | "cursor", "codex" | "opencode" | "pi" | "fx" | "claude" | "gemini" | "qwen" | "cursor", never>;
21
+ kind: Schema.Literal<["judge"]>;
22
+ name: Schema.filter<Schema.filter<typeof Schema.String>>;
23
+ model: Schema.filter<Schema.filter<typeof Schema.String>>;
24
+ rubric: Schema.filter<Schema.filter<typeof Schema.String>>;
25
+ expected: Schema.optional<Schema.filter<typeof Schema.String>>;
26
+ choices: Schema.filter<Schema.Record$<Schema.filter<typeof Schema.String>, Schema.filter<typeof Schema.Number>>>;
27
+ threshold: Schema.optionalWith<Schema.filter<typeof Schema.Number>, {
28
+ default: () => number;
29
+ }>;
30
+ timeoutMs: Schema.optionalWith<Schema.filter<typeof Schema.Int>, {
31
+ default: () => number;
32
+ }>;
33
+ }>]>;
34
+ type EvalJudge = typeof EvalJudge.Type;
35
+ declare const EvalJudgment: Schema.Struct<{
36
+ name: typeof Schema.String;
37
+ model: typeof Schema.String;
38
+ evaluator: typeof Schema.String;
39
+ score: Schema.NullOr<Schema.filter<typeof Schema.Number>>;
40
+ choice: Schema.NullOr<typeof Schema.String>;
41
+ reason: typeof Schema.String;
42
+ threshold: Schema.filter<typeof Schema.Number>;
43
+ durationMs: Schema.refine<number, typeof Schema.NonNegative>;
44
+ error: Schema.NullOr<typeof Schema.String>;
45
+ }>;
46
+ type EvalJudgment = typeof EvalJudgment.Type;
47
+ //#endregion
48
+ export { EvalJudgment as n, EvalJudge as t };