← All posts

JavaScript in the agent harness: when a script is cheaper than model reasoning

A sandboxed JS interpreter replaces some tool hops and LLM arithmetic. Notes from a Habr deep dive on LM Studio and LangChain.

JavaScript in the agent harness: when a script is cheaper than model reasoning
Contents

In brief

A language model continues text. An agent appears when you wrap it in a harness: tool calls, memory, rules, human confirmation. A Habr article argues for the next cut: let the model write short JavaScript in a narrow sandbox so arithmetic, dates, and JSON filters stop burning context and failing inside “reasoning.” This is not “run any code on the host.” It is a deliberately stripped compute layer.

What happened

The same model in different harnesses produces different quality. System instructions, the tool set, and the call loop often matter more than the flagship name. Every tool round-trip is another request and response in context; long chains get expensive even when the logic is school arithmetic.

The dangerous path is a full shell and network: a model mistake, a twisted reading of “delete this,” or prompt injection from a page can wipe files or leak secrets. A real sandbox (container, remote runner) fixes that at the cost of complexity. A narrower option is a JavaScript interpreter with no host filesystem and no network: the model writes a script, the harness runs it with time and memory limits.

In LM Studio, the js-code-sandbox plugin exposes run_javascript. Without it the model answers with a training-cutoff date; with a script it takes “now” from the runtime. Harder: “the third Thursday after the next full moon” — the model writes the calculation instead of holding the whole chain in its head. The author notes it is a linear lunar approximation, not an astronomy engine — the point still stands.

In LangChain the same idea is CodeInterpreterMiddleware on QuickJS. The system prompt states the walls: no fetch, no real filesystem, ~5s timeout, 64 MB memory. “Current date” from the script is banned by default: date is a separate tool, not a hidden superpower of the interpreter. If the script may call declared tools (programmatic tool calling), the model invokes eval once and the script calls tools.getCurrentDatetime() and continues — no dance of “ask date → think → ask eval.”

Why it matters

Teams grow a tool catalog: calculator, sum, percentile, filter. One interpreter covers that class with a syntax models already know. Deterministic code is faster and more honest than a long inner monologue — especially on lists, dates, and JSON that another tool just returned.

The price is boundary discipline. An in-process sandbox without network will not read ~/.ssh, but a tight loop still needs a timeout. “Confirm every suspicious step” decays into auto-approve. If the script can call external tools, the attack surface returns: injected instructions can request something that is no longer pure arithmetic.

For an agent architect this is a fork: not “JS instead of MCP,” but where logic must be executable versus where it is a model decision. The harness still matters more than the model — the same thesis as in agent-loop writing, with a concrete lever: a compute layer inside the cycle.

In practice

  1. Split “compute / filter / transform” from “decide which tool to call.” Give the first an interpreter; give the second an explicit catalog.
  2. Cut sandbox powers: no network, no arbitrary host filesystem, time and memory caps; log the source script.
  3. Do not hide “current date” or CRM access inside the interpreter “because it is convenient” — declare tools and, if you need PTC, an allowlist of calls from the script.
  4. Replay a trivial scenario locally in LM Studio before dragging QuickJS into your agent: you will see whether the model writes a script or keeps guessing.
  5. Count cost: one eval instead of five reasoning rounds is often cheaper, but a wrong script still needs a test or a result schema.

Takeaway

JavaScript in the harness hands the model what it is bad at (exact arithmetic, stable transforms) without handing it the keys to the machine. The win is not “one more tool.” It is that deterministic logic stops occupying context and breaking on every hop. Sandbox walls and an allowlist of calls matter more than the fact that the agent “can code.”