Browse the documentation

AEL 1.0.0

Calling your existing programs

You do not need to rewrite what already works. Agents written in AEL, the Agent Engineering Language, call your existing Python, JavaScript, Java or .NET programs, shell commands and operating-system operations as typed tools, with runtimes you provide, locally or on a remote worker.

The packages

PackageWhat it runs
execution/processA program, started with a list of arguments. No shell is involved.
execution/shellA command, through a shell you choose by name.
execution/terminalAn interactive terminal session with a program.
execution/python, execution/javascriptYour Python or JavaScript programs, scripts and modules.
execution/jvm, execution/dotnetYour Java or .NET programs.
execution/remoteAny of these on a remote worker that you authorize.

You add only the ones you use, with ael pack add:

ael pack add execution/python

Like every AEL package, these are written in AEL and ship as compiled, verified AEL, with no bundled native code and no install scripts. The programs they start are yours: the packages carry no Python, JavaScript, Java or .NET code of their own.

A function in your application, or a program you already have

AEL gives you two ways to use code in another language, for two different jobs:

  • You call your existing Python, JavaScript, Java or .NET programs, shell commands and operating-system operations as typed tools, with runtimes you provide, locally or on a remote worker. This page describes that way.
  • Your application can hold private functions written in other languages, with typed AEL signatures, built and run with your own toolchains, pinned by version. Functions in other languages describes them.
AspectAn existing program, through a packageA function in another language
The codeA program, script or command you already have, outside your source.A function you write in your application: a language file, such as profile.python.ael, or fn python … beside a component's helpers.
How you call itThrough execution/process, execution/python or another package of the table above, as a typed tool.Like any private function, through its typed AEL signature.
When it is builtNever by AEL: it runs as it is.When you build, by your own toolchain, at the version it pins.
Where it runsOn your machine, or on a remote worker you authorize.On the same machine as your program, or in a sandbox.
Use it forPrograms and commands that exist already and keep their own life cycle.Short code that belongs to your application and is built and versioned with it.

A function in another language runs on the same machine by default or in a sandbox, which needs the isolation facilities of Linux and runs only on the Linux target the documentation names for it, and a missing toolchain, a wrong version or a missing sandbox is refused before anything runs.

A program as a typed tool

From your agent's side, an existing program looks like any other typed operation: it takes typed arguments and returns a typed result.

# nodes/score.node.ael
node score(input: Claim) -> RiskScore {
    return run_scorer(input);
}

fn run_scorer(input: Claim) -> RiskScore {
    # Runs your existing scoring program through execution/python,
    # with typed arguments, a deadline and output limits.
}

A model-driven node can offer the same call to its model as a tool, with its own permission; see Tools and MCP. The body of run_scorer is a placeholder, as A first agent explains.

What each call declares

PartMeaning
ProgramThe script, module, application file or executable to run, and its entry point.
RuntimeThe runtime and version it needs, checked before the call runs.
ArgumentsA list of typed values, never joined into one command line.
EnvironmentThe working directory and the environment values you list; secrets passed by reference.
PermissionsThe folders, network destinations and process rights the program may use.
LimitsA deadline, output size, CPU, memory, process count and temporary storage.
ResultThe type its output must decode into, and your checks on it.

Results you can rely on

  • Exit does not mean success. A program that exits normally still has to produce output that decodes into your result type and passes your checks.
  • Every outcome is typed. A failure status, a program stopped by the operating system, a program that could not start, a deadline passed, a cancellation, a resource limit and unreadable output are separate outcomes.
  • Arguments stay data. A file name with spaces, quotes or a leading hyphen stays one argument. Only the shell package reads shell syntax, and only in the command text you give it.
  • Output is bounded. Standard output and error are bounded byte streams; when output passes its limit, the result says how much was kept and, where it is known, how much was produced.
  • Cancellation is thorough. Cancelling a call stops the program and the programs it started, first gracefully, then by force after a grace period.
  • Limits are real or refused. Where a platform cannot enforce a limit you asked for, the call is refused rather than run without it.

Runtimes you provide

Each call names the runtime it needs, and AEL checks the runtime's identity and version before running anything. It does not fall back to whatever happens to be installed.

  • On your machine or server, you install the runtimes your programs need.
  • In a minimal container image, there is no shell, Python, Java or .NET unless your deployment includes it deliberately; the alternative is a remote worker.
  • On a remote worker, the program runs on a machine you operate. The worker is authenticated, receives only the permissions, remaining budget and deadline of the call, and keeps the caller's tenant identity across reconnects. If the connection is lost after a call started, its outcome is reported as unknown, and work that cannot be repeated safely is never run again automatically.
  • On the smallest boards, there are no processes: a call needs AEL Gateway, and without it the build or deployment is refused before anything runs.

Starting a runtime takes real time, and that time is reported like any other.

Isolation

Permissions keep your own program from reaching what it should not. They are not a sandbox for a hostile program: run code you do not trust in isolation, for example on a remote worker you dedicate to it.

Inside your application, you mark an agent, node or subagent as isolated: it always runs in a sandbox in its own process, outside the shared communication layer, reachable only through the edges you declare and holding only the resources it declares itself. Isolation needs the isolation facilities of Linux and runs only on the Linux target the documentation names for it: on macOS, on Windows, on microcontroller boards, on other Linux targets and on any host without those facilities, an isolated component is refused, never run without isolation. A function in another language runs in a sandbox when it declares sandbox mode. Isolated components and Functions in other languages describe both.

Reports

Time and token usage is reported by default on every channel. The time a call takes is measured on your side. Model tokens that your external program uses on its own are reported as unknown unless they are measured; they are never guessed from its output.