Catalog PyPI
Back to packageRELEASE DETAILS

0.1.0b2

PyPI

aelix

Aelix — a pi-faithful Python agent runtime (providers, tools, compaction, extensions, TUI).

agentaianthropicclicoding-agentllmopenaitui
Published9 Sept 2026
Direct dependencies3
Transitive dependenciesNot calculated

README

Version 0.1.0b2 documentation

Aelix — the A×X mark above the Aelix wordmark

Your own agent world — built on the Python ecosystem.

한국어 README →

License: Apache 2.0 Python 3.11+

Aelix is a small core. The plugins and extensions are the ecosystem, and an extension is just a Python function — so the stack you already work in becomes the agent's toolbox. Self-hosted, auditable, and running on the model budgets you already pay for.

Aelix demo — the agent writes a DuckDB query tool into my_ext.py, /reload hot-loads it without restarting, and the next prompt runs it in-process against a Parquet file

The agent extends itself: it authors a duckdb_query tool into my_ext.py, /reload hot-loads it without restarting, and the very next prompt runs it in-process. Idle waits are cut and the recording plays at 2×.


What Aelix is

An agent runtime and extension platform in pure Python. It ships today as a terminal coding agent — its first workload, not its boundary. Read every line it runs, keep it entirely inside your own perimeter, and extend it with plain Python functions that import your existing stack — DuckDB, an internal SDK, a warehouse client — directly, in-process.

It sends nothing about you anywhere; there is no telemetry. It does make a few requests for itself, and they are the whole list: a once-a-day release check (interactive sessions only — headless runs never check, and /settings → Check for updates turns it off from the next launch), the first-use ripgrep/fd download, and the extension-catalog fetch. --offline turns those off.

Install

Aelix installs from GitHub Releases through a checksum-verified installer. It bootstraps uv if needed, verifies every Aelix wheel against the release's SHA256SUMS manifest, and installs the global aelix command pinned to the version that manifest named.

# macOS, Linux
curl -fsSL https://raw.githubusercontent.com/handochan/aelix-ai/main/install.sh | sh
# Windows — EXPERIMENTAL, and needs v0.1.0-beta.2 or newer (PowerShell 5.1+)
powershell -ExecutionPolicy Bypass -c "irm https://raw.githubusercontent.com/handochan/aelix-ai/main/install.ps1 | iex"

v0.1.0-beta.2 is the release every Windows fix landed in, and the installer takes the newest release — so on an older one that line installs a build where none of this works. Scope and evidence: Platform support.

Both read the same environment variables: AELIX_VERSION pins a release tag (vX.Y.Z-beta.N), AELIX_EXTRAS picks extras (default tui; empty installs the headless CLI, POSIX only), GITHUB_TOKEN lifts the anonymous GitHub API rate limit. Each has to reach the shell on the far side of the pipe, which is what makes the obvious spelling wrong on both platforms — VAR=x curl … | sh sets it for curl alone, and from a PowerShell prompt powershell -c "$env:VAR=…" is expanded by the outer shell before the child sees it (measured on pwsh 7.6). The getting-started guide gives the form that works on each. Re-run the same line to upgrade; on a PATH miss both installers now run uv tool update-shell for you instead of printing it. uv tool uninstall aelix removes it.

PyPI carries a placeholder until 0.1.0b2. Today all four names hold a metadata-only 0.0.0a0 reservation, so pip install aelix exits 0 and installs nothing runnable — no aelix command, and import aelix raises ModuleNotFoundError — while uv tool install aelix@latest deletes an existing install. From 0.1.0b2 on that trap is closed: every candidate on the index is a pre-release, and pip and uv both take the newest one in that case, so uv tool install 'aelix[tui]' and pipx install aelix resolve the real thing (ADR-0240). The installer above stays the recommended path — it is the only one that checks the release's SHA256SUMS — and an install it made upgrades by re-running it, not by uv tool upgrade.

Platform support

