Changelog
View Sourcev0.6.0 - 2026-10-13
Highlights
- MCP server -
Legion.MCP.Serverserves a Lua agent to MCP hosts asreplandhelptools; see the MCP guide- Over stdio or Streamable HTTP;
Legion.MCP.Plugrefuses browser requests from origins other thanlocalhostunless listed in:allowed_origins - OAuth 2.1, and a
session/1callback choosing the agent, vault and rate limits per call Legion.Tools.AgentToolandLegion.Tools.HumanToolare left out- Needs the optional
{:anubis_mcp, "~> 2.0"}dependency
- Over stdio or Streamable HTTP;
Legion.eval/3- runs code in a running agent as if its model had written it, with no LLM call: same tools, variables and limits, saved and rate-limited as one step of the conversation- On-demand tool docs -
tool_docs: :on_demandlists tools in the prompt by a one-linesummary/0, andHelp.help(Name)returns a tool's full description; the default over MCP - Sub-agents from Lua - Lua agents hold sub-agent conversations through
Legion.Tools.AgentTool, whose ids cross the Lua bridge
Breaking
Agents
- Default model is
openai:gpt-5.6-luna - An agent id belongs to one agent module - starting another module under an id whose stored conversation is not its own returns
{:error, {:agent_module_mismatch, stored}} Legion.resume/2andLegion.recover/2run the model only to finish an interrupted turn - one stopped behind a checkpoint or on a prompt with nothing after it;resume/2restores any other conversation and waits for the next message
Sub-agents
Legion.Tools.AgentToolworks with ids, not pids -start_link/1,2return the sub-agent's id, andcall/2,cast/2and the newstop/1take it- Only the agent that started a sub-agent reaches it
- A sub-agent stops when that agent stops, when the turn that started it ends unless
:binding_scopeis:conversation, or after:sub_agent_idle_timeoutwithout a message (default thirty minutes) - An agent runs at most
:max_sub_agentsat once (default 10) - A start is checked against the rate limit, returning
{:cancel, {:rate_limited, violations}}when denied
Tools
Helpis a reserved tool name -Legion.Tools.Helpis in every agent's sandbox, so a tool of your own with the short nameHelpis no longer reachable;use Legion.Agentwarns about itsummary/0andmcp?/0areLegion.Toolcallbacks -use Legion.Tooldefines them, so Lua code no longer reaches a tool function of either name, and asummary/0not marked@impl Legion.Toolwarns. A module that implements@behaviour Legion.Toolwithoutusemust define both
Rate limiting
:max_agentscaps agents, checked at start - a deniedLegion.start_link/2returns{:error, {:rate_limited, violations}}with no process started and nothing saved, andLegion.execute/3returns{:cancel, {:rate_limited, violations}}. Resumed and recovered runs are not checked:max_tokens,:max_evalsand:max_running_agentsneed the Postgres store - withLegion.RateLimiter.Postgres, an agent whose rules set any of them raises at start unless its store is aLegion.Store.Postgreson the limiter's repo and table, since those limits count what that store writes. Withconfig :legion, :track_usage, false, rules setting:max_tokensor:max_evalsraise at start too
Persistence
- Usage entries may have no token counts - a
Legion.eval/3step records%{"at" => ..., "evals" => 1, "message_index" => ...}, and an LLM entry whose action ran code carries"evals" => 1; code summing token keys over usage must allow for entries without them. SeeLegion.Store - Custom
Legion.Storeadapters must keep:base_bindings- a saved:conversation_statecarries it next to:bindings; an adapter that keeps only the keys it knew drops it, and an agent restarted mid-turn loses the variables that turn started with
Sandbox
:max_bindings_bytescaps leftover variables - default10_000_000; an execution that would leave larger variables behind fails. Set it to:infinityfor the old behaviourLegion.Sandbox.Luacheck/2no longer reports syntax errors - parsing moved inside the sandbox process, under its limits, and syntax errors come back fromexecute/5
Other changes
- Rate limiting -
:max_evalsinLegion.RateLimiter.Policycaps code evaluations per window Legion.start_link/2takes:vault, put in the agent process for its tools, and:idle_timeout, which stops an agent nobody callsmax_message_lengthdefaults to40_000bytes and also bounds code sent withLegion.eval/3:stepstores persist usage with every checkpoint instead of only at turn end
v0.5.1 - 2026-09-25
Changes
- Rate limiting -
Legion.RateLimiter.resolve!/1raises or warns on invalid or incomplete configuration;rules:no longer read fromconfig :legion, :rate_limit - Rate limiting -
:max_running_agentsinLegion.RateLimiter.Policycaps how many matching agents run a turn at once - Recovery - recovered runs skip rate limiting; rules are not persisted, so
Legion.resume/2takes:rate_limitagain - Usage tracking -
"message_index"links usage to its assistant message, seeLegion.Store - Persistence -
Legion.Store.Payloadexposes the agent's rate-limit identity as:ratelimit_metadata Legion.Sandbox.Luaskips Erlang modules in the tool list instead of crashing- Guides - Adding Legion to an existing app, Using Legion with Ash, Using local LLMs
v0.5.0 - 2026-09-01
Changes
- Pluggable sandboxes - the
Legion.Sandboxbehaviour,Legion.Sandbox.Elixir, sharedLegion.Sandbox.Runner Legion.Sandbox.Lua, now the default sandbox- Sandbox resource limits - timeout, memory, and CPU budgets in
Legion.Sandbox.Runner - Sandbox-specific
Legion.Tool.description/1 - Persistence -
Legion.Storesaves conversation state (messages, bindings, executor checkpoints), status, and LLM usage across restarts, with a Postgres adapter, versioned migrations, andpersistence_frequency/0 - Agent identity - string agent ids,
Legion.get_agent_id/1,Legion.lookup/1, cluster-wide:globalregistration;:nameoption removed - Recovery -
Legion.resume/2,Legion.recover/2,:recoverystartup config - Rate limiting -
Legion.RateLimiterwith rules, policies, and a Postgres adapter - LLM usage tracking - persisted per request (
:track_usage),usagein[:legion, :llm, :request, :stop]telemetry - Default model bumped to
openai:gpt-5.4 Legion.Tools.HumanTool.ask/1raises undereval_and_complete- Bump ReqLLM
v0.4.0 - 2026-05-17
Security
- Harden
Legion.Sandbox.ASTCheckeragainst a class of RCE paths. After this release, most (if not all) RCE vectors should be closed. Legion is still vulnerable to DoS kinds of attacks, but we assume that having a system prompt instruction to behave well AND improving sandbox should be enough for now.
Changes
- Broaden the sandbox surface for common LLM idioms: allow
Map.values/1,JSON,URI,:erlang.float_to_binary/2, additionalString/Enum/Date/DateTimefunctions, and theAccessprotocol (map[:k]) - Document the sandbox constraints with concrete idioms in the system prompt
- Fix tool source extraction breaking on heredocs and charlists
- Correct documentation for telemetry events, source registry, and
AgentTool.start_link/2
v0.3.0 - 2026-04-21
Changes
- Replace
share_bindingsboolean withbinding_scope(:iteration,:turn,:conversation) for fine-grained control over variable lifetime across code executions - Add
action_types/0callback to restrict which actions an agent can use (e.g.~w(return done)for read-only agents) - Add
max_message_lengthconfig with truncation support to prevent unbounded message growth - Add multimedia message support:
{:image, data, media_type},{:image_url, url}, and{:multipart, parts} - Add
Legion.get_messages/1to retrieve conversation history from a running agent - Expand
AgentToolwithparallel/2,pipeline/1,then/3, andextra_allowed_modules/0for sub-agent orchestration; sub-agents are auto-aliased in the sandbox - Generate dynamic
AgentTool.description/0from sub-agent moduledocs - Move system prompt resolution to
AgentPrompt, respecting customsystem_prompt/0overrides - Validate config keys at startup with warnings for unknown keys
- Add
@moduledoccompile-time validation via__before_compile__ - Harden sandbox: block
def/defp/__ENV__, additional:erlangfunctions (process_flag,list_to_atom,system_info), catch throws and exits, surface compiler diagnostics on errors - Handle executor exceptions gracefully instead of crashing the agent loop
- Add
Calendarto sandbox safe-module list - Emit
:exceptiontelemetry events for iteration, LLM, and sandbox spans; useSystem.convert_time_unit/3for duration reporting - Extensive new test coverage for
AgentServer,Executor,Sandbox, andASTChecker
v0.2.1 - 2026-03-24
- Improve source code extraction for tool definitions
- Adjust system prompt to better reflect capabilities
v0.2.0 - 2026-03-15
Changes
- Simplified and refactored internals
- Improved documentation and general library intent
v0.1.0 - 2025-12-29
New 🔥
- Initial release of Legion - an Elixir-native agentic AI framework
Legion.AIAgentbehaviour for building AI agents with customizable tools and configurationsLegion.Toolbehaviour for defining tools that agents can use- Integration with
req_llmfor LLM communication Legion.Sandboxfor secure code evaluation using DuneLegion.call/2andLegion.cast/2for synchronous and asynchronous message passingLegion.start_link/2for spawning long-lived agents- Telemetry events for monitoring and debugging agent execution
- Support for agent-to-agent communication and delegation