Skip to content

Arrival

Arrival is a sandboxed R7RS-subset Scheme for LLM agents that need to compute, not just call tools. Embed it when an agent should filter, map, and compose inside a symbolic REPL and hand plain JavaScript back to the host, instead of emitting a stream of opaque JSON tool calls.

The language is a faithful R7RS subset: no set!, no call/cc. Syntax extends toward Clojure, Racket, and Common Lisp only where R7RS is silent; a violation gets a diagnostic that names the intended form. The API is 0.x and still settling.

@inhuman.tools/arrival is ESM-only and needs Node.js 22 or newer.

Terminal window
npm install @inhuman.tools/arrival
import { exec } from "@inhuman.tools/arrival";
// One plain JS value per top-level form. The base roster assembles on first call.
const [result] = await exec(`(filter (lambda (x) (> x 5)) (list 1 3 7 9 2))`);
console.log(result); // [7, 9]

exec(code, options?) parses, runs, and unwraps each top-level result to plain JS. BASE_ROSTER (R7RS, the default SRFIs, and the polyglot packs) is always assembled. Pass capabilities to add tools; they do not replace the base.

A bare exec or execState mints a fresh scope (scope ?? LexicalScope.fresh()), so top-level defines do not accumulate. Reuse one LexicalScope.fresh() when a multi-turn session should keep bindings. Isolation across calls is the default.

Call exec from the library when your process is the host. Use the CLI when a person or a script should run a program, open a REPL, or check a file without embedding the interpreter.

Terminal window
npx @inhuman.tools/arrival-cli --help
# or: npm install -g @inhuman.tools/arrival-cli # installs the `arrival` bin

@inhuman.tools/arrival-cli is arrival run, a REPL, and arrival check. Its contract is packages/arrival-cli; this page does not restate it.

The base reaches nothing ambient by construction: no filesystem, no process, no network, no ports, no clock, no random, and no host globals (window, global, process, require). That is algebraic as well as practical. An ambient read has no construction site to root a value’s lineage at, so effects return as capability verbs that stamp provenance at the membrane.

At 0.x this is not a hard security boundary. Sandbox escape is still feasible, at least via property access and some rosetta-layer aspects.

Do not expose Arrival to untrusted input without additional isolation, use it in security-critical contexts, deploy it without containerization, or trust the sandbox. Isolation reports: security@here.build.

Interpretation is roughly 10–100× native JS. Keep Scheme for orchestration; register hot functions as capability verbs.

  • A tool is an EnvCapability: a name, a doc line, a typed contract, an implementation. Authoring is packages/arrival/docs/writing-capabilities.md.
  • Host data enters as a typed define/overridable parameter through the overridable capability and config.params, not as an ambient global.
  • staticValidation: "on" reports every problem before evaluation (default "off"). The pass cannot see bindings a (require …) spills at runtime.
  • Lineage is opt-in: tap: new EvalTrace() from @inhuman.tools/arrival/provenance. Without a tap, provenance reads [].
  • Read packages/arrival/docs/PRINCIPLES.md, docs/PROVENANCE.md, and, for agents, docs/llm-agent-card.md.

Early-stage work welcomes security review of the sandbox, performance benchmarks, Chibi conformance (every flipped it.fails is a gift), and teaching doors on dead-end errors. Issues are welcome; external pull requests are not the current focus. Sibling packages are listed in the repository README. Each package’s LICENSE.md is authoritative (MIT; the interpreter is a fork of LIPS.js).

  • arrival/README.md
  • arrival/packages/arrival/README.md