AJS Examples

Runnable AJS agents, from first steps to multi-agent pipelines.

AJS is a subset of JavaScript that compiles to data: a JSON program the VM runs in a sandbox, under a budget, with no network or storage unless the host grants it. It is for hosts that need to run code they did not write, such as an agent a user submitted or a model generated, without trusting it.

Every ajs block on these pages is live: you can edit the code and run it again in place. Each chapter is built around an agent you can run (the ones that call a model show their code and how to run it locally), then what to notice about it, a variation, and the place where it differs from JavaScript or where it can fail. Here is the smallest one. Change 'World' and run it again:

function hello({ name = 'World' }) {
  return { message: `Hello, ${name}!` }
}

What to notice: an agent takes one object of named inputs and returns a value, here an object. The page calls it with {}, so name takes its default.

How the examples run

They run on the same sandboxed VM you would embed, under the same rules:

To run an example on your own machine, transpile it and hand the AST (the JSON program the transpiler produces) to the VM, with the page's budget and network allowlist. Save the agent as hello.ajs, then:

import { readFileSync } from 'node:fs'
// `transpile` turns AJS source into the agent's AST (TJS uses `tjs()` instead).
import { transpile } from 'tjs-lang/lang'
import { AgentVM } from 'tjs-lang/vm-ast'

const { ast } = transpile(readFileSync('hello.ajs', 'utf8'))
const vm = new AgentVM()
const run = await vm.run(
  ast,
  { name: 'Ada' },
  {
    fuel: 10_000,
    context: {
      allowedFetchDomains: [
        'api.open-meteo.com',
        'itunes.apple.com',
        'api.github.com',
      ],
    },
  }
)
console.log(run.result) // { message: 'Hello, Ada!' }
console.log(run.error) // undefined
console.log(run.fuelUsed) // about 0.6

Locally you can pass real arguments: here name: 'Ada' replaces the default. The allowlist is enough for httpFetch to work, using the host's fetch; a host that wants to control requests itself passes a fetch capability instead.

When a run fails, error holds the error value (and result holds the same value). With fuel: 100 and a loop that never ends, run.error is { $error: true, message: 'Out of Fuel', op: 'seq' }.

tjs-lang/vm-ast is the VM without a parser: it accepts an AST only, so the transpile step runs outside the sandbox. That matters because parsing hostile source is the one step whose cost the attacker controls before any budget applies. With the parser outside, a bad payload can only stall your transpile step, which you can run in a worker or cap with maxSourceBytes, never the VM. Safe Eval covers budgets and capabilities in full.

Where to start

Read in order if AJS is new to you. Featured shows why AJS exists, Basics covers the language, APIs the network, LLMs models, and Advanced agents that combine them. Afterwards you can write an agent, run it with a budget and capabilities of your choosing, and let a model write one.