macOS, Linux and Windows. Windows became usable in v0.1.0-beta.2 and has the thinnest evidence of the three: CI runs the full suite on ubuntu-latest and windows-latest under Python 3.11 and 3.12 and runs install.ps1 end to end under both pwsh and Windows PowerShell 5.1, and on 2026-09-09 one person drove this candidate on one Windows machine — a bash call's Korean output came back as Korean, the model reported it was on PowerShell and used PowerShell syntax instead of &&, and the TUI drew correctly on a Windows console. That is the whole of it: one person, one machine, one locale, plus a green leg. No long-running use, no second machine, no third locale, and three of this release's Windows fixes are argued from source and CI alone.

A program that opens the Windows console directly — a git credential prompt, Read-Host -AsSecureString, Get-Credential — can still prompt there and burn a command's whole timeout. Nothing in this release touches that, and no test on the leg reaches it.

Which fix is measured how, and what is still open: getting-started → Windows and SLICE-STATUS.md; the port is tracked in #110.

Quick start

aelix                                            # interactive agent (TUI)
aelix --print "what changed in this repo?"       # one-shot, headless
aelix --model anthropic/claude-haiku-4-5 "summarise this repo"
aelix status                                     # trust, extensions, TLS — starts no session
aelix docs                                       # the guides, bundled in the wheel

Aelix needs a provider credential: set ANTHROPIC_API_KEY / OPENAI_API_KEY / OPENROUTER_API_KEY, run /login inside the TUI (Copilot / subscription OAuth), pass --api-key, or configure ~/.aelix/agent/models.json. See the providers guide.

On first use of grep or find, Aelix downloads ripgrep and fd into ~/.aelix/agent/bin so both honour .gitignore. These are the only binaries fetched at runtime, --offline skips them, and copies already on your PATH are preferred. The @ file menu uses that same fd when it can find one, so it stops offering the files git ignores — type the directory to reach them anyway (@target/), or turn /settings → Gitignore in @ menu off. Without an fd it falls back to a directory walk that applies no ignore rules. Either enumerator stops after 20 000 paths, and with ignore rules off a big ignored build tree can eat that budget before your own directories are reached.

Why Aelix

  • 🐍 Extensions are just Python. A tool is a plain function — no plugin language, no out-of-process bridge. The same ExtensionAPI registers tools, slash commands, providers, renderers, themes and your own /login flow, and every extension hot-reloads without a restart. Even policy and guardrails are swappable built-in extensions.
  • 💳 Runs on the budget you already own. Anthropic, OpenAI, OpenRouter, Gemini/Vertex, Cloudflare and the GitHub Copilot seat you already sign in with. Route cheap work and hard reasoning to different models in one session. No metered credits, no new vendor.
  • 🔍 Auditable and self-hosted. Fully open source, no telemetry, built for closed networks. --offline turns off the requests Aelix makes for itself. Trust lives in code you can read — the answer to "why run an agent I didn't write?"
  • ⚙️ Scriptable and headless. --print, line-delimited --mode json, and a --mode rpc JSONL protocol make Aelix embeddable in pipelines, CI and evaluation loops.

Extensions are just Python — query your data stack in-process

An extension is a setup(aelix) function. No plugin language, no bridge, so a tool imports your existing stack and hands results straight back to the model:

# my_ext.py  —  loads with:  aelix -e ./my_ext.py
from typing import Any

import duckdb                              # your own dependency, imported in-process

from aelix_coding_agent.extensions.api import ExtensionAPI
from aelix_agent_core.types import AgentTool
from aelix_ai.tools import ToolExecutionContext, ToolResult
from aelix_ai.messages import TextContent


async def _query(args: dict[str, Any], context: ToolExecutionContext) -> ToolResult:
    # DuckDB reads Parquet/CSV/JSON in place — no load step, no copy.
    rel = duckdb.sql(args["sql"]).limit(args.get("limit", 20))
    return ToolResult(content=[TextContent(text=str(rel))])


def setup(aelix: ExtensionAPI) -> None:
    aelix.register_tool(AgentTool(
        name="duckdb_query",
        description="Run DuckDB SQL straight against Parquet/CSV/JSON files. No load step.",
        parameters={
            "type": "object",
            "properties": {
                "sql": {"type": "string", "description": "SELECT … FROM 'data/*.parquet'"},
                "limit": {"type": "integer", "description": "Max rows returned (default 20)."},
            },
            "required": ["sql"],
        },
        execute=_query,
    ))

Embed it anywhere Python runs — a notebook, an Airflow/Prefect/Dagster task, a CI job:

