Browse the documentation

AEL 1.0.0

Files and roles

In AEL, the Agent Engineering Language, a file's name says what it holds. Agents, subagents, nodes (business steps), edges (typed two-way connections), configuration, hooks and skills are first-class building blocks, each in its own named file, so a project's structure reads at a glance.

One component per file

Every source file except the root files, language files and extension files is named <name>.<role>.ael:

  • <name> is an AEL name: letters, digits and _, not starting with a digit, and not a reserved keyword; see Syntax basics.
  • <role> is one of the roles below, in lower case.
  • The file declares exactly one component of that role, its primary, and the primary carries the file's name.
FileMust declareRefused
summarize.node.aelnode summarize(…)node billing(…), a second node, or none
support.agent.aelagent support(…)fn support(…) in place of the agent
auth.hook.aelhook auth(…)hook authorize(…)

Names are case-sensitive, and the check refuses two files whose names differ only in letter case, so a project means the same thing on every operating system.

The roles

RoleDeclaresHolds
agentagent <name>(…)A workflow: its nodes and edges, where work starts, what completes it.
subagentsubagent <name>(…)A short-lived agent that an agent spawns at run time: it does one task, returns a typed result to the agent that spawned it and ends. See Subagents.
nodenode <name>(…)One business step: deterministic logic, or a model call with its tools.
edgeedge <name>(…)A typed, two-way channel between two components: a value one way, and its reply back. See Communication between agents.
configconfig <name>(…)Typed, reusable configuration with defaults. See Configuration.
hookhook <name>(…)Authorization, auditing or cleanup around other code. See Hooks.
skillskill <name>(…)A versioned, reusable bundle of instructions, typed tools and functions that an agent or subagent loads. See Skills.
globalglobal <name>(…)A shared function visible in its folder and below.
reusereuse <name>(…)A shared function that a file imports by path.
routesroutes <name>(…)HTTP routes, a role that the server/http package adds.

Functions and visibility explains global and reusable functions. A package can add a role only when a manifest selects that package directly, and no package can replace a built-in role or a root file.

An agent, a node or a subagent can carry the modifier isolated before its keyword, as in isolated node scrub(…), and its file keeps its name, scrub.node.ael; Isolated components describes the modifier.

Language files

Your application can hold private functions written in other languages, with typed AEL signatures, built and run with your own toolchains, pinned by version. A function that has a file of its own is in a language file, named <name>.<lang>.ael, where <lang> is the tag of a language, such as python:

FileMust declare
profile.python.aelpython profile(…), a function in Python with an AEL signature
rotate.bash.aelbash rotate(…), a function in bash
  • A language file's primary carries the file's name and the file's language, and it is private to that file and its extension files.
  • A file name never holds both a language and a role. A component's file, such as profile.reuse.ael, holds functions in other languages as fn python … beside its AEL helpers.

Functions in other languages describes both forms, their options and the list of languages.

The root files

Three files sit at the root of every project, and only there:

  • main.ael declares the program's only entry point, main followed by the application's name: main <name>(), where <name> equals name in metadata.ael. A project whose metadata.ael holds name = "support"; declares main support(), and ael build names the program support. main main() is refused.
  • pack.ael is the manifest: a record of the packages the project uses, never code. A folder can add its own pack.ael for its own packages; see Folder scopes.
  • metadata.ael is a record of the project's name, description and settings. The name is required: it names main and the program.

main takes no parameters. Without a result type it returns nothing, and the program exits successfully when main finishes; main support() -> i64 returns an i64 instead, and that value is the program's exit status. main calls your agents and functions and, in a service, mounts its routes. It is always written in AEL, and it can call functions in other languages.

A package project keeps a main.ael even when main does nothing. Its metadata.ael names the package, and main takes the last segment of the package name: a package named acme/billing declares main billing().

Nothing starts on its own. Declaring an agent, a node or a set of routes starts no model call, timer or server: main starts what your program runs.

Inside a component file

A component file holds an optional header, which always comes first, then the primary, any supporting types and any number of private helper functions.

# src/nodes/summarize.node.ael
import "../validation.reuse.ael";

struct Summary {
    text: Str<1000>,
    urgent: bool,
}

node summarize(request: Str<4000>) -> Summary {
    let checked: Str<4000> = validation(request);
    return write_summary(checked);
}

fn write_summary(request: Str<4000>) -> Summary {
    # A private helper: only this component can call it.
}
  • The header comes before the first declaration. import "<relative path>"; makes a reusable function available, here validation, and a subagent, as import "./issue_reader.subagent.ael"; does. A header can also name the exports of a package that the file uses, such as refer server/http select get;; every selected export must be used.
  • Helper functions (fn) are private to the component. There is no pub fn: to share a function, give it a file of its own as a global or reusable function. The body of write_summary above is a placeholder, as A first agent explains.
  • Helpers in other languages are private helpers too: fn python … and the other language tags sit beside the AEL helpers of every component except a hook, a configuration or a skill.

Extension files

When a component needs more private helpers than fit in its file, you move them into extension files beside it:

src/agents/
  support.agent.ael
  support.agent.extended.ael
  support.agent.extended.2.ael
  support.agent.extended.3.ael
  • An extension file holds only private helper functions and comments: no header, no primary, no types. The helpers of a component's extension files are in AEL or, written fn <lang>, in another language.
  • Its helpers belong to that one component and can call the component's other helpers; no other file can call them.
  • It must sit in the same folder as its component. main.ael uses main.extended.ael, then main.extended.2.ael and up, and a subagent issue_reader.subagent.extended.ael and up.
  • A language file's extension files, profile.python.extended.ael, then profile.python.extended.2.ael and up, hold only functions in that file's language.
  • pack.ael and metadata.ael take no extension files.

Limits gives the size limit that every file, including each extension file, stays within.

Names that fail the check

  • A file without a role or a language, such as util.ael or global.ael.
  • A word that is neither a role nor a language in the list, such as billing.service.ael, or an extra dot, such as billing.v2.node.ael. A language tag is not an extra dot: profile.python.ael is a language file.
  • A language and a role in one name, in either order, such as profile.python.reuse.ael or profile.reuse.python.extended.ael.
  • A second entry point, such as app.main.ael, or a main.ael below the project root.
  • An extension numbered 1 or with a leading zero, such as support.agent.extended.1.ael.
  • An extension file with no component beside it, or in another folder.
  • A primary whose name differs from the file's, even only in letter case, and a main whose name differs from the application's.

Generated files

Two generated items appear beside a manifest, and you never edit either:

  • pack.lock, the lock file, records the exact packages a build used.
  • .package/ holds the installed packages. It is not source.