Files
NexusOS/interface/web/src/preview/run-langs.test.js
T
Athena KaminskyandClaude Opus 5 affba1805c feat(chat): add run_snippet, an execution track beside the render track
render_preview validates markup and hands it to the browser, which renders it
in an opaque-origin sandboxed iframe. Nothing executes server-side. That model
fits HTML/SVG/JSX and cannot fit C, Rust or Erlang, which need a real
toolchain - so those get a second tool instead of a widened first one.

The split is the feature: the model picks a track by picking a tool, rather
than picking a `lang` value from an enum where half the entries run
server-side and half do not.

synapse/code_run.py compiles and runs one file in a throwaway directory and
returns a ```nexus-run fence carrying the source and its captured output
together, so a model cannot paste output without the code that produced it.
Backticks in the source are re-encoded as ` - still valid JSON, and it
cannot close the fence early.

It is not a sandbox, and the module docstring says so up front. What it gives
is containment by layers: consent (an action tool, gated by
action_tool_policy, per-call Approve/Deny on "ask"), static screening, a
scrubbed environment in a temp dir, wall-clock and POSIX rlimits, and a
network namespace on Linux where unprivileged userns are available. Screening
is a tripwire against a model reaching for `requests` out of habit, not a
boundary against an adversary; layers 1 and 3-5 are the load-bearing ones.

Backend RUN_LANGS and frontend run-langs.js are separate registries because
the two sides need different things - one executes, one labels - and neither
should depend on the other at runtime. tests/test_tools.py asserts the key
sets and the fence tag stay equal, so drift fails the gate instead of
rendering a run result under the wrong language.

tests/snippet_probes/ is a data catalog rather than inlined cases, so adding a
language is a data change and the meta-tests can assert every RUN_LANGS key
has both a smoke probe and a screening probe. Probes skip cleanly on hosts
without the toolchain.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 14:30:03 -05:00

84 lines
3.2 KiB
JavaScript

/*
* The run-result envelope: what parseRunResult will and won't accept.
*
* Everything this parses arrived as text a language model chose to paste into
* its reply, so the interesting cases are all the malformed ones. A bad envelope
* has to return null - the caller then shows the raw block as code, which is
* ugly but honest - rather than yield a half-built object that renders as a run
* that never happened.
*/
import { test } from "node:test";
import assert from "node:assert/strict";
import { RUN_LANGS, RUN_FENCE_LANG, parseRunResult } from "./run-langs.js";
const envelope = (over = {}) => JSON.stringify({
lang: "python",
source: "print(1)",
stdout: "1\n",
stderr: "",
exit_code: 0,
...over,
});
test("the fence tag is the one the backend emits", () => {
assert.equal(RUN_FENCE_LANG, "nexus-run");
});
test("every language has a display label", () => {
for (const [name, spec] of Object.entries(RUN_LANGS)) {
assert.equal(typeof spec.label, "string", name);
assert.ok(spec.label.length, name);
}
});
test("a well-formed envelope parses into display fields", () => {
const run = parseRunResult(envelope());
assert.equal(run.lang, "python");
assert.equal(run.label, "Python");
assert.equal(run.source, "print(1)");
assert.equal(run.stdout, "1\n");
assert.equal(run.exitCode, 0);
});
test("a backtick-escaped source round-trips through JSON", () => {
// run_snippet re-encodes ` as ` so the source cannot close the fence.
const run = parseRunResult('{"lang":"python","source":"x = \\u0060a\\u0060","exit_code":0}');
assert.equal(run.source, "x = `a`");
});
test("a non-zero exit code is preserved, not coerced away", () => {
// `exitCode || null` would turn 0 into null and hide a clean exit; a plain
// falsy check on the other side would call a failing program successful.
assert.equal(parseRunResult(envelope({ exit_code: 2 })).exitCode, 2);
assert.equal(parseRunResult(envelope({ exit_code: 0 })).exitCode, 0);
});
test("a missing exit code becomes null rather than a guess", () => {
assert.equal(parseRunResult(envelope({ exit_code: undefined })).exitCode, null);
assert.equal(parseRunResult(envelope({ exit_code: "0" })).exitCode, null);
});
test("absent streams read as empty, never undefined", () => {
const run = parseRunResult('{"lang":"c","source":"int main(){}","exit_code":0}');
assert.equal(run.stdout, "");
assert.equal(run.stderr, "");
});
test("malformed or foreign envelopes are rejected", () => {
assert.equal(parseRunResult("not json at all"), null);
assert.equal(parseRunResult("null"), null);
assert.equal(parseRunResult("[1,2,3]"), null);
assert.equal(parseRunResult('"a string"'), null);
assert.equal(parseRunResult(envelope({ lang: "haskell" })), null);
assert.equal(parseRunResult(envelope({ source: undefined })), null);
assert.equal(parseRunResult(envelope({ source: 42 })), null);
});
test("a prototype key is not mistaken for a supported language", () => {
// `data.lang in RUN_LANGS` would be true for "toString" and read the label
// off Object.prototype - a run panel titled with a function body.
assert.equal(parseRunResult(envelope({ lang: "toString" })), null);
assert.equal(parseRunResult(envelope({ lang: "constructor" })), null);
});