Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to WASM-OJ are recorded here. Releases follow
[Semantic Versioning](https://semver.org/) and the contract/package versioning policy in
[the versioning policy](docs/versioning.md).

## Unreleased

- Accept runtime-bundle interactors (for example CPython) in `Runner.interact` on the server
and in the browser runner Worker. Either side of an interactive session may now be a
standalone Wasm module or a runtime bundle that provides streaming fd 0.

## 0.2.3 - 2026-10-05

- Download pinned browser toolchain assets before the compiler timeout starts, so slow
Expand Down
3 changes: 2 additions & 1 deletion docs/library-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,8 @@ set/multiset, output-file sets, and standalone sandboxed Wasm checkers.

Interactive cases start contestant and interactor concurrently with full-duplex pipes, independent
resource policies, process-local deterministic clocks, and secret inputs mounted only on the
interactor side. Runtime bundles that cannot provide streaming fd 0 are rejected for interaction.
interactor side. Either side may be a standalone Wasm module or a runtime bundle such as CPython;
runtime bundles that cannot provide streaming fd 0 are rejected for interaction.

Each case executes under the broad hard policy once. Correct output and the same normalized metrics
are evaluated against ordered cumulative `baseline`, `efficient`, and `optimal` policies. The
Expand Down
3 changes: 0 additions & 3 deletions src/runtime/runner.worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -412,9 +412,6 @@ async function interactArtifacts(
): Promise<InteractiveRunResult> {
const started = performance.now();
if (!runtimeDrivers) throw new Error("The WASM-OJ runtime-driver registry is not initialized.");
if (request.interactor.kind !== "wasm") {
throw new Error("Interactive judge artifacts must be standalone Wasm modules.");
}
progress(request.requestId, "loading-toolchain", "Resolving contestant and interactor runtimes", 0.1);
const [contestant, interactor] = await Promise.all([
prepareArtifactInteraction(
Expand Down
40 changes: 40 additions & 0 deletions src/server/judge.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,46 @@ describe.skipIf(!enabled)("real server judge contracts", () => {
});
});

it("runs a CPython interactor against a compiled contestant", { timeout: 300_000 }, async () => {
const contestant = await compileC("bundle-contestant", [
"#include <stdio.h>",
"int main(void) {",
" int challenge = 0;",
" if (scanf(\"%d\", &challenge) != 1) return 2;",
" printf(\"%d\\n\", challenge + 1);",
" fflush(stdout);",
" return 0;",
"}",
].join("\n"));
const built = await engine.compile({
projectId: "judge-integration:python-interactor",
name: "python-interactor",
language: "python",
entry: "main.py",
files: {
"main.py": [
"import sys",
"target = int(open(sys.argv[1]).read())",
"print(target - 1, flush=True)",
"sys.exit(0 if int(input()) == target else 1)",
].join("\n") + "\n",
},
}, { cache: false });
expect(built.success).toBe(true);
expect(built.artifact?.kind).toBe("runtime-bundle");

const result = await engine.interact(contestant, built.artifact!, {
interactor: { args: ["/judge/input.txt"], files: { "/judge/input.txt": "42\n" } },
});

expect(result).toMatchObject({
contestantToInteractor: "42\n",
interactorToContestant: "41\n",
contestant: { code: 0, termination: "exited" },
interactor: { code: 0, termination: "exited" },
});
});

async function compileC(name: string, source: string): Promise<BuildArtifact> {
const entry = `src/${name}.c`;
const input: CompileInput = {
Expand Down
3 changes: 0 additions & 3 deletions src/server/server-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,6 @@ export class ServerRunner implements Runner {
if (this.activeOperation || this.cacheClearActive) {
throw new Error("ServerRunner accepts one active operation at a time.");
}
if (interactorArtifact.kind !== "wasm") {
throw new Error("Interactive judge artifacts must be standalone Wasm modules.");
}
const operation = createServerRunOperation(this.generation);
this.activeOperation = operation;
this.inFlightRuns.add(operation);
Expand Down