@coder/aegis 0.1.1-rc.0 → 0.3.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/lib/index.js CHANGED
@@ -27,6 +27,7 @@ const OVERLAY_KINDS = /* @__PURE__ */ new Set([
27
27
  ]);
28
28
  function defineExperiment(id, define) {
29
29
  assertSafeID("experiment", id);
30
+ strict(id !== "bundle-assets", `experiment id "bundle-assets" collides with the reserved bundle-assets store namespace`);
30
31
  const builder = new ExperimentBuilder(id);
31
32
  define(builder);
32
33
  return builder.build();
@@ -237,14 +238,7 @@ var ExperimentBuilder = class {
237
238
  strict(this.baseRepos.has(task.seed.base_repo), `task ${task.id}: base repo ${task.seed.base_repo} was not declared`);
238
239
  validateTaskGraders(task, graders);
239
240
  }
240
- const subject = {
241
- harness: this.config.subject.harness,
242
- harness_version: this.config.subject.harnessVersion,
243
- control_transport: this.config.subject.controlTransport,
244
- model: this.config.subject.model,
245
- allowed_tools: [...this.config.subject.allowedTools],
246
- env: sortedStringRecord(this.config.subject.env ?? {})
247
- };
241
+ const subjectFields = this.config.subject !== void 0 ? { subject: experimentSubjectDoc(this.config.subject) } : { subjects: codeUnitSortedSubjects(this.config.subjects ?? {}) };
248
242
  const budget = {
249
243
  max_usd: this.config.budget.maxUsd,
250
244
  confirm_required: this.config.budget.confirmRequired,
@@ -261,7 +255,7 @@ var ExperimentBuilder = class {
261
255
  backend: this.config.backend,
262
256
  ...backendOptions === void 0 ? {} : { backend_options: backendOptions },
263
257
  hypothesis: this.config.hypothesis,
264
- subject,
258
+ ...subjectFields,
265
259
  suite: suiteID,
266
260
  grader_registry: ["graders/common.yaml"],
267
261
  suites: suiteDocs,
@@ -594,12 +588,23 @@ function assertExperimentConfig(id, config) {
594
588
  }
595
589
  }
596
590
  assertNonEmpty(`experiment ${id}.hypothesis`, config.hypothesis);
597
- strict(config.subject && typeof config.subject === "object", `experiment ${id}: subject is required`);
598
- assertNonEmpty(`experiment ${id}.subject.harness`, config.subject.harness);
599
- assertNonEmpty(`experiment ${id}.subject.harnessVersion`, config.subject.harnessVersion);
600
- assertNonEmpty(`experiment ${id}.subject.controlTransport`, config.subject.controlTransport);
601
- assertNonEmpty(`experiment ${id}.subject.model`, config.subject.model);
602
- assertStringArray(`experiment ${id}.subject.allowedTools`, config.subject.allowedTools);
591
+ strict(config.subject === void 0 !== (config.subjects === void 0), `experiment ${id}: exactly one of subject or subjects must be set`);
592
+ if (config.subject !== void 0) {
593
+ strict(config.subject && typeof config.subject === "object", `experiment ${id}: subject is required`);
594
+ assertExperimentSubjectInput(`experiment ${id}.subject`, config.subject);
595
+ } else {
596
+ strict(config.subjects !== null && typeof config.subjects === "object" && !Array.isArray(config.subjects), `experiment ${id}.subjects must be an object`);
597
+ const subjectIDs = Object.keys(config.subjects ?? {});
598
+ strict(subjectIDs.length > 0, `experiment ${id}.subjects must declare at least one subject`);
599
+ for (const subjectID of subjectIDs) {
600
+ assertSafeID("subject", subjectID);
601
+ strict(!/^[0-9]+$/.test(subjectID), `experiment ${id}.subjects id ${JSON.stringify(subjectID)} must not be purely numeric: JSON object key order for integer-like keys diverges between emitters`);
602
+ strict(!subjectID.includes("__"), `experiment ${id}.subjects id ${JSON.stringify(subjectID)} must not contain "__": trial ids join task, subject, variant, and repeat segments with it`);
603
+ const subject = (config.subjects ?? {})[subjectID];
604
+ strict(subject && typeof subject === "object", `experiment ${id}.subjects[${JSON.stringify(subjectID)}] must be an object`);
605
+ assertExperimentSubjectInput(`experiment ${id}.subjects[${JSON.stringify(subjectID)}]`, subject);
606
+ }
607
+ }
603
608
  strict(Number.isInteger(config.repeats) && config.repeats > 0, `experiment ${id}: repeats must be a positive integer`);
604
609
  strict(config.budget && typeof config.budget === "object", `experiment ${id}: budget is required`);
605
610
  assertFiniteNumber(`experiment ${id}.budget.maxUsd`, config.budget.maxUsd);
@@ -612,6 +617,13 @@ function assertExperimentConfig(id, config) {
612
617
  assertNonEmpty(`experiment ${id}.scheduling.order`, config.scheduling.order);
613
618
  assertNonEmpty(`experiment ${id}.scheduling.retry`, config.scheduling.retry);
614
619
  }
620
+ function assertExperimentSubjectInput(context, subject) {
621
+ assertNonEmpty(`${context}.harness`, subject.harness);
622
+ assertNonEmpty(`${context}.harnessVersion`, subject.harnessVersion);
623
+ assertNonEmpty(`${context}.controlTransport`, subject.controlTransport);
624
+ assertNonEmpty(`${context}.model`, subject.model);
625
+ assertStringArray(`${context}.allowedTools`, subject.allowedTools);
626
+ }
615
627
  function assertGraderRef(context, grader) {
616
628
  strict(grader && typeof grader === "object", `${context}: grader ref must be an object`);
617
629
  strict(grader.kind === "aegis.grader-ref", `${context}: expected an Aegis grader helper result`);
@@ -824,6 +836,20 @@ function normalizePrompt(input) {
824
836
  strict(content.length > 0, "prompt must not be blank");
825
837
  return `${content}\n`;
826
838
  }
839
+ function experimentSubjectDoc(subject) {
840
+ return {
841
+ harness: subject.harness,
842
+ harness_version: subject.harnessVersion,
843
+ control_transport: subject.controlTransport,
844
+ model: subject.model,
845
+ allowed_tools: [...subject.allowedTools],
846
+ env: sortedStringRecord(subject.env ?? {})
847
+ };
848
+ }
849
+ function codeUnitSortedSubjects(input) {
850
+ const entries = Object.entries(input).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
851
+ return Object.fromEntries(entries.map(([id, subject]) => [id, experimentSubjectDoc(subject)]));
852
+ }
827
853
  function sortedStringRecord(input) {
828
854
  const entries = Object.entries(input).sort(([a], [b]) => a.localeCompare(b));
829
855
  for (const [key, value] of entries) {
@@ -10,7 +10,10 @@ export interface ExperimentConfig {
10
10
  backend: string;
11
11
  backendOptions?: BackendOptionsInput;
12
12
  hypothesis: string;
13
- subject: ExperimentSubjectInput;
13
+ /** Exactly one of subject or subjects must be set. */
14
+ subject?: ExperimentSubjectInput;
15
+ /** Multi-subject axis keyed by repo-safe subject id. */
16
+ subjects?: Record<string, ExperimentSubjectInput>;
14
17
  repeats: number;
15
18
  budget: BudgetInput;
16
19
  primaryMetric: string;
@@ -4,7 +4,8 @@ export interface ExperimentDoc {
4
4
  backend: string;
5
5
  backend_options?: BackendOptionsDoc;
6
6
  hypothesis: string;
7
- subject: ExperimentSubjectDoc;
7
+ subject?: ExperimentSubjectDoc;
8
+ subjects?: Record<string, ExperimentSubjectDoc>;
8
9
  suite: string;
9
10
  grader_registry: string[];
10
11
  suites: Record<string, SuiteDoc>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coder/aegis",
3
- "version": "0.1.1-rc.0",
3
+ "version": "0.3.0",
4
4
  "description": "aegis CLI and TypeScript eval-authoring facade (Go binary with embedded TS eval compiler and web UI, plus the @coder/aegis import surface)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,10 +23,10 @@
23
23
  "url": "git+https://github.com/coder/aegis.git"
24
24
  },
25
25
  "optionalDependencies": {
26
- "@coder/aegis-linux-x64": "0.1.1-rc.0",
27
- "@coder/aegis-linux-arm64": "0.1.1-rc.0",
28
- "@coder/aegis-darwin-x64": "0.1.1-rc.0",
29
- "@coder/aegis-darwin-arm64": "0.1.1-rc.0",
30
- "@coder/aegis-win32-x64": "0.1.1-rc.0"
26
+ "@coder/aegis-linux-x64": "0.3.0",
27
+ "@coder/aegis-linux-arm64": "0.3.0",
28
+ "@coder/aegis-darwin-x64": "0.3.0",
29
+ "@coder/aegis-darwin-arm64": "0.3.0",
30
+ "@coder/aegis-win32-x64": "0.3.0"
31
31
  }
32
- }
32
+ }