Runtime & Typing¶
This page describes the AgentLang execution pipeline — from source text to final output — and the guarantees each phase provides.
Execution phases¶
Source text (.agent file)
│
▼
Lexer (agentlang/lexer.py)
│ Tokenizes source, decodes escape sequences
▼
Parser (agentlang/parser.py)
│ Builds AST, enforces structural uniqueness
▼
Lowering (agentlang/lowering.py)
│ Compiles workflows into explicit pipelines
▼
Type Checker (agentlang/checker.py)
│ Verifies types, bindings, and return paths
▼
Runtime (agentlang/runtime.py)
│ Executes pipeline, calls task handlers
▼
Output (JSON to stdout)
Lexer¶
The lexer converts source text into a flat token stream.
- Recognizes keywords:
agent,task,pipeline,let,run,with,by,retries,on_fail,abort,use,parallel,join,if,else,return,true,false,type,enum,try,catch,assert,test,max_concurrency - Recognizes workflow keywords such as
workflow,stage,review,checks,revise,using, andmax_rounds - Decodes string escape sequences:
\n,\t,\r,\\,\",\',\0,\uXXXX,\UXXXXXXXX - Rejects unknown escapes and invalid Unicode code points (surrogates, values above
U+10FFFF)
Parser¶
The parser builds the AST and enforces structural uniqueness constraints.
Guarantees:
- Unique agent, task, and pipeline names
- Unique workflow names
- Unique type alias and enum names
- Unique enum variant names within each enum
- Unique parameter names within each task/pipeline
- Unique field names in object type declarations and object literals
- Unique tool names within an agent's tool list
- Unique argument keys in
runcall argument maps - Well-formed
parallel,if,return,try/catch,assert, andtestblocks
Lowering¶
When a file contains workflow declarations, the lowering pass compiles them into ordinary PipelineDef nodes before type-checking and execution.
Current workflow lowering handles:
stagesteps- declarative
review ... revise ... max_rounds ...loops - hidden countdown-based loop budgets
- workflow returns
python main.py <file> <workflow_name> --lower prints the lowered pipeline IR.
Type checker¶
The type checker walks the AST and verifies semantic correctness before any task executes.
Verifies:
- Every variable reference is bound
runtarget task or pipeline exists in the task/pipeline tablebyagent exists in the agent table (when specified)- Argument names exactly match task parameter names
- Argument expression types match declared task input types
ifcondition has typeBoolwhilecondition has typeBoolbreak/continueonly occur inside loopsif letonly unwrapsOption[T]on_fail usefallback type matches the task's return typereturnexpression type equals the pipeline's declared return typetry/catchblock well-formedness; error variable typed asStringassertexpression has typeBool- Enum values are valid variants of the declared enum
- Type aliases are resolved to their underlying types
- Pipeline-as-run-target parameter and return types match
- workflow-lowered pipelines remain well-typed after compilation
Path sensitivity:
if/else: the checker tracks variable bindings introduced in both branchesifwithoutelse: the checker does not assume a guaranteed return from theifblock alone — areturnmust exist outside it, or in anelsetry/catch: variables bound beforetrymay be re-bound in both blocks; the merged environment is available after
Runtime¶
The runtime walks pipeline statements in order, maintaining an environment map of name → value.
Run statement¶
- Evaluates argument expressions against the current environment.
- Looks up the task handler from the registry.
- Executes the handler (up to
retries + 1times on failure). - If a
timeoutclause is present, the handler runs on a daemon thread. If it does not finish within the deadline,HandlerTimeoutErroris raised and remaining retries are skipped (because the abandoned handler thread is still running in the background). - On exhausted retries:
on_fail abort: raisesRuntimeError, pipeline stops.on_fail use <expr>: evaluates the fallback expression and binds the result.- Binds the result to the declared variable name.
Parallel block¶
- Takes a read-only snapshot of the current environment.
- Submits each branch as a
ThreadPoolExecutorfuture. - Waits for all futures to complete.
- Merges all branch results into the main environment.
- Continues sequentially after
join.
Conditional¶
- Evaluates the condition expression.
- Executes exactly one branch (then or else).
While / break / continue¶
- Evaluates the loop condition.
- Executes the body while the condition remains
true. breakexits the nearest loop.continueskips to the next iteration.
Agent tasks¶
- Selects the model from the bound agent.
- Exposes the agent's declared tools, if any.
- Executes tool calls through the runtime tool registry.
- Decodes final model output as JSON.
- Validates the resulting runtime value against the declared DSL type.
Return¶
- Evaluates the return expression.
- Exits the pipeline immediately, returning the value.
Try / catch¶
- Executes the
tryblock statements in order. - If any statement raises a runtime error, execution jumps to the
catchblock. - The error variable is bound as a
Stringcontaining the error message. - After either block completes, execution continues with the merged environment.
Assert¶
- Evaluates the expression (must be
Bool). - If
false, halts execution with an assertion error containing the message string. - If
true, continues to the next statement.
Pipeline call¶
- Evaluates argument expressions against the current environment.
- Creates a new scope for the target pipeline with the arguments bound as parameters.
- Executes the target pipeline.
- Binds the result to the declared variable name.
ExecutionContext¶
When --output-trace PATH is passed, an ExecutionContext records structured events throughout execution:
task_start/task_end/task_error— per-task lifecycleparallel_start/parallel_end— parallel block boundariesretry— retry attempts with error detailspipeline_call— pipeline-calls-pipeline events
Each record_task_start call returns a unique correlation id (e.g. task:research:1). All subsequent retry, task_end, and task_error events for that invocation carry the same id, making concurrent same-name tasks distinguishable in traces.
After execution, the trace is written as JSON to the specified path.
Diagnostics¶
get_leaked_thread_count() (from agentlang.runtime) returns the number of abandoned handler threads that are still alive. A handler thread is abandoned when its timeout deadline is exceeded. This function is useful for monitoring resource leaks in long-running processes.
Determinism¶
| Component | Deterministic? |
|---|---|
| Lexer + parser | Always |
| Type checker | Always |
| Expression evaluation | Always |
| Mock task handlers | Always |
parallel branch ordering |
Not guaranteed |
| Live task outputs | Depends on LLM/network |
The final output is deterministic only if all task handlers are deterministic. Mock mode is always deterministic; live mode depends on the LLM.
Failure sources¶
Failures during execution surface as:
Common sources:
| Source | Example message |
|---|---|
| Missing task handler | Unknown task 'my_task' |
| Adapter/network error | LLM call failed: <http error> |
| Invalid field access | KeyError: 'missing_field' |
| Unhandled task exception | Task 'flaky_fetch' failed after 3 attempts. Last error: RuntimeError: ... |
| Invalid pipeline inputs | Pipeline 'p' missing inputs: ['topic'] |
| Invalid live agent output | Agent task 'investigate' returned non-JSON output: '...' |
| Handler timeout | Task handler exceeded Xs deadline (handler may still be running in background). |