@openpond/evals 0.12.4 → 0.12.6

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 (46) hide show
  1. package/LEARNING.md +21 -0
  2. package/dist/javascript-isolate-process-entry.js +4 -1
  3. package/dist/javascript-isolate-process-source.js +1 -1
  4. package/dist/javascript-isolate-process.js +36 -2
  5. package/dist/javascript-isolate.js +8 -2
  6. package/dist/javascript-verifier-node.js +1 -0
  7. package/dist/javascript-verifier-worker-source.js +1 -1
  8. package/dist/learning/admission.js +1 -1
  9. package/dist/learning/contracts.js +12 -0
  10. package/dist/learning/index.js +1 -0
  11. package/dist/learning/iteration-dispatch-commands.js +1 -1
  12. package/dist/learning/iteration-dispatch-contracts.js +27 -0
  13. package/dist/learning/iteration-dispatch-deferral.js +28 -0
  14. package/dist/learning/iteration-dispatch-worker.js +34 -5
  15. package/dist/learning/reward-authoring.js +4 -2
  16. package/dist/types/javascript-isolate-process.d.ts +7 -0
  17. package/dist/types/javascript-isolate-process.d.ts.map +1 -1
  18. package/dist/types/javascript-isolate.d.ts +3 -1
  19. package/dist/types/javascript-isolate.d.ts.map +1 -1
  20. package/dist/types/javascript-verifier-node.d.ts +1 -0
  21. package/dist/types/javascript-verifier-node.d.ts.map +1 -1
  22. package/dist/types/learning/batch-service.d.ts +1 -0
  23. package/dist/types/learning/batch-service.d.ts.map +1 -1
  24. package/dist/types/learning/contracts.d.ts +42 -0
  25. package/dist/types/learning/contracts.d.ts.map +1 -1
  26. package/dist/types/learning/index.d.ts +1 -0
  27. package/dist/types/learning/index.d.ts.map +1 -1
  28. package/dist/types/learning/iteration-dispatch-contracts.d.ts +45 -0
  29. package/dist/types/learning/iteration-dispatch-contracts.d.ts.map +1 -1
  30. package/dist/types/learning/iteration-dispatch-deferral.d.ts +71 -0
  31. package/dist/types/learning/iteration-dispatch-deferral.d.ts.map +1 -0
  32. package/dist/types/learning/iteration-dispatch-worker.d.ts +7 -2
  33. package/dist/types/learning/iteration-dispatch-worker.d.ts.map +1 -1
  34. package/dist/types/learning/operations.d.ts +38 -0
  35. package/dist/types/learning/operations.d.ts.map +1 -1
  36. package/dist/types/learning/repository.d.ts +30 -0
  37. package/dist/types/learning/repository.d.ts.map +1 -1
  38. package/dist/types/learning/reward-authoring.d.ts.map +1 -1
  39. package/dist/types/learning/transport.d.ts +49 -0
  40. package/dist/types/learning/transport.d.ts.map +1 -1
  41. package/package.json +2 -2
  42. package/schemas/learning/v1/command-request.schema.json +119 -93
  43. package/schemas/learning/v1/dispatch.schema.json +100 -1
  44. package/schemas/learning/v1/evidence.schema.json +3 -0
  45. package/schemas/learning/v1/example-submission.schema.json +3 -0
  46. package/schemas/learning/v1/source.schema.json +20 -0
@@ -1,6 +1,18 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { JavaScriptIsolateExecutionError, validateJavaScriptIsolateInput } from "./javascript-isolate.js";
3
3
  import { assertBoundedTaskJson } from "./task-schema.js";
4
+ /** Created only by the trusted parent wall timer, never from guest error text.
5
+ * The process executor rejects with this error only after child exit. */
6
+ export class JavaScriptVerifierHostTimeoutError extends Error {
7
+ timeoutMs;
8
+ elapsedMs;
9
+ constructor(timeoutMs, elapsedMs) {
10
+ super("verifier_timeout");
11
+ this.timeoutMs = timeoutMs;
12
+ this.elapsedMs = elapsedMs;
13
+ this.name = "JavaScriptVerifierHostTimeoutError";
14
+ }
15
+ }
4
16
  /** A Node subprocess owns the interpreter; completion always waits for its exit. */