aelix --print "which channels in data/orders.parquet have missing churn scores?"
aelix --mode json "run the eval suite and summarise failures"   # line-delimited events

See writing an extension for the full surface, and the Aelix Marketplace — the catalog Aelix reads by default. It ships empty for the beta and is open for submissions.

Providers

Adapters are hand-written and keyed by wire protocol, not by vendor — no litellm, no generic wrapper layer — so provider-specific behaviour (Anthropic thinking-block replay, per-model /responses vs /chat/completions routing, Copilot enterprise host resolution) is preserved rather than flattened. Six adapters exist, and catalog providers ride them: OpenRouter and Cloudflare Workers AI both run on openai-completions, while every bundled OpenAI model runs on openai-responses.

ProviderRuns onStatus
Anthropicanthropic-messages✅ supported
OpenRouteropenai-completions✅ supported
GitHub Copilot (individual / Business)mixed✅ supported
OpenAIopenai-responses🧪 experimental
GitHub Copilot (Enterprise)mixed🧪 not live-tested
Google Gemini / Vertexgoogle-*🧪 experimental
Cloudflare Workers AIopenai-completions🧪 experimental

Every bundled OpenAI model routes through openai-responses, so choosing one puts you on the experimental row; the openai-completions adapter's real traffic is OpenRouter, Cloudflare and other OpenAI-compatible hosts.

Copilot Enterprise is implemented and unit-tested — host resolution, domain prompting, persistence — but has never been exercised against a live Enterprise seat.

Three catalogued providers have no adapter in this build (amazon-bedrock, azure-openai-responses, mistral). The /model picker hides them and dispatch refuses them with a message naming the protocols it does support — but --list-models will still print them if you set their API key. See the providers guide.

Trust and self-hosting

Built for closed networks and customer-site deployment. --offline turns off the outbound calls Aelix makes for itself — the rg/fd download, the catalog fetch, index-less extension installs, the update check — but not the provider you configured, so a closed network still needs a reachable or self-hosted model endpoint. Two gaps worth knowing: --offline before an extension subcommand is not seen (pass it after), and a git+https:// install target proceeds regardless. Policy and guardrails run as built-in extensions, so every tool call and context mutation is an auditable hook event.

Built extension artifacts carry a signed supply chain that survives an air-gapped install — aelix extension keygen | sign | trust add, and install --require-signature is fail-closed. It covers artifacts installed by path or from an index; a git+ target and an editable directory are refused under that flag rather than verified. It is also not on by default: no first-party keys are provisioned yet, so without the flag a missing signature is accepted on first use.

One input is deliberately not gated: an AGENTS.md found between your working directory and the filesystem root is read into the system prompt whether or not you trusted the project, and its text then goes to your configured provider. --no-context-files turns that off. See SECURITY.md and the project trust guide.

Known limitations (beta)

Six things worth knowing before you point Aelix at something that matters.

