-
Notifications
You must be signed in to change notification settings - Fork 4
Description
CL_EX Ecosystem Mapping
Component-by-Component Mapping to BEAM/Elixir + CL_EX Ecosystem Equivalents
Document 03 of the CL_EX Design Series
Date: 2026-02-02
Table of Contents
- Component Mapping Table
- Detailed Component Mappings
- Dependency Graph
- Heterogeneous Stack Diagrams
- What's Missing
- Generic Agentic Stack
1. Component Mapping Table
| # | OpenClaw Component | Role | CL_EX Equivalent | Status | Hex Package |
|---|---|---|---|---|---|
| 1 | pi-agent-core |
Agent execution, AgentTool, AgentMessage, streaming | cl_ex_agent (Jido.Agent-based runtime, OTP GenServer) |
NEW | {:jido, "~> 1.0"} |
| 2 | pi-coding-agent (SessionManager, tools) |
Session lifecycle, filesystem tools, skills | cl_ex_agent (sessions) + cl_ex_tools (Jido.Action-based tools) |
NEW | -- |
| 3 | pi-ai (Model API, streaming) |
Model abstraction, API clients, completions | cl_ex_agent LLM layer via altar_ai + provider SDKs (claude_agent_sdk, codex_sdk, gemini_ex, ollixir) |
ADAPT | {:altar_ai, "~> 0.1"} |
| 4 | pi-tui (Terminal UI) |
Terminal UI components | owl + ratatouille + custom cl_ex_tui |
ADAPT | {:owl, "~> 0.1"}, {:ratatouille, "~> 0.5"} |
| 5 | grammy (Telegram) |
Telegram Bot API | nadia / telegex / ex_gram |
ADAPT | {:ex_gram, "~> 0.52"} |
| 6 | @buape/carbon (Discord) |
Discord framework | nostrum |
ADAPT | {:nostrum, "~> 0.10"} |
| 7 | @slack/bolt (Slack) |
Slack integration | slack_elixir / custom |
ADAPT | {:slack_elixir, "~> 1.0"} |
| 8 | @whiskeysockets/baileys (WhatsApp) |
WhatsApp Web protocol | None -- must wrap or port | NEW | -- |
| 9 | playwright-core (Browser) |
Browser automation | wallaby + playwright_elixir |
ADAPT | {:wallaby, "~> 0.30"} |
| 10 | sqlite-vec (Vector search) |
Vector search, embeddings | cl_ex_memory via Bedrock (primary), portfolio_core/portfolio_index for RAG, pgvector + ecto (fallback) |
ADAPT | {:bedrock, "~> 0.1"}, {:pgvector, "~> 0.3"} |
| 11 | commander (CLI) |
CLI argument parsing | Built-in OptionParser + owl |
EXISTS | (stdlib) |
| 12 | hono (HTTP server) |
HTTP server, webhooks | bandit + plug / phoenix |
EXISTS | {:bandit, "~> 1.0"}, {:plug, "~> 1.16"} |
| 13 | croner (Cron) |
Cron scheduling | quantum |
EXISTS | {:quantum, "~> 3.5"} |
| 14 | dotenv (Env) |
Environment variable loading | dotenvy |
EXISTS | {:dotenvy, "~> 0.8"} |
| 15 | JSONL session files | Session transcript persistence | Bedrock (primary state store) with ETS cache; session management in cl_ex_agent |
NEW | {:bedrock, "~> 0.1"} |
| 16 | Docker sandbox | Sandboxed code execution | cl_ex_sandbox (Docker via :erlexec + NIF, or Firecracker) |
NEW | -- |
| 17 | Plugin loader (jiti) |
Dynamic plugin loading | Native BEAM code loading + cl_ex_plugins |
EXISTS | (OTP native) |
| 18 | Config (JSON5 + Zod) | Configuration + validation | NimbleOptions + Application config (part of cl_ex core) |
EXISTS | {:nimble_options, "~> 1.1"} |
2. Detailed Component Mappings
2.1 pi-agent-core --> cl_ex_agent
OpenClaw Role: Core agent execution runtime. Defines AgentTool, AgentMessage, StreamFn, and event primitives. Manages the run/attempt lifecycle, streaming token delivery, and tool-call dispatch loop.
CL_EX Equivalent: cl_ex_agent -- a NEW OTP application built on Jido.Agent + Jido.AgentServer (GenServer-based agent runtime). See Doc 01 Section 3.4 and Doc 02 Section 3 for the canonical agent architecture.
Status: NEW
Integration Notes:
- Each agent instance is a
Jido.AgentServerprocess (GenServer) wrapping aJido.Agentstruct. Agent state transitions (idle, thinking, tool_calling, streaming, error) are managed via Jido's directive-based effect model rather than explicit state machine states. - Agent processes are registered in a
Registrynamespaced by{agent_id, session_key}. Session keys follow the canonical format:session:{identity_id}:{channel}:{session_id}. - The run/attempt loop uses Jido's
cmd/2pipeline: each tool call is aJido.Actiondispatched via directives, each LLM response updates agent state immutably. AgentToolmaps toJido.Actionwith@callback run(params, context) :: {:ok, result} | {:error, %ClEx.Error{}}. Higher-level tool compositions useJido.Skill.AgentMessagemaps to a typed struct:%ClEx.Signal{role: :user | :assistant | :tool, content: term(), metadata: map()}-- the canonical internal envelope (see Doc 01).- Streaming is handled via
Process.send/2-- the BEAM's message passing IS the streaming primitive. No need forStreamFncallbacks; subscribers simply receive messages. - Event bus:
Phoenix.PubSub(or:pgfor pure OTP) for lifecycle events. Synapse workflow integration provides step-level retry and signal registry. - Supervision tree: one
DynamicSupervisorper node manages all agent processes. Crash recovery is automatic via OTP. State is persisted to Bedrock and restored on restart.
Key Elixir Advantage: The BEAM makes every agent a lightweight process (~2KB), enabling thousands of concurrent agents per node with preemptive scheduling. See Doc 04 for detailed comparison with OpenClaw's concurrency model.
defmodule MyApp.Agent do
use Jido.Agent,
name: "my_agent",
description: "A CL_EX agent",
actions: [ClEx.Tools.Read, ClEx.Tools.Write, ClEx.Tools.Search],
schema: [
model: [type: :string, default: "claude-sonnet-4-20250514"],
workspace: [type: :string]
]
# Jido agents are pure functional -- state transitions return
# directives (effects) rather than performing side effects directly.
# The AgentServer (GenServer) executes directives on behalf of the agent.
end
# Starting an agent (managed by cl_ex_agent supervisor):
{:ok, pid} = ClEx.Agent.Runtime.start_agent(%{
id: "helper",
module: MyApp.Agent,
model: "claude-sonnet-4-20250514"
})2.2 pi-coding-agent --> cl_ex_agent (sessions) + cl_ex_tools
OpenClaw Role: SessionManager handles JSONL transcript persistence, session keying, reset policies. Coding tools provide read, write, edit filesystem operations with policy enforcement.
CL_EX Equivalent:
cl_ex_agent-- Session lifecycle management is embedded within the agent component (asClEx.Agent.Session/ClEx.Agent.Session.Store). See Doc 02 Section 3.cl_ex_tools-- Tool behaviour and standard tool library, usingJido.Actionas the tool primitive (NEW)
Status: NEW
Integration Notes:
- Session storage: Bedrock is the primary state store for all session data. Sessions are keyed by
session:{identity_id}:{channel}:{session_id}(matching Doc 01's canonical key format). ETS serves as a hot cache with Bedrock as the durable backend. - For single-node/embedded deployments without Bedrock: ETS + DETS provides a graceful degradation path.
- Reset policies: Implemented as configurable strategies via
NimbleOptions::manual,:age_based,:token_count,:message_count. - Coding tools: Implemented as
Jido.Actionmodules (e.g.,ClEx.Tools.Read,ClEx.Tools.Write,ClEx.Tools.Edit). Elixir'sFilemodule provides all filesystem ops. Theedittool can use:binary.match/2for fast string replacement. Sandbox policies enforced viacl_ex_security(GUARDRAIL) by checking paths against allowed directories before any operation. - Session compaction: Transcript summarization via LLM call when token count exceeds threshold -- same pattern as OpenClaw but triggered by a GenServer timer. FlowStone can orchestrate the compaction pipeline with lineage tracking.
defmodule ClEx.Agent.Session.Store do
@moduledoc "Bedrock-backed session store with ETS cache"
def append(session_key, message) do
# Write-through: ETS cache + Bedrock persistence
:ets.insert(:cl_ex_sessions, {session_key, message})
Bedrock.put("session:#{session_key}", message)
end
def load(session_key) do
case :ets.lookup(:cl_ex_sessions, session_key) do
[] -> Bedrock.get("session:#{session_key}")
[{_, messages}] -> {:ok, messages}
end
end
end2.3 pi-ai --> cl_ex_agent LLM layer (ADAPT ecosystem SDKs)
OpenClaw Role: Model abstraction layer. Provides unified API across OpenAI, Anthropic, Google, Bedrock, Ollama. Handles streaming completions, tool-call formatting, token counting, and fallback chains.
CL_EX Equivalent: The LLM abstraction lives within cl_ex_agent as ClEx.Agent.LLM (see Doc 02 Section 3). It is built on the ecosystem's own provider SDKs:
altar_ai-- Multi-provider LLM abstraction with unified APIclaude_agent_sdk-- Anthropic Claude provider adaptercodex_sdk-- OpenAI / Codex provider adaptergemini_ex-- Google Gemini provider adapterollixir-- Ollama / local model provider adapterlangchain(Elixir) -- Optional fallback for providers not yet covered by ecosystem SDKs
Status: ADAPT
Integration Notes:
altar_aiprovides the unified LLM interface with streaming, tool calls, and structured output. Provider-specific adapters (ClEx.Agent.LLM.Anthropic,ClEx.Agent.LLM.OpenAI,ClEx.Agent.LLM.Gemini,ClEx.Agent.LLM.Ollama) wrap the ecosystem SDKs.ClEx.LLM.Routerhandles ensemble routing viacrucible_*modules for model selection, fallback chains, and load balancing across providers.- Structured output validation uses
Exdanticschemas (replacing Zod-based output parsing in OpenClaw). - Rate limiting via Foundation's rate limiter. Token counting via provider SDK utilities.
- Telemetry integration via
:telemetryfor cost tracking; AITrace for LLM call observability. - Streaming: Provider SDKs emit BEAM messages natively -- the subscribing agent process receives tokens directly. No callback adapter layer needed.
langchaincan be retained as an optional dependency for providers not yet supported by the ecosystem SDKs, but it is not the primary abstraction.
defmodule ClEx.Agent.LLM do
@moduledoc "Unified LLM interface with provider routing and fallback chains"
def chat(messages, opts \\ []) do
chain = Keyword.get(opts, :fallback_chain, [opts[:model] || default_model()])
Enum.reduce_while(chain, {:error, %ClEx.Error{type: :llm, reason: :all_models_failed}}, fn model, _acc ->
adapter = adapter_for(model)
case adapter.chat(messages, opts) do
{:ok, response} -> {:halt, {:ok, response}}
{:error, _reason} -> {:cont, {:error, %ClEx.Error{type: :llm, reason: :all_models_failed}}}
end
end)
end
defp adapter_for("claude-" <> _), do: ClEx.Agent.LLM.Anthropic
defp adapter_for("gpt-" <> _), do: ClEx.Agent.LLM.OpenAI
defp adapter_for("gemini-" <> _), do: ClEx.Agent.LLM.Gemini
defp adapter_for(_), do: ClEx.Agent.LLM.Ollama
endHex Dependencies:
{:altar_ai, "~> 0.1"}, # Multi-provider LLM abstraction
{:claude_agent_sdk, "~> 0.1"}, # Anthropic provider
{:codex_sdk, "~> 0.1"}, # OpenAI provider
{:gemini_ex, "~> 0.1"}, # Google Gemini provider
{:ollixir, "~> 0.1"}, # Ollama / local models
{:langchain, "~> 0.3", optional: true}, # Optional fallback2.4 pi-tui --> cl_ex_tui (ADAPT)
OpenClaw Role: Terminal UI for interactive agent sessions. Renders streaming output, tool call progress, thinking indicators, and markdown-formatted responses.
CL_EX Equivalent:
ratatouille-- Termbox-based terminal UI framework for Elixirowl-- Terminal UI component library (progress bars, spinners, tables)cl_ex_tui-- NEW composition layer
Status: ADAPT
Integration Notes:
ratatouilleprovides the Elm-architecture terminal UI framework (model-update-view). It handles raw terminal rendering, keyboard input, and layout.owlprovides higher-level components: spinners, progress bars, tables, and live-updating output. Good for CLI-mode rendering.cl_ex_tuicomposes these into agent-specific views:- Streaming text panel (assistant output arriving token-by-token)
- Tool call sidebar (shows active tool executions)
- Session info bar (model, token count, session key)
- Input area with history
- Alternative:
ExTermboxfor lower-level control, or simply useIO.ANSI+owlfor a lighter approach. - The BEAM's process model means the TUI is a separate process receiving messages from the agent -- no callback spaghetti.
2.5 grammy --> ExGram / Telegex (ADAPT)
OpenClaw Role: Telegram Bot API client. Handles long polling, webhook mode, inline keyboards, message formatting, media upload/download, and bot commands.
CL_EX Equivalent: ex_gram -- Telegram Bot API framework for Elixir.
Status: ADAPT
Integration Notes:
ex_gramprovides a full Telegram Bot API implementation with both long-polling and webhook modes.- It includes middleware, command handling, and inline keyboard support.
- Adaptation needed: Wrap
ex_gramin aClEx.Transport.Telegramadapter that implements theClEx.Transport.Channelbehaviour (see Doc 02 Section 2):- Normalize inbound messages to
%ClEx.Signal{} - Translate outbound
%ClEx.Signal{}to Telegram API calls - Map Telegram threading (reply_to_message_id) to CL_EX session threading
- Handle media attachments (photos, documents, voice)
- Normalize inbound messages to
- Alternative:
telegexis another option with a more modern API but smaller community. nadiaexists but is older and less maintained.
defmodule ClEx.Transport.Telegram do
@behaviour ClEx.Transport.Channel
@impl true
def normalize(%ExGram.Model.Message{} = msg) do
%ClEx.Signal{
channel: :telegram,
sender_id: "tg:#{msg.from.id}",
group_id: if(msg.chat.type != "private", do: "tg:#{msg.chat.id}"),
text: msg.text,
thread_id: msg.message_thread_id,
metadata: %{chat_type: msg.chat.type}
}
end
@impl true
def deliver(signal, opts) do
ExGram.send_message(signal.metadata.chat_id, signal.content, opts)
end
end2.6 @buape/carbon --> Nostrum (ADAPT)
OpenClaw Role: Discord bot framework. Handles gateway connection, slash commands, message events, embeds, reactions, threads, and voice channel awareness.
CL_EX Equivalent: nostrum -- Discord API library for Elixir.
Status: ADAPT
Integration Notes:
nostrumis the mature Discord library in Elixir. It provides:- Gateway WebSocket connection with automatic reconnection
- Full REST API coverage
- Built-in caching (ETS-backed) for guilds, channels, users
- Consumer-based event handling
- Adaptation needed: Same pattern as Telegram -- wrap in
ClEx.Transport.Discord:- Normalize Discord events to
%ClEx.Signal{} - Handle Discord-specific features: embeds, slash commands, components (buttons, selects)
- Map Discord threads to CL_EX sessions
- Handle rate limiting (Nostrum does this internally)
- Normalize Discord events to
nostrumuses its own supervision tree and process structure -- fits naturally into the BEAM.
2.7 @slack/bolt --> Slack Elixir (ADAPT)
OpenClaw Role: Slack integration. Handles Events API, slash commands, interactive messages, modal dialogs, threading, and workspace management.
CL_EX Equivalent: slack_elixir or custom Plug-based integration.
Status: ADAPT
Integration Notes:
- The Elixir Slack ecosystem is less mature than Telegram/Discord. Options:
slack_elixir-- Basic Slack Web API + Events APIslack_web-- Auto-generated API client from Slack's OpenAPI spec- Custom: Use
Plugto receive Events API webhooks +Req/Finchfor API calls
- Recommendation: Build
ClEx.Transport.SlackwithReqfor API calls and aPlug.Routerfor incoming webhooks. This gives full control and avoids dependency on under-maintained wrappers. - Socket Mode support may need to be built (WebSocket connection to Slack instead of HTTP webhooks).
2.8 @whiskeysockets/baileys --> cl_ex_whatsapp (NEW)
OpenClaw Role: WhatsApp Web multi-device protocol implementation. Handles QR code authentication, message encryption (Signal protocol), media upload/download, group management, and presence updates.
CL_EX Equivalent: cl_ex_whatsapp -- Must be built or wrapped.
Status: NEW
Integration Notes:
- This is the biggest gap. Baileys is a complex reverse-engineered protocol implementation (~20k lines of TypeScript). There is no Elixir equivalent.
- Options:
- NIF/Port wrapper: Run a minimal Node.js Baileys instance as a Port or use Rustler to wrap a Rust WhatsApp library (e.g.,
whatsmeowin Go can be called via Port). - Go port:
whatsmeowis a mature Go implementation. Call it via Erlang ports or usego_erlangfor tighter integration. - Direct port: Long-term, port the Signal protocol implementation and WhatsApp Web protocol to Elixir. Massive effort.
- Cloud API: Use WhatsApp Business Cloud API (official REST API) instead of Web protocol. Much simpler but requires Business account and has message template restrictions.
- NIF/Port wrapper: Run a minimal Node.js Baileys instance as a Port or use Rustler to wrap a Rust WhatsApp library (e.g.,
- Recommendation for v1: Use WhatsApp Business Cloud API via
ReqHTTP client. For full feature parity with OpenClaw, wrapwhatsmeow(Go) as an Erlang port.
2.9 playwright-core --> Wallaby / Playwright Elixir (ADAPT)
OpenClaw Role: Browser automation for web scraping, screenshot capture, and interactive browsing by agents.
CL_EX Equivalent:
wallaby-- Browser automation testing framework (wraps ChromeDriver/Selenium)playwright_elixir-- Elixir bindings for Playwright (if available)- Custom: Use
Req+Flokifor HTML scraping without a browser
Status: ADAPT
Integration Notes:
wallabyis mature but oriented toward testing. Needs adaptation for agent-driven browsing.- For headless browsing: Use Chrome DevTools Protocol (CDP) directly via WebSocket. Libraries like
chrome_remote_interfaceexist. - For simple scraping:
Req+Floki(HTML parser) handles most cases without browser overhead. - Recommendation: Layer approach:
- Light scraping:
Req+Floki(no browser needed) - JavaScript-rendered pages: CDP via WebSocket
- Full interaction: Playwright via Node.js port (same pattern as WhatsApp)
- Light scraping:
2.10 sqlite-vec --> cl_ex_memory (Bedrock + portfolio_core) (ADAPT)
OpenClaw Role: SQLite with vector search extension for semantic memory. Stores embeddings, enables cosine similarity search, and provides the memory/RAG backend.
CL_EX Equivalent:
Bedrock-- Primary distributed state store for all memory data (see Doc 01)portfolio_core/portfolio_index-- RAG pipeline: chunking, indexing, and retrievalembed_ex-- Embedding generation (local or API-based)pgvector+ecto-- PostgreSQL vector extension (fallback for deployments without Bedrock)exqlite-- SQLite3 NIF bindings (single-node/embedded fallback)
Status: ADAPT
Integration Notes:
- Primary path (Bedrock): Bedrock provides the distributed KV store for memory entries with consistent reads across the cluster. Vector indices are managed via
portfolio_index, which provides chunking, embedding, and semantic search over Bedrock-stored documents. - RAG pipeline:
portfolio_corehandles the end-to-end RAG flow: document ingestion, chunking strategies, embedding viaembed_ex, storage in Bedrock, and retrieval with reranking. This replaces the need to manually wire pgvector + Ecto for RAG. - Fallback Option A (PostgreSQL): Use
pgvectorwith Ecto. Better for teams with existing Postgres infrastructure who do not want the Bedrock dependency. - Fallback Option B (SQLite): Use
exqliteand load thesqlite-vecextension at runtime. Maintains parity with OpenClaw's approach for single-node embedded deployments. - Embedding generation: Use
embed_exwhich wrapsBumblebee+EXLAfor local embedding models, or calls OpenAI/Voyage embeddings API viaReq. - Recommendation: Default to Bedrock +
portfolio_corefor production. Fall back topgvector+ Ecto when Bedrock is unavailable. Useexqlite+sqlite-veconly for single-node/embedded deployments. Abstract behindClEx.Memory.VectorStorebehaviour.
defmodule ClEx.Memory.VectorStore do
@callback store(collection :: String.t(), text :: String.t(), embedding :: [float()], metadata :: map()) :: :ok
@callback search(collection :: String.t(), query_embedding :: [float()], opts :: keyword()) :: [result()]
@callback delete(collection :: String.t(), id :: String.t()) :: :ok
end2.11 commander --> OptionParser (EXISTS)
OpenClaw Role: CLI argument parsing, subcommand routing, help generation.
CL_EX Equivalent: Elixir's built-in OptionParser + Mix tasks + Owl for rich output.
Status: EXISTS
Integration Notes:
- Elixir's
OptionParserhandles flag/option parsing natively. No external dependency needed. - For subcommand routing: Use Mix tasks (
mix cl_ex.start,mix cl_ex.status) during development. For production CLI, useescriptorBurritofor self-contained binaries. Owlprovides rich terminal output (tables, trees, spinners) for CLI responses.- For interactive prompts:
IO.gets/1+ custom wrapper, orex_prompt.
2.12 hono --> Bandit + Plug / Phoenix (EXISTS)
OpenClaw Role: Lightweight HTTP server for webhook endpoints, gateway API, and health checks.
CL_EX Equivalent: Bandit (HTTP server) + Plug (middleware) or full Phoenix framework.
Status: EXISTS
Integration Notes:
Banditis a modern, pure-Elixir HTTP/1.1 and HTTP/2 server. Drop-in replacement for Cowboy.Plugprovides composable middleware (routing, parsing, CORS, etc.).- For WebSocket support:
Phoenix.ChannelorWebSockAdapterwith Bandit. - Decision: Use
Plug+Banditfor the lightweight gateway. If users want to embed CL_EX into a Phoenix app, it plugs directly into the Phoenix router. - Phoenix is NOT required -- Plug + Bandit gives a full HTTP stack in ~50 lines of config.
defmodule ClEx.Gateway.Router do
use Plug.Router
plug :match
plug Plug.Parsers, parsers: [:json], json_decoder: Jason
plug :dispatch
post "/webhook/:channel" do
ClEx.Transport.dispatch(channel, conn.body_params)
send_resp(conn, 200, "ok")
end
get "/health" do
send_resp(conn, 200, Jason.encode!(%{status: "ok", agents: ClEx.Agent.count()}))
end
end2.13 croner --> Quantum (EXISTS)
OpenClaw Role: Cron-style job scheduling for periodic agent tasks, health checks, and automation.
CL_EX Equivalent: Quantum -- Cron-like job scheduler for Elixir.
Status: EXISTS
Integration Notes:
Quantumsupports cron expressions, runtime job management, and clustered execution (only one node runs each job).- Direct replacement -- no adaptation needed.
- Alternative:
Obanfor persistent job queues with retry/backoff (heavier but more robust for critical tasks).
2.14 dotenv --> Dotenvy (EXISTS)
OpenClaw Role: Load environment variables from .env files.
CL_EX Equivalent: Dotenvy -- Dotenv file loading for Elixir.
Status: EXISTS
Integration Notes:
Dotenvyloads.envfiles and integrates with Elixir'sConfigsystem.- Elixir's
config/runtime.exsalready provides environment-based configuration. - No adaptation needed. Use
Dotenvyfor development convenience; rely on system env vars orConfig.Providerfor production.
2.15 JSONL Session Files --> Bedrock (primary) + ETS cache (NEW)
OpenClaw Role: Persist session transcripts as JSONL (one JSON object per line) files on the filesystem. Each session has a file; messages are appended.
CL_EX Equivalent: Session data is managed within cl_ex_agent (as ClEx.Agent.Session.Store). Bedrock is the primary durable store; ETS serves as a hot cache.
Status: NEW
Integration Notes:
- Bedrock is the primary state store for session transcripts. Session keys follow the canonical format
session:{identity_id}:{channel}:{session_id}(see Doc 01 Section 3.3.2). Bedrock provides distributed durability with Raft consensus, so sessions survive node failures without Mnesia. - ETS serves as the hot cache for active sessions -- O(1) lookup, ordered_set for message ordering, ~microsecond access. Write-through to Bedrock on every append.
- Graceful degradation: When Bedrock is unavailable (e.g., single-node dev mode), ETS + DETS provides a local-only fallback (see Doc 02 Section 3).
- JSONL format can be maintained for export/import compatibility, but internal representation should be native Erlang terms for performance.
- Session compaction and memory consolidation can be orchestrated via FlowStone with lineage tracking.
2.16 Docker Sandbox --> cl_ex_sandbox (NEW)
OpenClaw Role: Run agent-generated code in a Docker container for isolation. Mounts workspace directory, sets resource limits, captures stdout/stderr.
CL_EX Equivalent: cl_ex_sandbox -- NEW module for sandboxed execution.
Status: NEW
Integration Notes:
- Option A (Docker): Use Erlang
:os.cmd/1orSystem.cmd/2to invokedocker run. Wrap in a GenServer for lifecycle management. - Option B (erlexec): Use
erlexecfor advanced OS process management with resource limits, signal handling, and stdout/stderr capture. Can run Docker or direct processes. - Option C (Firecracker): MicroVMs for stronger isolation. More complex but better security properties.
- Option D (BEAM sandbox): For Elixir code execution, use
Code.eval_string/3in a restricted environment with a separate node (Node.spawn_link/2) and resource limits via cgroups. - Recommendation: Start with Docker via
System.cmd/2wrapped in a GenServer. Add Firecracker support later for enterprise deployments.
defmodule ClEx.Sandbox do
use GenServer
def execute(code, opts \\ []) do
timeout = Keyword.get(opts, :timeout, 30_000)
workspace = Keyword.get(opts, :workspace, "/tmp/cl_ex_sandbox")
GenServer.call(__MODULE__, {:execute, code, workspace, timeout}, timeout + 5_000)
end
end2.17 Plugin Loader (jiti) --> OTP Code Loading (EXISTS)
OpenClaw Role: Dynamic plugin loading at runtime. jiti transpiles and loads TypeScript/JavaScript modules on-the-fly.
CL_EX Equivalent: Native BEAM code loading, managed by cl_ex_plugins (see Doc 02 Section 10).
Status: EXISTS
Integration Notes:
- The BEAM has first-class hot code loading. Plugins can be:
- Compiled
.beamfiles: Loaded via:code.load_file/1or:code.load_binary/3 - Mix dependencies: Standard Hex packages added to
mix.exs - Script plugins:
.exsfiles evaluated viaCode.eval_file/1(similar to jiti's dynamic loading) - OTP releases with plugins: Use
:release_handlerfor hot upgrades
- Compiled
- Plugin discovery: Scan a
plugins/directory for.beamfiles or.exsscripts. - Plugin contract: Elixir behaviours enforce the interface at compile time. Runtime plugin installation is gated by
allow_runtime_install: falseby default (security consideration -- see Doc 02). - This is a massive BEAM advantage. No transpilation, no module resolution hacks, no bundler. Plugins are just modules that implement a behaviour.
defmodule ClEx.Plugins.Plugin do
@callback init(config :: map()) :: {:ok, state :: term()} | {:error, %ClEx.Error{}}
@callback handle_event(event :: ClEx.Signal.t(), state :: term()) :: {:ok, state :: term()}
@callback tools() :: [Jido.Action.t()]
@optional_callbacks [tools: 0]
end2.18 Config (JSON5 + Zod) --> NimbleOptions + Application Config (EXISTS)
OpenClaw Role: Configuration loading (JSON5 format) with runtime validation (Zod schemas). Supports nested config, defaults, and type coercion.
CL_EX Equivalent: NimbleOptions + Elixir's Config system.
Status: EXISTS
Integration Notes:
NimbleOptionsprovides declarative option schemas with types, defaults, validation, and documentation generation. It is the Zod equivalent for Elixir.- Elixir's
Configsystem (config/config.exs,config/runtime.exs) replaces JSON5 files. - For user-facing config files: Support TOML via
tomlhex package (more user-friendly than Elixir syntax for non-developers). - Config validation happens at application startup -- fail fast with clear error messages.
defmodule ClEx.Config do
@schema [
agents: [
type: {:list, :keyword_list},
default: [],
doc: "List of agent configurations",
keys: [
id: [type: :string, required: true],
model: [type: :string, default: "claude-sonnet-4-20250514"],
workspace: [type: :string],
tools: [type: {:list, :atom}, default: [:read, :write, :edit, :search]]
]
],
channels: [
type: :keyword_list,
default: [],
doc: "Channel configurations"
]
]
def validate!(opts), do: NimbleOptions.validate!(opts, @schema)
end3. Dependency Graph
┌─────────────────────────────────────────────────┐
│ cl_ex (core) │
│ Top-level application + public API │
└──────────────────────┬──────────────────────────┘
│
┌─────────────────┬───────────────────┼───────────────────┬─────────────────┐
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────┐ ┌──────────────┐
│ cl_ex_transport │ │ cl_ex_agent │ │ cl_ex_tools │ │ cl_ex_memory │ │ cl_ex_observe│
│ (Transport) │ │ (Agent Runtime) │ │ (Tool Framework)│ │ (Memory/RAG) │ │ (Telemetry) │
│ │ │ │ │ │ │ │ │ │
│ Channel behaviour│ │ Jido.AgentServer │ │ Jido.Action-based│ │ Bedrock store│ │ AITrace │
│ Bandit + Plug │ │ Session mgmt │ │ Coding tools │ │ portfolio_* │ │ OpenTelemetry│
│ Webhook ingress │ │ LLM adapter layer│ │ Policy enforce │ │ embed_ex │ │ Metrics │
└────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘ └──────────────┘ └──────────────┘
│ │ │
▼ │ ▼
┌──────────────┐ │ ┌──────────────────┐
│ Adapters: │ │ │ cl_ex_sandbox │
│ Telegram │ │ │ (Sandboxed Exec) │
│ Discord │ │ │ │
│ Slack │ │ │ Docker/Firecracker│
│ WhatsApp │ │ │ Resource limits │
│ Signal │ │ └──────────────────┘
│ HTTP/WS │ │
└──────────────┘ │
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ cl_ex_security │ │ cl_ex_mesh │ │ cl_ex_plugins │
│ (Security) │ │ (Distribution) │ │ (Plugin System) │
│ │ │ │ │ │
│ GUARDRAIL engine │ │ libcluster │ │ Behaviour-based │
│ Shield zero-trust│ │ Bedrock distrib. │ │ Hot code loading │
│ Capability tokens│ │ :pg process grps │ │ Plugin discovery │
└──────────────────┘ └──────────────────┘ └──────────────────┘
════════════════════════════════════════════════════════════════════════════
ECOSYSTEM + EXTERNAL HEX DEPENDENCIES
════════════════════════════════════════════════════════════════════════════
Ecosystem (custom) LLM/AI SDKs Channels Infrastructure
────────────────── ─────────── ──────── ──────────────
jido altar_ai ex_gram bandit
bedrock claude_agent_sdk nostrum plug
foundation codex_sdk req phoenix_pubsub
synapse gemini_ex floki quantum
flowstone ollixir wallaby oban
guardrail langchain (optional) libcluster
shield bumblebee telemetry
portfolio_core exla
portfolio_index
embed_ex Storage Utilities
exdantic ─────── ─────────
aitrace ecto jason
pgvector nimble_options
exqlite (fallback) dotenvy
owl
ratatouille
4. Heterogeneous Stack Diagrams
4.1 Minimal: Single BEAM Node, Embedded in Phoenix App
For developers who want to add AI agent capability to an existing Phoenix application.
┌─────────────────────────────────────────────────────────────────┐
│ Single BEAM Node (fly.io / VPS) │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Phoenix Application │ │
│ │ │ │
│ │ ┌─────────────┐ ┌──────────────┐ ┌────────────────┐ │ │
│ │ │ Phoenix │ │ Phoenix │ │ Your existing │ │ │
│ │ │ Router │ │ LiveView │ │ business logic │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ /webhook/* │ │ /agent/chat │ │ │ │ │
│ │ └──────┬───────┘ └──────┬───────┘ └────────────────┘ │ │
│ │ │ │ │ │
│ │ ┌──────▼──────────────────▼──────────────────────────┐ │ │
│ │ │ ClEx (embedded) │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │ │
│ │ │ │ 1 Agent │ │ Session │ │ LLM (altar_ai) │ │ │ │
│ │ │ │ (Jido) │ │ (ETS+BD) │ │ Anthropic/OpenAI │ │ │ │
│ │ │ └──────────┘ └──────────┘ └──────────────────┘ │ │ │
│ │ │ ┌──────────┐ ┌──────────┐ │ │ │
│ │ │ │ Telegram │ │ Memory │ │ │ │
│ │ │ │ Channel │ │ (SQLite) │ │ │ │
│ │ │ └──────────┘ └──────────┘ │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ Resources: 1 CPU, 512MB RAM, 1GB disk │
│ Agents: 1-10 concurrent │
│ Channels: 1-2 │
└─────────────────────────────────────────────────────────────────┘
mix.exs:
defp deps do
[
{:phoenix, "~> 1.7"},
{:cl_ex, "~> 0.1"}, # <-- Just add this
# ... your existing deps
]
endapplication.ex:
children = [
# ... your existing children
{ClEx, config: Application.get_env(:my_app, :cl_ex)}
]4.2 Standard: Multi-Node BEAM Cluster
For teams running multiple agents across channels with high availability.
┌──────────────────────┐
│ Load Balancer │
│ (Fly.io / AWS ALB) │
└──────────┬───────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ BEAM Node A │ │ BEAM Node B │ │ BEAM Node C │
│ (us-east-1) │ │ (eu-west-1) │ │ (ap-southeast-1)│
│ │ │ │ │ │
│ ┌──────────────┐ │ │ ┌──────────────┐ │ │ ┌──────────────┐ │
│ │ ClEx Core │ │ │ │ ClEx Core │ │ │ │ ClEx Core │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ Agents: 500 │ │ │ │ Agents: 500 │ │ │ │ Agents: 500 │ │
│ │ Telegram ch. │ │ │ │ Discord ch. │ │ │ │ Slack ch. │ │
│ │ Slack ch. │ │ │ │ WhatsApp ch. │ │ │ │ Web ch. │ │
│ └──────────────┘ │ │ └──────────────┘ │ │ └──────────────┘ │
│ │ │ │ │ │
│ ┌──────────────┐ │ │ ┌──────────────┐ │ │ ┌──────────────┐ │
│ │ Gateway │ │ │ │ Gateway │ │ │ │ Gateway │ │
│ │ (Bandit) │ │ │ │ (Bandit) │ │ │ │ (Bandit) │ │
│ └──────────────┘ │ │ └──────────────┘ │ │ └──────────────┘ │
│ │ │ │ │ │
│ ┌──────────────┐ │ │ ┌──────────────┐ │ │ ┌──────────────┐ │
│ │ Bedrock │◄├─┤►│ Bedrock │◄├─┤►│ Bedrock │ │
│ │ (Distributed │ │ │ │ (Distributed │ │ │ │ (Distributed │ │
│ │ State/Raft) │ │ │ │ State/Raft) │ │ │ │ State/Raft) │ │
│ └──────────────┘ │ │ └──────────────┘ │ │ └──────────────┘ │
└────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘
│ │ │
│ ┌────────────────┤ │
│ │ libcluster │ (Gossip / DNS) │
│ │ auto-discovery │
└────┴─────────────┬──┴─────────────────────┘
│
┌────────▼────────┐
│ PostgreSQL │
│ + pgvector │
│ (fallback DB) │
│ │
│ Vectors/search │
│ Analytics │
└─────────────────┘
Total capacity: ~1500 concurrent agents
Channels: All supported
State: Bedrock (Raft consensus) for sessions, config, and distributed KV
Failover: Automatic (Bedrock replicates state; OTP restarts agents on surviving nodes)
Session affinity: Not needed (Bedrock provides consistent reads from any node)
4.3 Full: BEAM Cluster + Sprites + Cloud VPS Auto-Provisioning
For power users and organizations running autonomous agents that need compute resources.
┌─────────────────────────────────────────────────────────────────────────────┐
│ CONTROL PLANE (BEAM Cluster) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Node A │ │ Node B │ │ Node C │ │ Node D │ │
│ │ Agent Mesh │ │ Agent Mesh │ │ Channel Hub │ │ Scheduler │ │
│ │ 100 agents │ │ 100 agents │ │ All channels │ │ Quantum │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ libcluster │ │ │ │
│ └──────────────────┴──────────────────┴──────────────────┘ │
└────────────────────────────────┬────────────────────────────────────────────┘
│
┌────────────┴────────────┐
│ Sprite Orchestrator │
│ (cl_ex_sprites) │
│ │
│ Provisions, monitors, │
│ and reclaims compute │
└────┬──────────┬─────────┘
│ │
┌────────────┘ └────────────┐
▼ ▼
┌───────────────────────┐ ┌───────────────────────┐
│ Sprite Pool A │ │ Sprite Pool B │
│ (Hetzner ARM VPS) │ │ (AWS Spot Instances) │
│ │ │ │
│ ┌─────────────────┐ │ │ ┌─────────────────┐ │
│ │ Sprite 1 │ │ │ │ Sprite 5 │ │
│ │ Docker sandbox │ │ │ │ GPU instance │ │
│ │ Agent workspace │ │ │ │ Bumblebee │ │
│ │ 2 CPU / 4GB │ │ │ │ Local embeddings│ │
│ ├─────────────────┤ │ │ │ Local LLM │ │
│ │ Sprite 2 │ │ │ │ 8 CPU / 32GB │ │
│ │ Docker sandbox │ │ │ ├─────────────────┤ │
│ │ Agent workspace │ │ │ │ Sprite 6 │ │
│ │ 2 CPU / 4GB │ │ │ │ Browser farm │ │
│ ├─────────────────┤ │ │ │ Playwright │ │
│ │ Sprite 3 │ │ │ │ 4 CPU / 8GB │ │
│ │ Docker sandbox │ │ │ └─────────────────┘ │
│ │ 2 CPU / 4GB │ │ │ │
│ ├─────────────────┤ │ └───────────────────────┘
│ │ Sprite 4 │ │
│ │ Docker sandbox │ │
│ │ 2 CPU / 4GB │ │
│ └─────────────────┘ │
└───────────────────────┘
Sprite lifecycle:
1. Agent needs compute → Orchestrator provisions VPS via cloud API
2. Sprite boots → Connects back to BEAM cluster as hidden node
3. Agent dispatches work → Sprite executes in Docker sandbox
4. Work complete → Sprite idles → Auto-reclaimed after timeout
5. Crash → Orchestrator detects via BEAM monitor → Reprovisions
Cloud providers supported:
- Hetzner (ARM, cheapest)
- AWS (Spot instances, GPU)
- GCP, Azure, DigitalOcean, Vultr, Linode
- Fly.io (BEAM-native, fastest provision)
4.4 Enterprise: Full + ASKA Security + Shield Zero-Trust
For regulated industries, government, and high-security deployments.
┌─────────────────────────────────────────────────────────────────────────────────┐
│ ASKA SECURITY PERIMETER │
│ (Adaptive Security Kernel Architecture) │
│ │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ Shield Zero-Trust Layer │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────────────┐ │ │
│ │ │ Identity │ │ Policy │ │ Audit │ │ Secrets │ │ │
│ │ │ Provider │ │ Engine │ │ Logger │ │ Manager │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ mTLS certs │ │ OPA/Rego │ │ Immutable │ │ Vault / SOPS │ │ │
│ │ │ SPIFFE IDs │ │ ABAC rules │ │ append-only │ │ Rotate on use │ │ │
│ │ │ JWT + RBAC │ │ Per-agent │ │ Signed log │ │ Env isolation │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └───────┬────────┘ │ │
│ │ └────────────────┴────────────────┴──────────────────┘ │ │
│ └──────────────────────────────┬────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────────▼────────────────────────────────────────────┐ │
│ │ BEAM CLUSTER (Encrypted Mesh) │ │
│ │ All inter-node traffic: TLS 1.3 │ │
│ │ Cookie: Derived from HSM / TPM │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Agent Node │ │ Agent Node │ │ Gateway │ │ │
│ │ │ (Enclave A) │ │ (Enclave B) │ │ (DMZ Node) │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ Agents run │ │ Agents run │ │ TLS termn. │ │ │
│ │ │ in isolated │ │ in isolated │ │ WAF rules │ │ │
│ │ │ schedulers │ │ schedulers │ │ Rate limit │ │ │
│ │ │ │ │ │ │ DDoS prot. │ │ │
│ │ │ Memory: │ │ Memory: │ │ │ │ │
│ │ │ encrypted │ │ encrypted │ │ Channels: │ │ │
│ │ │ at rest │ │ at rest │ │ All, with │ │ │
│ │ │ │ │ │ │ E2E proxy │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Tool Execution: Sandboxed Sprites (Firecracker microVMs) │ │ │
│ │ │ │ │ │
│ │ │ - Each agent gets dedicated microVM │ │ │
│ │ │ - Network: Isolated VPC, egress whitelist only │ │ │
│ │ │ - Filesystem: Encrypted, ephemeral, no persistence by default │ │ │
│ │ │ - Resource: Hard cgroup limits (CPU, RAM, I/O, network) │ │ │
│ │ │ - Lifetime: Max 1 hour, then force-terminated and shredded │ │ │
│ │ └──────────────────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ Data Layer (Encrypted) │ │
│ │ │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌────────────────────────┐ │ │
│ │ │ PostgreSQL │ │ Object Storage │ │ Audit Store │ │ │
│ │ │ (TDE enabled) │ │ (S3 + SSE-KMS) │ │ (Immutable, signed) │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ Sessions │ │ Agent artifacts │ │ Every agent action │ │ │
│ │ │ Vectors │ │ Media files │ │ Every tool invocation │ │ │
│ │ │ Config │ │ Exports │ │ Every LLM call │ │ │
│ │ │ (Row-level │ │ │ │ Tamper-evident chain │ │ │
│ │ │ encryption) │ │ │ │ │ │ │
│ │ └──────────────────┘ └──────────────────┘ └────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │
│ Compliance: SOC2 Type II, HIPAA, FedRAMP (with appropriate CSP) │
│ Key management: AWS KMS / GCP KMS / HashiCorp Vault │
│ Network: Private VPC, no public IPs on agent/data nodes │
│ Observability: OpenTelemetry → encrypted Grafana stack │
└─────────────────────────────────────────────────────────────────────────────────┘
5. What's Missing
5.1 Critical Gaps (Must Build for v0.1)
| Gap | Severity | Description | Estimated Effort |
|---|---|---|---|
Agent Runtime (cl_ex_agent) |
CRITICAL | Jido provides the core agent model (Jido.Agent, Jido.AgentServer), so the work is integration rather than greenfield. Must build: LLM adapter layer (over altar_ai + provider SDKs), session management (ClEx.Agent.Session), and the ClEx.Agent.Runtime orchestration wrapper. |
3-4 weeks |
Transport Layer (cl_ex_transport) |
CRITICAL | No unified channel behaviour exists. Each platform library (ExGram, Nostrum, etc.) has its own API. Need a ClEx.Transport.Channel behaviour, normalization pipeline (to ClEx.Signal), and per-platform adapters. |
2-3 weeks |
Session Management (within cl_ex_agent) |
HIGH | Session lifecycle is part of cl_ex_agent (not a separate package). Must integrate with Bedrock for persistence, implement compaction and reset policies. Effort is lower since Bedrock provides the state backend. |
1-2 weeks |
| WhatsApp Adapter | HIGH | No Elixir WhatsApp library exists. Must wrap Go's whatsmeow via Port or use Business Cloud API. |
3-4 weeks (Port) or 1 week (Cloud API) |
Tool Framework (cl_ex_tools) |
HIGH | Jido provides Jido.Action as the tool primitive, so the behaviour contract exists. Must build: standard tool library (read, write, edit, search, bash), policy enforcement via cl_ex_security (GUARDRAIL), and sandboxing hooks. |
2-3 weeks |
Sandbox Execution (cl_ex_sandbox) |
MEDIUM | No existing package provides agent-oriented sandboxed code execution. Must build Docker/Firecracker integration. | 2-3 weeks |
5.2 Channel Adapter Libraries -- Detailed Gap Analysis
Channel Elixir Library Quality Gap / Work Needed
───────────── ──────────────── ───────── ──────────────────────────────────
Telegram ex_gram Good Wrap in ClEx.Transport.Channel behaviour.
Needs: media handling adapter,
inline keyboard → CL_EX action map.
Effort: 3-5 days.
Discord nostrum Excellent Wrap in ClEx.Transport.Channel behaviour.
Needs: slash command registration,
embed builder, component mapping.
Effort: 3-5 days.
Slack slack_elixir Fair Thin wrapper, may need custom
Events API webhook handler.
Socket Mode: must build.
Effort: 1-2 weeks.
WhatsApp NONE N/A Biggest gap. Options:
A) Cloud API (Req) - 1 week
B) whatsmeow Port - 3-4 weeks
C) Baileys Port - 2-3 weeks
Effort: 1-4 weeks.
Signal NONE N/A signal-cli exists (Java). Wrap as
Port or use Signal REST API.
Effort: 2-3 weeks.
iMessage NONE N/A macOS-only. Requires AppleScript
or BlueBubbles server integration.
Effort: 2-3 weeks.
LINE NONE N/A REST API is straightforward.
Build from scratch with Req.
Effort: 1 week.
Google Chat NONE N/A REST API + PubSub. Build with
Req + goth (Google auth).
Effort: 1 week.
Matrix NONE partial Poor matrix_sdk_ex exists but
unmaintained. Likely rebuild.
Effort: 2 weeks.
MS Teams NONE N/A Bot Framework REST API.
Build with Req.
Effort: 1-2 weeks.
5.3 CLI Framework Gap
Elixir has no single "commander.js equivalent" that provides:
- Subcommand trees with help generation
- Interactive prompts
- Rich terminal output (tables, colors, spinners)
- Shell completions
Solution: Compose from existing pieces:
OptionParser(stdlib) -- argument parsingOwl-- rich output, tables, spinners, progress barsIO.ANSI(stdlib) -- colors- Custom subcommand router (~100 lines)
Burrito-- package as standalone binary (replaces npm global install)
This is a minor gap -- the pieces exist, they just need composition.
5.4 Other Gaps
| Gap | Description | Solution |
|---|---|---|
| Sprite Orchestrator | Cloud VPS provisioning and management. No existing package. | Build within cl_ex_mesh using cloud provider APIs via Req. 4-6 weeks. |
| Security Layer | Zero-trust security kernel. | Build cl_ex_security. Integrate GUARDRAIL (policy engine) and Shield (zero-trust auth). Leverage OTP distribution security (TLS), add SPIFFE/mTLS. 6-8 weeks. |
| Skill System | Reusable prompt fragments with discovery. | Leverage Jido.Skill for higher-level compositions of Jido.Action tools. Build skill registry and filesystem discovery. 1-2 weeks. |
| Memory/RAG Pipeline | End-to-end: chunk, embed, store, search, inject. | Build cl_ex_memory. Integrate portfolio_core/portfolio_index for RAG, embed_ex for embeddings, Bedrock for storage. 2-3 weeks. |
| Config File Format | User-friendly config for non-Elixir developers. | Support TOML via toml package + NimbleOptions validation. 3-5 days. |
| Hot Code Upgrade | Seamless agent updates without dropping sessions. | Leverage OTP releases + :release_handler. Built-in but needs orchestration. 1-2 weeks. |
6. Generic Agentic Stack
The Vision: One Dependency, Full Agent Infrastructure
Any Elixir developer adds one line to their mix.exs and gets a complete, production-grade AI agent platform:
# mix.exs
defp deps do
[
{:cl_ex, "~> 0.1"}
]
endThis single dependency brings the entire stack. Here is what activates and how.
6.1 What You Get
┌─────────────────────────────────────────────────────────────────────────────┐
│ │
│ {:cl_ex, "~> 0.1"} │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ AUTO-DISCOVERED BEAM CLUSTERING │ │
│ │ │ │
│ │ ClEx starts libcluster automatically. │ │
│ │ Strategy auto-detected: │ │
│ │ - Fly.io → Fly.io DNS strategy (zero config) │ │
│ │ - K8s → Kubernetes DNS strategy (zero config) │ │
│ │ - Local → Epmd strategy (zero config) │ │
│ │ - Custom → Configure in application env │ │
│ │ │ │
│ │ Result: Add more nodes → agents auto-distribute. │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ SELF-HEALING AGENT MESH │ │
│ │ │ │
│ │ Every agent is a supervised Jido.AgentServer (GenServer) process. │ │
│ │ Bedrock + cl_ex_mesh distribute agents across cluster. │ │
│ │ │ │
│ │ Node dies → cl_ex_mesh restarts agents on surviving nodes. │ │
│ │ Agent crashes → DynamicSupervisor restarts it. │ │
│ │ Session restored from persistent storage automatically. │ │
│ │ │ │
│ │ You write: │ │
│ │ ClEx.chat("my-agent", "Hello!", model: "claude-sonnet-4-20250514")│ │
│ │ That's it. Crash recovery, distribution, and monitoring are free. │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ZERO-CONFIG CHANNEL CONNECTIVITY │ │
│ │ │ │
│ │ Set environment variables. Channels auto-activate: │ │
│ │ │ │
│ │ CLEX_TELEGRAM_BOT_TOKEN=xxx → Telegram channel starts │ │
│ │ CLEX_DISCORD_BOT_TOKEN=xxx → Discord channel starts │ │
│ │ CLEX_SLACK_BOT_TOKEN=xxx → Slack channel starts │ │
│ │ CLEX_WHATSAPP_API_TOKEN=xxx → WhatsApp channel starts │ │
│ │ │ │
│ │ No channel configured? HTTP webhook gateway starts by default. │ │
│ │ All channels normalize to %ClEx.Signal{} automatically. │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ EMBEDDED AI AGENT WITH TOOLS │ │
│ │ │ │
│ │ Built-in tools (opt-in via config): │ │
│ │ :read, :write, :edit → Filesystem operations │ │
│ │ :search → Code/text search │ │
│ │ :bash → Shell execution (sandboxed) │ │
│ │ :web_search, :web_fetch → Internet access │ │
│ │ :message → Cross-channel messaging │ │
│ │ :memory_search → Semantic memory recall │ │
│ │ :browser → Headless browser automation │ │
│ │ │ │
│ │ Custom tools in 5 lines: │ │
│ │ defmodule MyTool do │ │
│ │ use Jido.Action, │ │
│ │ name: "my_tool", │ │
│ │ description: "Does something useful", │ │
│ │ schema: [input: [type: :string, required: true]] │ │
│ │ def run(%{input: val}, _ctx), do: {:ok, process(val)} │ │
│ │ end │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ MEMORY / RAG CAPABILITY │ │
│ │ │ │
│ │ Three memory tiers, all automatic: │ │
│ │ │ │
│ │ 1. Session Memory (Bedrock + ETS cache) │ │
│ │ - Current conversation transcript │ │
│ │ - Auto-compaction when token limit approached │ │
│ │ - Distributed across cluster via Bedrock (Raft consensus) │ │
│ │ │ │
│ │ 2. Semantic Memory (portfolio_core + portfolio_index) │ │
│ │ - Long-term knowledge base with RAG pipeline │ │
│ │ - Auto-embedded via embed_ex (local Bumblebee or API) │ │
│ │ - Searched automatically when agent needs context │ │
│ │ - Backend: Bedrock (primary) or pgvector/SQLite (fallback) │ │
│ │ │ │
│ │ 3. Episodic Memory (Event Log) │ │
│ │ - What happened, when, across which channels │ │
│ │ - Queryable timeline of agent actions │ │
│ │ - Powers "what did I do yesterday?" queries │ │
│ │ │ │
│ │ Config: │ │
│ │ config :cl_ex_memory, │ │
│ │ backend: :bedrock, # or :pgvector, :sqlite, :in_memory │ │
│ │ embedding: :local, # or :openai, :voyage (via embed_ex) │ │
│ │ auto_embed: true │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
6.2 Progressive Disclosure: From Simple to Full
LEVEL 0: Library Mode (10 seconds)
══════════════════════════════════
ClEx.chat("What is 2+2?")
#=> "2 + 2 equals 4."
No config. No processes. Just a function call.
Uses default model, no tools, no memory.
LEVEL 1: Agent Mode (1 minute)
══════════════════════════════
# In your application.ex:
children = [{ClEx, agents: [%{id: "helper", model: "claude-sonnet-4-20250514"}]}]
# Anywhere in your code:
ClEx.chat("helper", "Summarize this document", context: doc_text)
#=> "The document discusses..."
One supervised agent. Tools optional. Memory optional.
LEVEL 2: Channel Mode (5 minutes)
═════════════════════════════════
# config/runtime.exs:
config :cl_ex,
agents: [%{id: "bot", model: "claude-sonnet-4-20250514", tools: [:search, :memory_search]}],
channels: [
telegram: [token: System.get_env("CLEX_TELEGRAM_BOT_TOKEN")],
discord: [token: System.get_env("CLEX_DISCORD_BOT_TOKEN")]
]
# That's it. Bot is now live on Telegram and Discord.
# Messages auto-route to the "bot" agent.
# Sessions auto-managed per user per channel.
LEVEL 3: Multi-Agent Mode (15 minutes)
══════════════════════════════════════
config :cl_ex,
agents: [
%{id: "triage", model: "claude-haiku", tools: [:message],
system: "Route questions to the right specialist agent."},
%{id: "coder", model: "claude-sonnet-4-20250514", tools: [:read, :write, :edit, :bash],
workspace: "/app/code"},
%{id: "research", model: "claude-sonnet-4-20250514", tools: [:web_search, :web_fetch, :memory_search]},
%{id: "ops", model: "claude-haiku", tools: [:bash, :message],
system: "You manage infrastructure."}
],
channels: [
telegram: [token: "..."],
slack: [token: "...", channel_routing: %{"#dev" => "coder", "#research" => "research"}]
]
# Triage agent receives all unrouted messages.
# It uses the :message tool to hand off to specialists.
# Each specialist has its own session, tools, and workspace.
LEVEL 4: Distributed + Sprites (30 minutes)
═══════════════════════════════════════════
config :cl_ex,
cluster: [strategy: :fly_io], # Auto-discover peers
sprites: [
provider: :hetzner, # Auto-provision VPS
pool_size: {2, 10}, # Min 2, max 10 sprites
instance_type: "cax11" # ARM, 2 CPU, 4GB
],
# ... agents and channels as before