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
| Package | What it runs |
|---|---|
execution/process | A program, started with a list of arguments. No shell is involved. |
execution/shell | A command, through a shell you choose by name. |
execution/terminal | An interactive terminal session with a program. |
execution/python, execution/javascript | Your Python or JavaScript programs, scripts and modules. |
execution/jvm, execution/dotnet | Your Java or .NET programs. |
execution/remote | Any 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.
| Aspect | An existing program, through a package | A function in another language |
|---|---|---|
| The code | A 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 it | Through 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 built | Never by AEL: it runs as it is. | When you build, by your own toolchain, at the version it pins. |
| Where it runs | On your machine, or on a remote worker you authorize. | On the same machine as your program, or in a sandbox. |
| Use it for | Programs 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
| Part | Meaning |
|---|---|
| Program | The script, module, application file or executable to run, and its entry point. |
| Runtime | The runtime and version it needs, checked before the call runs. |
| Arguments | A list of typed values, never joined into one command line. |
| Environment | The working directory and the environment values you list; secrets passed by reference. |
| Permissions | The folders, network destinations and process rights the program may use. |
| Limits | A deadline, output size, CPU, memory, process count and temporary storage. |
| Result | The 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.
Related
- Files and processes: the full reference for files, processes and their outcomes.
- Functions in other languages: private functions in other languages inside your application.
- Distributions: the components a distribution includes.
- Effects and capabilities: how permissions are granted and checked.
- Tools and MCP: starting a local MCP server as a separate process.