A run has no spend ceiling. No iteration cap, no duplicate-call detection, no cumulative token or cost budget — a model that keeps calling tools keeps costing money until it finishes or you stop it. Esc, the 600 s bash default timeout (an explicit per-call value is honoured up to an hour) and automatic compaction are real backstops; none of them bounds spend (#14, #6, #52).

Headless mode auto-approves mutating tools, and the two safety nets only see built-in ones. --print, --mode json and --mode rpc have no terminal for an approval dialog, so write, edit and bash run without asking; and both backstops match a fixed list of built-in tool names, so a tool from an MCP server, a skill or a third-party extension reaches neither GuardrailExtension nor the --permission-mode plan block. Give a headless run a container or a checkout you can throw away (#188).

One session, one terminal. Session JSONL is append-only and nothing locks it, so opening the same session twice makes one terminal's work a branch that no --resume walks — valid on disk, gone from the transcript (#137).

Transcripts keep everything, forever, unredacted. Every prompt, tool argument and tool result is written verbatim; there is no scrubbing pass. They are owner-only on macOS and Linux (0600 inside 0700), and Windows has no mode-bit equivalent for Aelix to set — either way the exposure is to whatever copies your home directory: backups, sync clients, support bundles (#138).

A successful bash call can silently lose the tail of its output. With the reader thread starved across the command's exit, up to ~64 KiB can be dropped from the end — exit_code is 0, nothing says anything went missing, and the tail is the part the model is shown. It did not reproduce in ~3,300 ordinary rounds (#260).

Delegation's spend is invisible to /cost, and its cleanup is not equal on every OS. A child's tokens never enter the parent's session — read each delegation's own footer — and a headless parent consents to spawns on its own. If Aelix itself dies, Linux's PR_SET_PDEATHSIG and a Windows job object's kill_on_close end the child; macOS has neither, and a grandchild that made its own session escapes the process group that is all the containment there is (#110).

Architecture

Three packages (a uv workspace), orchestrated by Agent and AgentHarness:

  • aelix-ai — provider-agnostic messages, streaming primitives, tool definitions. No loop, no hooks.
  • aelix-agent-core — the agent loop, Agent, AgentHarness, and the typed HookBus. No extension deps.
  • aelix-coding-agent — ExtensionAPI, extension loader, built-in PolicyExtension / GuardrailExtension.

Small kernel, broad extension surface; policy and guardrails as built-in extensions rather than core; an explicit hook bus for auditability. Full rationale in docs/.

Docs

Getting started · Providers & models · Custom models · Agent profiles · Writing an extension · Project trust · Private catalog · Releasing

Every guide except RELEASING.md ships inside the wheel, so an installed machine reads them with no network and no checkout — aelix docs, aelix docs project-trust, aelix docs --search register_tool.

Homepage → · Extension catalog →

Building from source

uv sync                  # create .venv and install all workspace packages
uv run pytest            # run the test suite
uv run aelix --help      # the real CLI

Copy .env.example to .env for live-provider credentials. A .env is admitted for provider credentials and a short list of provider-configuration names, and nothing else — it is read before Aelix knows whether it trusts the directory. That admission list is narrow but it is not nothing: a cloned repo's .env can supply the API key your session then runs on, so the prompts go to the attacker's account. What it cannot do is execute a program, relocate the credentials store, widen its own gate, or move a provider off its real host (ADR-0203). CA bundles, SDK knobs and base URLs belong in your shell.

License & attribution

Apache-2.0, with an explicit patent grant. The name and logo are separate from the code licence, and TRADEMARK.md grants more than Apache-2.0 §6 does: describing your work as built on, compatible with, or an extension of Aelix needs no permission, and neither does naming your package aelix-<something>.

Substantial portions of Aelix are a TypeScript-to-Python port of pi (reference commit 734e08e), Copyright © 2025 Mario Zechner, MIT licensed. The bundled model catalog derives from models.dev (MIT). Full third-party licence texts ship in every wheel and sdist (NOTICE, THIRD-PARTY-NOTICES.md); the dependency inventory is a CycloneDX SBOM under sbom/.

Anthropic, OpenAI, Google Gemini, GitHub Copilot, OpenRouter and Cloudflare are trademarks of their respective owners; Aelix is an independent project, and names are used only to identify the services it can connect to.

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Declared dependencies 4

Native requirements
3 direct targetsNot calculated transitive count
Package / targetRequirementRelationship
aelix-agent-core
Environment
{
  "extras": "",
  "marker": null,
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
aelix-agent-core==0.1.0b2runtime
aelix-ai
Environment
{
  "extras": "",
  "marker": null,
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
aelix-ai==0.1.0b2runtime
aelix-coding-agent
Environment
{
  "extras": "",
  "marker": null,
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
aelix-coding-agent==0.1.0b2runtime
aelix-coding-agent
Environment
{
  "extras": "tui",
  "marker": "extra == \"tui\"",
  "requires_python": ">=3.11",
  "evaluation": "NOT_EVALUATED"
}
aelix-coding-agent[tui]==0.1.0b2; extra == "tui"runtime

Distribution files

2 collected
aelix-0.1.0b2-py3-none-any.whl

bdist_wheel · 21.29 KB · Python py3

Source file
aelix-0.1.0b2.tar.gz

sdist · 5,361.89 KB · Python source

Source file

Repository

PENDING
handochan/aelix-ai

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 20:23 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package_version:87a84e18d7448662fbfec6e12cc22d8bbd8a8727b85e349f70c4f76178bd734d

Other collected versions

View all collected versions →