@openpond/evals 0.1.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/CONTRACT.md ADDED
@@ -0,0 +1,39 @@
1
+ # Portable contract map
2
+
3
+ `@openpond/evals` owns the portable wire formats and pure protocol logic. Host
4
+ applications own persistence, provider sessions, secret leases, connected-app
5
+ authorization, model streaming, artifact bytes, and runtime processes.
6
+
7
+ | Existing object | Portable object | Migration rule |
8
+ | --- | --- | --- |
9
+ | `Taskset` | `TasksetRelease` | Project only the released tasks, policy, environment, tools, capabilities, graders, and immutable Harness binding. Authoring state and UI readiness remain host state. |
10
+ | `HarnessRunManifest` (`openpond.harnessRunManifest.v1`) | `RunManifest` (`openpond.runManifest.v1`) | Treat the old object as a legacy training projection. Normalize its release/model/runtime identities into one new manifest; recipe, compute, engine, secret leases, and approval records remain host bindings referenced by hashes. |
11
+ | `HarnessRunTrace` | `HarnessTrace` | Preserve ordered actions, observations, lifecycle events, terminal state, failure class, and trace hash. Learning-signal envelopes remain a training projection of the trace and receipt. |
12
+ | `TaskAttemptResult` | `AttemptReceipt` | Preserve the old record for application persistence while adding a lossless receipt reference. Output becomes `outputHash`; trace and artifacts are separately hash-bound. |
13
+ | `GradeResult` | `GraderEvidence[]` | Preserve component score, pass, reward eligibility, failure class, feedback, and visible/private evidence references. Aggregate UI results remain host projections. |
14
+ | managed-RL local receipt | `AttemptReceipt` | Submit canonical manifest/task/trace/artifact/grader identities; policy token responses and provider request IDs remain host-private trace data. |
15
+ | resolved training bundle | `HarnessRelease` + host training binding | Environment, tools, program, policy, files, and grader interface belong to the Harness. Dataset/evidence, recipe, compute, engine, approval, and opaque leases remain explicit host-side bindings. |
16
+
17
+ ## Compatibility policy
18
+
19
+ - Schema versions are literal and independent from package semver.
20
+ - `openpond.harnessRunManifest.v1` and `openpond.taskAttempt.v1` remain accepted
21
+ host persistence formats during migration, but are not emitted as new public
22
+ identities by this package.
23
+ - Compatible package releases may add optional helpers and exports. Changing a
24
+ required field, identity hash, or privacy boundary requires a new schema
25
+ literal and an explicit normalizer.
26
+ - The initial support target is Node.js ESM on Node 22.14 through Node 24.
27
+ - Portable paths are relative and at most 2,000 characters. Individual assets
28
+ are at most 250 MB. Tasksets, traces, and evidence arrays have schema-level
29
+ upper bounds.
30
+ - Immutable content never contains secret values, opaque lease values, mutable
31
+ provider resource IDs, database keys, UI state, or process handles.
32
+
33
+ ## Runtime interfaces
34
+
35
+ `HarnessRuntime` owns create/reset/step/collect/destroy environment state.
36
+ `HarnessExecutor` owns the existing model or Agent loop and emits one receipt
37
+ plus a hash-bound trace. `EvaluationRunner` selects tasks, seeds, repetitions,
38
+ and models, invokes the executor, runs graders in their declared boundary, and
39
+ aggregates host results. Runtime adapters stay with Desktop or Sandbox.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OpenPond
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # `@openpond/evals`
2
+
3
+ Portable OpenPond contracts and reference helpers for immutable Agent snapshots,
4
+ Harness releases, Taskset releases, run manifests, attempt receipts, deterministic
5
+ graders, and conformance fixtures.
6
+
7
+ ```ts
8
+ import {
9
+ AttemptReceiptSchema,
10
+ HarnessReleaseSchema,
11
+ TasksetReleaseSchema,
12
+ validateTasksetRelease,
13
+ verifyAttemptReceipt,
14
+ } from "@openpond/evals";
15
+ ```
16
+
17
+ Subpath exports are available at `/harness`, `/tasksets`, `/graders`, `/runs`,
18
+ and `/conformance`. The package is a protocol library, not a hosted client. It
19
+ does not execute OpenPond Desktop or Sandbox sessions, resolve credentials, or
20
+ persist artifacts.
21
+
22
+ ## Privacy boundary
23
+
24
+ Release content may include approved policy-visible assets and immutable hashes
25
+ for verifier/host-private assets. Never serialize secrets, credential leases,
26
+ provider resource handles, database identifiers, unrelated conversations, or
27
+ mutable local UI state. Private graders receive only their declared evidence and
28
+ must execute outside the policy-controlled Agent environment when tamper
29
+ resistance matters.
30
+
31
+ ## Runtime adapters
32
+
33
+ Implement `HarnessRuntime` for environment state and `HarnessExecutor` around
34
+ the host's existing model/Agent loop. Do not reimplement prompting, tool dispatch,
35
+ session persistence, cost accounting, cancellation, or cleanup in this package.
36
+ A Prime-style environment maps reset/step/observation semantics to the same
37
+ interfaces and keeps provider allocation, authentication, and cleanup in its host.
38
+
39
+ ## Conformance
40
+
41
+ Run both `genericToolConformance` and `marketingPortfolioConformance` through the
42
+ same adapter. The marketing fixture is named test data, not a runtime switch.
43
+ Compare manifest, terminal/failure, output, trace, artifact, and grader receipt
44
+ semantics. Infrastructure failures must remain reward-ineligible.
45
+
46
+ ## Schema lifecycle
47
+
48
+ Package semver and schema literals are independent. See [CONTRACT.md](./CONTRACT.md)
49
+ for compatibility aliases, migration rules, size limits, and the field map.
50
+
51
+ ## Release preparation
52
+
53
+ ```bash
54
+ pnpm --dir packages/evals run check
55
+ pnpm run release:evals:patch -- --dry-run
56
+ ```
57
+
58
+ The first npm publication must be bootstrapped manually before trusted publishing
59
+ can take over. Publication uses package-specific `evals-vX.Y.Z` tags and npm
60
+ provenance; see the release workflow for the exact one-time setup.
package/RELEASING.md ADDED
@@ -0,0 +1,49 @@
1
+ # Releasing `@openpond/evals`
2
+
3
+ The package version is independent from OpenPond application and schema
4
+ versions. Version `0.1.0` supports these initial schema literals:
5
+
6
+ - `openpond.agentSnapshot.v1`
7
+ - `openpond.harnessRelease.v1`
8
+ - `openpond.tasksetRelease.v1`
9
+ - `openpond.runManifest.v1`
10
+ - `openpond.attemptReceipt.v1`
11
+ - `openpond.harnessTrace.v1`
12
+ - `openpond.graderEvidence.v1`
13
+ - `openpond.evaluationResult.v1`
14
+
15
+ ## One-time npm bootstrap
16
+
17
+ Trusted publishing can only be configured after the scoped package exists. Once
18
+ this feature is merged, use a clean current `master` checkout:
19
+
20
+ ```bash
21
+ pnpm evals:check
22
+ npm login
23
+ npm publish ./packages/evals --access public --ignore-scripts --provenance=false
24
+ npm install --global npm@^11.15.0
25
+ npm trust github @openpond/evals \
26
+ --repo openpond/openpond \
27
+ --file release-evals.yml \
28
+ --environment npm-production \
29
+ --allow-publish
30
+ npm trust list @openpond/evals
31
+ gh workflow run release-evals.yml --ref master
32
+ ```
33
+
34
+ The last command is idempotent recovery: because `0.1.0` is already on npm, the
35
+ workflow skips publication and creates the missing `evals-v0.1.0` GitHub release.
36
+
37
+ ## Later releases
38
+
39
+ From a clean current `master` checkout:
40
+
41
+ ```bash
42
+ pnpm release:evals:patch
43
+ # or release:evals:minor / release:evals:major
44
+ ```
45
+
46
+ The release helper creates an Evals-only release PR. Merging it triggers trusted
47
+ publishing with provenance, registry verification, and a package-specific tag.
48
+ Record the registry integrity/provenance evidence and merge commit in the
49
+ release notes after publication.
package/dist/common.js ADDED
@@ -0,0 +1,67 @@
1
+ import { z } from "zod";
2
+ import { sha256Hex } from "./sha256.js";
3
+ export const MAX_PORTABLE_PATH_BYTES = 2_000;
4
+ export const MAX_PORTABLE_ASSET_BYTES = 250_000_000;
5
+ export const ReleaseIdSchema = z.string().trim().min(1).max(240);
6
+ export const ReleaseHashSchema = z.string().regex(/^[a-f0-9]{64}$/);
7
+ export const ReleaseTimestampSchema = z.string().datetime({ offset: true });
8
+ export const MetadataSchema = z.record(z.string(), z.unknown()).default({});
9
+ export const ImmutableReleaseRefSchema = z.object({
10
+ id: ReleaseIdSchema,
11
+ contentHash: ReleaseHashSchema,
12
+ }).strict();
13
+ export const ImmutableAssetRefSchema = z.object({
14
+ id: ReleaseIdSchema,
15
+ path: z.string().trim().min(1).max(MAX_PORTABLE_PATH_BYTES).refine(safeRelativePath),
16
+ contentHash: ReleaseHashSchema,
17
+ sizeBytes: z.number().int().nonnegative().max(MAX_PORTABLE_ASSET_BYTES),
18
+ mediaType: z.string().trim().min(1).max(200),
19
+ visibility: z.enum(["policy", "verifier", "host_private"]),
20
+ }).strict();
21
+ export const ImmutableArtifactRefSchema = z.object({
22
+ id: ReleaseIdSchema,
23
+ contentHash: ReleaseHashSchema,
24
+ mediaType: z.string().trim().min(1).max(200).nullable().default(null),
25
+ sizeBytes: z.number().int().nonnegative().max(MAX_PORTABLE_ASSET_BYTES).nullable().default(null),
26
+ }).strict();
27
+ export const FailureClassSchema = z.enum([
28
+ "policy_failure",
29
+ "grader_failure",
30
+ "environment_failure",
31
+ "infrastructure_failure",
32
+ "timeout",
33
+ "cancelled",
34
+ ]);
35
+ export function canonicalJson(value) {
36
+ return `${JSON.stringify(sortValue(value), null, 2)}\n`;
37
+ }
38
+ export function sha256(value) {
39
+ return sha256Hex(value);
40
+ }
41
+ export function contentHash(value) {
42
+ return sha256(canonicalJson(value));
43
+ }
44
+ export function withContentHash(value) {
45
+ return { ...value, contentHash: contentHash(value) };
46
+ }
47
+ export function assertContentHash(value, label) {
48
+ const { contentHash: actual, ...hashable } = value;
49
+ const expected = contentHash(hashable);
50
+ if (actual !== expected)
51
+ throw new Error(`${label} contentHash is ${actual}; expected ${expected}.`);
52
+ }
53
+ function safeRelativePath(value) {
54
+ const normalized = value.replaceAll("\\", "/");
55
+ if (!normalized || normalized.startsWith("/") || normalized.includes("\0"))
56
+ return false;
57
+ return !normalized.split("/").some((part) => !part || part === "." || part === "..");
58
+ }
59
+ function sortValue(value) {
60
+ if (Array.isArray(value))
61
+ return value.map(sortValue);
62
+ if (!value || typeof value !== "object")
63
+ return value;
64
+ return Object.fromEntries(Object.entries(value)
65
+ .sort(([left], [right]) => left.localeCompare(right))
66
+ .map(([key, child]) => [key, sortValue(child)]));
67
+ }
@@ -0,0 +1,66 @@
1
+ import { contentHash, withContentHash } from "./common.js";
2
+ import { createAgentSnapshot, createHarnessRelease } from "./harness.js";
3
+ import { createRunManifest } from "./runs.js";
4
+ import { TasksetReleaseSchema } from "./tasksets.js";
5
+ const EMPTY_HASH = contentHash("");
6
+ const dependencyLock = { id: "fixture-lock", path: "package-lock.json", contentHash: EMPTY_HASH, sizeBytes: 0, mediaType: "application/json", visibility: "policy" };
7
+ const program = { id: "fixture-program", path: "harness.mjs", contentHash: EMPTY_HASH, sizeBytes: 0, mediaType: "text/javascript", visibility: "policy" };
8
+ export const genericToolConformance = fixture("generic-tool-v1", [
9
+ { name: "lookup", description: "Look up a deterministic value.", inputSchema: { type: "object" }, inputSchemaHash: contentHash({ type: "object" }), sideEffect: "read", timeoutMs: 1_000 },
10
+ ]);
11
+ export const marketingPortfolioConformance = fixture("marketing-portfolio-v1", [
12
+ { name: "get_portfolio_snapshot", description: "Read the fixture portfolio.", inputSchema: { type: "object" }, inputSchemaHash: contentHash({ type: "object" }), sideEffect: "read", timeoutMs: 1_000 },
13
+ { name: "submit_budget_decision", description: "Submit a fixture decision.", inputSchema: { type: "object" }, inputSchemaHash: contentHash({ type: "object" }), sideEffect: "write", timeoutMs: 1_000 },
14
+ ]);
15
+ function fixture(id, tools) {
16
+ const snapshot = createAgentSnapshot({
17
+ schemaVersion: "openpond.agentSnapshot.v1",
18
+ id: `${id}-agent`,
19
+ profileRelease: null,
20
+ instructions: [], skills: [], agents: [], toolDeclarations: tools,
21
+ capabilityRequirements: [], dependencyLock,
22
+ portability: { portable: true, blockers: [], localOnlyAssetRefs: [], hostPrivateAssetRefs: [] },
23
+ metadata: { conformanceFixture: id },
24
+ });
25
+ const harness = createHarnessRelease({
26
+ schemaVersion: "openpond.harnessRelease.v1",
27
+ id: `${id}-harness`,
28
+ agentSnapshot: { id: snapshot.id, contentHash: snapshot.contentHash },
29
+ program,
30
+ environment: { protocolVersion: "openpond.environment.v1", kind: "agent", entrypoint: id, stateful: true, deterministicSeeds: true, lifecycle: ["create", "reset", "step", "collect", "destroy"], networkPolicy: "none", defaultTimeoutMs: 5_000 },
31
+ tools,
32
+ lifecycle: { create: true, reset: true, step: true, collect: true, destroy: true, resetScope: "attempt" },
33
+ graderInterface: { visibleEvidence: ["output"], privilegedEvidence: ["expected"], privateVerifierIsolation: true },
34
+ policy: { policyVisibleFields: ["input"], privilegedFields: ["expectedOutput"], hiddenGraderRefs: [], connectedAppScopes: [] },
35
+ files: [], metadata: { conformanceFixture: id },
36
+ });
37
+ const tasksetContent = {
38
+ schemaVersion: "openpond.tasksetRelease.v1",
39
+ id: `${id}-taskset`, revision: 1,
40
+ harnessRelease: { id: harness.id, contentHash: harness.contentHash },
41
+ policy: harness.policy,
42
+ environment: harness.environment,
43
+ tools,
44
+ capabilities: [],
45
+ tasks: [
46
+ { id: `${id}-train`, clusterKey: `${id}-train`, split: "train", input: { prompt: "Use the declared tool." }, expectedOutput: { text: "done" }, policyVisibleContext: {}, privilegedContextRef: `${id}-expected`, artifactRefs: [], tags: ["conformance"] },
47
+ { id: `${id}-frozen`, clusterKey: `${id}-frozen`, split: "frozen_eval", input: { prompt: "Repeat the protocol." }, expectedOutput: { text: "done" }, policyVisibleContext: {}, privilegedContextRef: `${id}-expected-frozen`, artifactRefs: [], tags: ["conformance"] },
48
+ ],
49
+ graders: [{ id: `${id}-exact`, version: "1", kind: "content", weight: 1, hardGate: true, rewardEligible: true, privileged: true, config: { outputField: "text", expectedField: "text" } }],
50
+ metadata: { conformanceFixture: id },
51
+ };
52
+ const taskset = TasksetReleaseSchema.parse(withContentHash(tasksetContent));
53
+ const manifest = createRunManifest({
54
+ schemaVersion: "openpond.runManifest.v1",
55
+ id: `${id}-run`,
56
+ harnessRelease: { id: harness.id, contentHash: harness.contentHash },
57
+ tasksetRelease: { id: taskset.id, contentHash: taskset.contentHash },
58
+ model: { provider: "fixture", model: "scripted", revision: "1", artifactHash: null, tokenizerRevision: null, chatTemplateHash: null },
59
+ runtimeTarget: { adapterId: "fixture-runtime", placement: "local", runtimeVersion: "1", capabilityReceipt: contentHash(tools.map((tool) => tool.name)) },
60
+ limits: { maxTurns: 8, timeoutMs: 5_000, maxOutputBytes: 1_000_000, maximumSpendUsd: 0 },
61
+ approval: null,
62
+ createdAt: "2026-08-03T00:00:00.000Z",
63
+ metadata: { conformanceFixture: id },
64
+ });
65
+ return { snapshot, harness, taskset, manifest };
66
+ }
@@ -0,0 +1,87 @@
1
+ import { z } from "zod";
2
+ import { FailureClassSchema, ReleaseHashSchema, ReleaseIdSchema, contentHash } from "./common.js";
3
+ export const GraderEvidenceContentSchema = z.object({
4
+ schemaVersion: z.literal("openpond.graderEvidence.v1"),
5
+ graderId: ReleaseIdSchema,
6
+ graderVersion: z.string().trim().min(1).max(100),
7
+ score: z.number().min(0).max(1).nullable(),
8
+ passed: z.boolean(),
9
+ rewardEligible: z.boolean(),
10
+ failureClass: FailureClassSchema.nullable(),
11
+ feedback: z.array(z.string().max(20_000)).max(1_000),
12
+ visibleEvidenceRefs: z.array(ReleaseIdSchema).max(10_000),
13
+ privilegedEvidenceRefs: z.array(ReleaseIdSchema).max(10_000),
14
+ }).strict();
15
+ export const GraderEvidenceSchema = GraderEvidenceContentSchema.extend({ contentHash: ReleaseHashSchema }).strict();
16
+ export async function gradeEvidence(input) {
17
+ if (input.evidence.infrastructureError) {
18
+ return input.graders.map((grader) => evidence(grader, {
19
+ score: null,
20
+ passed: false,
21
+ rewardEligible: false,
22
+ failureClass: "infrastructure_failure",
23
+ feedback: [input.evidence.infrastructureError],
24
+ visibleEvidenceRefs: [],
25
+ privilegedEvidenceRefs: [],
26
+ }));
27
+ }
28
+ return Promise.all(input.graders.map(async (grader) => {
29
+ if (grader.kind === "model_judge") {
30
+ if (!input.modelJudge || grader.calibrationStatus !== "passed")
31
+ return evidence(grader, unavailable("Model judge is unavailable or uncalibrated."));
32
+ return evidence(grader, await input.modelJudge({ grader, task: input.task, evidence: input.evidence }));
33
+ }
34
+ if (grader.kind === "custom_verifier") {
35
+ if (!input.customVerifier)
36
+ return evidence(grader, unavailable("Custom verifier is unavailable."));
37
+ return evidence(grader, await input.customVerifier({ grader, task: input.task, evidence: input.evidence }));
38
+ }
39
+ if (grader.kind === "human")
40
+ return evidence(grader, unavailable("Human review is pending."));
41
+ return evidence(grader, gradeDeterministic(grader, input.task, input.evidence));
42
+ }));
43
+ }
44
+ function gradeDeterministic(grader, task, attempt) {
45
+ let passed = false;
46
+ const config = grader.config;
47
+ if (grader.kind === "content") {
48
+ const outputField = string(config.outputField) ?? "text";
49
+ const actual = string(attempt.output[outputField]);
50
+ const expected = string(config.expectedValue) ?? string(task.expectedOutput?.[string(config.expectedField) ?? "text"]);
51
+ passed = actual !== null && expected !== null && normalize(actual) === normalize(expected);
52
+ }
53
+ else if (grader.kind === "schema") {
54
+ passed = strings(config.requiredKeys).every((key) => Object.hasOwn(attempt.output, key));
55
+ }
56
+ else if (grader.kind === "artifact") {
57
+ const contains = string(config.refIncludes) ?? "";
58
+ passed = attempt.artifactRefs.some((ref) => ref.includes(contains));
59
+ }
60
+ else if (grader.kind === "runtime_event") {
61
+ passed = strings(config.requiredEvents).every((required) => attempt.runtimeEventRefs.some((ref) => ref.includes(required)));
62
+ }
63
+ else {
64
+ const fields = strings(config.fields);
65
+ const compared = fields.length ? fields : Object.keys(task.expectedOutput ?? {});
66
+ passed = compared.every((field) => Object.is(attempt.output[field], task.expectedOutput?.[field]));
67
+ }
68
+ return {
69
+ score: passed ? 1 : 0,
70
+ passed,
71
+ rewardEligible: grader.rewardEligible && passed,
72
+ failureClass: passed ? null : "policy_failure",
73
+ feedback: [passed ? "Deterministic grader passed." : "Deterministic grader failed."],
74
+ visibleEvidenceRefs: [...attempt.runtimeEventRefs, ...attempt.artifactRefs],
75
+ privilegedEvidenceRefs: grader.privileged ? [task.privilegedContextRef].filter((ref) => ref !== null) : [],
76
+ };
77
+ }
78
+ function evidence(grader, result) {
79
+ const content = GraderEvidenceContentSchema.parse({ schemaVersion: "openpond.graderEvidence.v1", graderId: grader.id, graderVersion: grader.version, ...result });
80
+ return GraderEvidenceSchema.parse({ ...content, contentHash: contentHash(content) });
81
+ }
82
+ function unavailable(message) {
83
+ return { score: null, passed: false, rewardEligible: false, failureClass: "grader_failure", feedback: [message], visibleEvidenceRefs: [], privilegedEvidenceRefs: [] };
84
+ }
85
+ function string(value) { return typeof value === "string" ? value : null; }
86
+ function strings(value) { return Array.isArray(value) ? value.filter((item) => typeof item === "string") : []; }
87
+ function normalize(value) { return value.normalize("NFKC").trim().replace(/[,,]/g, "").replace(/[.\s]+$/g, "").replace(/\s+/g, " "); }
@@ -0,0 +1,206 @@
1
+ import { z } from "zod";
2
+ import { FailureClassSchema, ImmutableArtifactRefSchema, ImmutableAssetRefSchema, ImmutableReleaseRefSchema, MetadataSchema, ReleaseHashSchema, ReleaseIdSchema, contentHash, } from "./common.js";
3
+ import { EnvironmentContractSchema, PolicyBoundarySchema, ToolDeclarationSchema } from "./tasksets.js";
4
+ import { createAttemptReceipt } from "./runs.js";
5
+ export const PortabilityReportSchema = z.object({
6
+ portable: z.boolean(),
7
+ blockers: z.array(z.string().trim().min(1).max(2_000)).max(1_000),
8
+ localOnlyAssetRefs: z.array(ReleaseIdSchema).max(10_000),
9
+ hostPrivateAssetRefs: z.array(ReleaseIdSchema).max(10_000),
10
+ }).strict();
11
+ export const AgentSnapshotContentSchema = z.object({
12
+ schemaVersion: z.literal("openpond.agentSnapshot.v1"),
13
+ id: ReleaseIdSchema,
14
+ profileRelease: ImmutableReleaseRefSchema.nullable(),
15
+ instructions: z.array(ImmutableAssetRefSchema).max(10_000),
16
+ skills: z.array(ImmutableAssetRefSchema).max(10_000),
17
+ agents: z.array(ImmutableAssetRefSchema).max(10_000),
18
+ toolDeclarations: z.array(ToolDeclarationSchema).max(200),
19
+ capabilityRequirements: z.array(z.object({
20
+ id: ReleaseIdSchema,
21
+ required: z.boolean(),
22
+ scopes: z.array(z.string().trim().min(1).max(500)).max(100),
23
+ }).strict()).max(200),
24
+ dependencyLock: ImmutableAssetRefSchema,
25
+ portability: PortabilityReportSchema,
26
+ metadata: MetadataSchema,
27
+ }).strict();
28
+ export const AgentSnapshotSchema = AgentSnapshotContentSchema.extend({ contentHash: ReleaseHashSchema }).strict();
29
+ export const LifecycleContractSchema = z.object({
30
+ create: z.literal(true),
31
+ reset: z.literal(true),
32
+ step: z.literal(true),
33
+ collect: z.literal(true),
34
+ destroy: z.literal(true),
35
+ resetScope: z.enum(["task", "attempt"]),
36
+ }).strict();
37
+ export const GraderInterfaceContractSchema = z.object({
38
+ visibleEvidence: z.array(ReleaseIdSchema).max(1_000),
39
+ privilegedEvidence: z.array(ReleaseIdSchema).max(1_000),
40
+ privateVerifierIsolation: z.boolean(),
41
+ }).strict();
42
+ export const HarnessReleaseContentSchema = z.object({
43
+ schemaVersion: z.literal("openpond.harnessRelease.v1"),
44
+ id: ReleaseIdSchema,
45
+ agentSnapshot: ImmutableReleaseRefSchema,
46
+ program: ImmutableAssetRefSchema,
47
+ environment: EnvironmentContractSchema,
48
+ tools: z.array(ToolDeclarationSchema).max(200),
49
+ lifecycle: LifecycleContractSchema,
50
+ graderInterface: GraderInterfaceContractSchema,
51
+ policy: PolicyBoundarySchema,
52
+ files: z.array(ImmutableAssetRefSchema).max(100_000),
53
+ metadata: MetadataSchema,
54
+ }).strict();
55
+ export const HarnessReleaseSchema = HarnessReleaseContentSchema.extend({ contentHash: ReleaseHashSchema }).strict();
56
+ export const ModelActionSchema = z.object({
57
+ id: ReleaseIdSchema,
58
+ turn: z.number().int().nonnegative(),
59
+ kind: z.enum(["message", "tool_call", "terminal"]),
60
+ name: ReleaseIdSchema.nullable(),
61
+ arguments: z.record(z.string(), z.unknown()),
62
+ content: z.string().max(1_000_000).nullable(),
63
+ }).strict();
64
+ export const ToolObservationSchema = z.object({
65
+ actionId: ReleaseIdSchema,
66
+ turn: z.number().int().nonnegative(),
67
+ terminal: z.boolean(),
68
+ output: z.record(z.string(), z.unknown()),
69
+ artifactRefs: z.array(ImmutableArtifactRefSchema).max(100_000),
70
+ }).strict();
71
+ export async function executeRuntimeProtocol(input) {
72
+ const now = input.now ?? (() => new Date().toISOString());
73
+ const startedAt = now();
74
+ const controller = new AbortController();
75
+ const abort = () => controller.abort(input.signal?.reason ?? new Error("Harness execution cancelled."));
76
+ input.signal?.addEventListener("abort", abort, { once: true });
77
+ const timer = setTimeout(() => controller.abort(new Error("Harness execution timed out.")), input.manifest.limits.timeoutMs);
78
+ timer.unref?.();
79
+ const events = [];
80
+ const actions = [];
81
+ const observations = [];
82
+ let sequence = 0;
83
+ let lease = null;
84
+ let terminal = false;
85
+ let failureClass = null;
86
+ let output = {};
87
+ let artifactRefs = [];
88
+ const event = (type, payload, metadata = {}) => {
89
+ events.push({ sequence: sequence++, type, payloadHash: contentHash(payload), metadata });
90
+ };
91
+ try {
92
+ lease = await input.runtime.create({ manifest: input.manifest, taskId: input.taskId, seed: input.seed, signal: controller.signal });
93
+ event("created", { leaseId: lease.id });
94
+ await input.runtime.reset(lease, { seed: input.seed, signal: controller.signal });
95
+ event("reset", { seed: input.seed });
96
+ for (const candidate of input.actions.slice(0, input.manifest.limits.maxTurns)) {
97
+ if (controller.signal.aborted)
98
+ throw controller.signal.reason;
99
+ const action = ModelActionSchema.parse(candidate);
100
+ actions.push(action);
101
+ event("action", action);
102
+ const observation = ToolObservationSchema.parse(await input.runtime.step(lease, action, controller.signal));
103
+ if (observation.actionId !== action.id || observation.turn !== action.turn) {
104
+ throw new Error(`Observation ${observation.actionId} does not match action ${action.id}.`);
105
+ }
106
+ observations.push(observation);
107
+ output = observation.output;
108
+ event("observation", observation);
109
+ if (observation.terminal) {
110
+ terminal = true;
111
+ event("terminal", observation);
112
+ break;
113
+ }
114
+ }
115
+ }
116
+ catch (error) {
117
+ failureClass = controller.signal.aborted
118
+ ? input.signal?.aborted ? "cancelled" : "timeout"
119
+ : "infrastructure_failure";
120
+ event("failure", { message: error instanceof Error ? error.message : String(error) }, { rewardEligible: false });
121
+ }
122
+ finally {
123
+ clearTimeout(timer);
124
+ input.signal?.removeEventListener("abort", abort);
125
+ if (lease) {
126
+ try {
127
+ const collected = await input.runtime.collect(lease, new AbortController().signal);
128
+ artifactRefs = collected.artifactRefs;
129
+ event("collected", collected);
130
+ }
131
+ catch (error) {
132
+ failureClass = "infrastructure_failure";
133
+ event("failure", { message: error instanceof Error ? error.message : String(error) }, { phase: "collect", rewardEligible: false });
134
+ }
135
+ try {
136
+ await input.runtime.destroy(lease);
137
+ event("destroyed", { leaseId: lease.id });
138
+ }
139
+ catch (error) {
140
+ failureClass = "infrastructure_failure";
141
+ event("failure", { message: error instanceof Error ? error.message : String(error) }, { phase: "destroy", rewardEligible: false });
142
+ }
143
+ }
144
+ }
145
+ const traceContent = {
146
+ schemaVersion: "openpond.harnessTrace.v1",
147
+ manifest: { id: input.manifest.id, contentHash: input.manifest.contentHash },
148
+ taskId: input.taskId,
149
+ seed: input.seed,
150
+ events,
151
+ actions,
152
+ observations,
153
+ terminal,
154
+ failureClass,
155
+ output,
156
+ };
157
+ const trace = HarnessTraceSchema.parse({ ...traceContent, contentHash: contentHash(traceContent) });
158
+ const completedAt = now();
159
+ const receipt = createAttemptReceipt({
160
+ schemaVersion: "openpond.attemptReceipt.v1",
161
+ id: `attempt-${contentHash([input.manifest.contentHash, input.taskId, input.seed, trace.contentHash]).slice(0, 24)}`,
162
+ runManifest: { id: input.manifest.id, contentHash: input.manifest.contentHash },
163
+ taskId: input.taskId,
164
+ seed: input.seed,
165
+ terminal,
166
+ failureClass,
167
+ outputHash: terminal ? contentHash(output) : null,
168
+ traceHash: trace.contentHash,
169
+ artifactRefs,
170
+ graderEvidenceRefs: [],
171
+ startedAt,
172
+ completedAt,
173
+ latencyMs: Math.max(0, Date.parse(completedAt) - Date.parse(startedAt)),
174
+ costUsd: null,
175
+ legacyAttemptRef: null,
176
+ metadata: {},
177
+ });
178
+ return { receipt, trace, output };
179
+ }
180
+ export function createAgentSnapshot(input) {
181
+ const content = AgentSnapshotContentSchema.parse(input);
182
+ return AgentSnapshotSchema.parse({ ...content, contentHash: contentHash(content) });
183
+ }
184
+ export function createHarnessRelease(input) {
185
+ const content = HarnessReleaseContentSchema.parse(input);
186
+ return HarnessReleaseSchema.parse({ ...content, contentHash: contentHash(content) });
187
+ }
188
+ export const HarnessLifecycleEventSchema = z.object({
189
+ sequence: z.number().int().nonnegative(),
190
+ type: z.enum(["created", "reset", "action", "observation", "terminal", "failure", "collected", "destroyed"]),
191
+ payloadHash: ReleaseHashSchema,
192
+ metadata: MetadataSchema,
193
+ }).strict();
194
+ export const HarnessTraceSchema = z.object({
195
+ schemaVersion: z.literal("openpond.harnessTrace.v1"),
196
+ manifest: ImmutableReleaseRefSchema,
197
+ taskId: ReleaseIdSchema,
198
+ seed: z.string().trim().min(1).max(500),
199
+ events: z.array(HarnessLifecycleEventSchema).max(1_000_000),
200
+ actions: z.array(ModelActionSchema).max(100_000),
201
+ observations: z.array(ToolObservationSchema).max(100_000),
202
+ terminal: z.boolean(),
203
+ failureClass: FailureClassSchema.nullable(),
204
+ output: z.record(z.string(), z.unknown()),
205
+ contentHash: ReleaseHashSchema,
206
+ }).strict();
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from "./common.js";
2
+ export * from "./graders.js";
3
+ export * from "./harness.js";
4
+ export * from "./runs.js";
5
+ export * from "./tasksets.js";