5
17
  export async function executeJavaScriptIsolateInProcess(input) {
6
18
  input.signal?.throwIfAborted();
@@ -12,8 +24,9 @@ export async function executeJavaScriptIsolateInProcess(input) {
12
24
  // The authored source remains JSON data passed to QuickJS, never host code.
13
25
  const program = `globalThis.__openpondIsolateInput = JSON.parse(${JSON.stringify(JSON.stringify(data))});\n${stripTypeScript ? 'globalThis.__openpondIsolateInput.source = require("node:module").stripTypeScriptTypes(globalThis.__openpondIsolateInput.source, { mode: "strip" });\n' : ""}${javascriptIsolateProcessSource}`;
14
26
  return new Promise((resolve, reject) => {
27
+ const startedAt = performance.now();
15
28
  const child = spawn("node", ["--max-old-space-size=64", "--input-type=commonjs", "-"], {
16
- stdio: ["pipe", "pipe", "pipe"],
29
+ stdio: ["pipe", "pipe", "pipe", "ipc"],
17
30
  env: { PATH: process.env.PATH },
18
31
  windowsHide: true,
19
32
  });
@@ -26,7 +39,26 @@ export async function executeJavaScriptIsolateInProcess(input) {
26
39
  child.kill("SIGKILL");
27
40
  };
28
41
  const cancel = () => stop(signal?.reason instanceof Error ? signal.reason : error("cancelled"));
29
- const timer = setTimeout(() => stop(input.errorPrefix === "candidate" ? new JavaScriptIsolateExecutionError("candidate_timeout") : error("timeout")), input.timeoutMs);
42
+ const verifier = input.errorPrefix === "verifier";
43
+ let executionReady = !verifier;
44
+ const timedOut = () => stop(input.errorPrefix === "candidate"
45
+ ? new JavaScriptIsolateExecutionError("candidate_timeout")
46
+ : input.errorPrefix === "verifier"
47
+ ? new JavaScriptVerifierHostTimeoutError(input.timeoutMs, performance.now() - startedAt)
48
+ : error("timeout"));
49
+ // Cold Node/WASM initialization has its own finite bound. It must not
50
+ // consume the authored verifier budget, and cancellation covers both phases.
51
+ let timer = setTimeout(timedOut, verifier ? 30_000 : input.timeoutMs);
52
+ child.on("message", (message) => {
53
+ if (!verifier || executionReady || !message || typeof message !== "object" ||
54
+ !("type" in message) || message.type !== "execution_ready") {
55
+ stop(error("process_invalid_startup_message"));
56
+ return;
57
+ }
58
+ executionReady = true;
59
+ clearTimeout(timer);
60
+ timer = setTimeout(timedOut, input.timeoutMs);
61
+ });
30
62
  signal?.addEventListener("abort", cancel, { once: true });
31
63
  child.stdout.setEncoding("utf8");
32
64
  child.stdout.on("data", (chunk) => {
@@ -53,6 +85,8 @@ export async function executeJavaScriptIsolateInProcess(input) {
53
85
  try {
54
86
  if (code !== 0)
55
87
  throw error("process_exited_without_result");
88
+ if (!executionReady)
89
+ throw error("process_exited_before_ready");
56
90
  const message = JSON.parse(response);
57
91
  if (!message || typeof message !== "object" || !("ok" in message))
58
92
  throw error("process_invalid_response");
@@ -4,11 +4,11 @@ export class JavaScriptIsolateExecutionError extends Error {
4
4
  constructor(message) { super(message); this.name = "JavaScriptIsolateExecutionError"; }
5
5
  }
6
6
  /** Authored code is interpreter data, with no host functions or module loader. */
7
- export async function executeJavaScriptIsolate(input) {
7
+ export async function executeJavaScriptIsolate(input, host) {
8
8
  validateJavaScriptIsolateInput(input);
9
9
  input.signal?.throwIfAborted();
10
10
  const error = (suffix) => new Error(`${input.errorPrefix}_${suffix}`);
11
- const deadline = Date.now() + input.timeoutMs;
11
+ let deadline = Date.now() + input.timeoutMs;
12
12
  const { default: variant } = await import("@jitl/quickjs-singlefile-cjs-release-sync");
13
13
  input.signal?.throwIfAborted();
14
14
  const module = await newQuickJSWASMModuleFromVariant(variant);
@@ -20,6 +20,12 @@ export async function executeJavaScriptIsolate(input) {
20
20
  runtime.setMaxStackSize(262_144);
21
21
  runtime.setInterruptHandler(() => Date.now() >= deadline || input.signal?.aborted === true);
22
22
  const context = scope.manage(runtime.newContext());
23
+ // Only a trusted process owner supplies this hook. Interpreter startup is
24
+ // bounded by that owner; authored source gets its pinned execution budget.
25
+ if (host) {
26
+ deadline = Date.now() + input.timeoutMs;
27
+ host.onExecutionReady();
28
+ }
23
29
  if (input.deterministic) {
24
30
  scope.manage(context.unwrapResult(context.evalCode("globalThis.Date = undefined; Math.random = () => { throw new Error('environment_random_requires_explicit_seed'); };")));
25
31
  }
@@ -1,6 +1,7 @@
1
1
  import { assertIsolatedVerifierRuntime, JavaScriptVerifierResultSchema } from "./javascript-verifier-contract.js";
2
2
  import { executeJavaScriptIsolateInWorker } from "./javascript-isolate-node.js";
3
3
  import { executeJavaScriptIsolateInProcess } from "./javascript-isolate-process.js";
4
+ export { JavaScriptVerifierHostTimeoutError } from "./javascript-isolate-process.js";
4
5
  /** Resolves/rejects only after the execution owner has stopped the worker. */
5
6
  export async function executeJavaScriptVerifierInWorker(input) {
6
7
  assertIsolatedVerifierRuntime(input.runtime);