ALENA (Adaptive Learning Enhanced Neural Assistant) is a locally-run, modular AI assistant: a privacy-first personal copilot across voice, web and system workflows.
It runs inference on a local LLM, acts through MCP tool servers, and reaches those tools through a policy gateway that decides, records and executes every call. Nothing leaves the machine unless you configure something that does.
- What it does
- Architecture
- How the improvement loop works
- Repository layout
- Tools
- Requirements
- Quickstart
- Running each service
- Configuration
- Testing
- Documentation
- Project status
- Local LLM inference. LM Studio over its OpenAI-compatible API, using native tool calling — the planner is never asked to hand-write JSON. Runs on this machine or another one on the tailnet.
- Speech to text. Transcription by text-whisperer over the tailnet. No model, GPU or audio stack lives in this repo.
- MCP tooling. Three MCP servers, discovered at startup rather than hand-listed. Every call from an ALENA agent goes through the Tool Gateway: the policy decides, the audit log records, a pooled session executes.
- Web and voice interfaces. A Nuxt frontend with WebSocket push-to-talk, and a Telegram bot that accepts text and voice memos.
- Autonomous codebase improvement. Scans declared repositories, ingests research, runs two independent engineering reviewers, and — only behind a recorded human decision — writes a branch. See modules/improve.
CLI · Web UI · Telegram
│
▼
┌───────────────────┐ asks for a plan
│ ALENA Controller │ ─────────────────────────► LM Studio
│ (core agent loop)│ ◄───────────────────────── (local model)
└─────────┬─────────┘ prose, or a tool call
│
│ every tool call
▼
┌───────────────────┐
│ Tool Gateway │ catalog · policy · approval · audit · pool
└─────────┬─────────┘
│
┌─────────┼─────────────────┐
▼ ▼ ▼
codex-server google-calendar alena-core
(Codex CLI) (Calendar API) repo · memory · portfolio · resources
Voice: browser / Telegram ──► text-whisperer (tailnet) ──► controller
The planner never invokes a tool. It asks the gateway, which answers a fixed set of questions — is the tool in the catalog, are the arguments complete, is this agent allowed, is this repository allowed, does a human have to agree — logs the attempt, and only then calls it.
One pass a night, in order, stopping where a person is needed:
flowchart LR
subgraph nightly["alena-improve cycle — nightly 02:00, automatic"]
direction LR
S[scan] --> I[ingest research] --> R[review] --> C[score] --> P[refresh portfolio]
end
P --> G{{"you decide"}}
G -->|accept| A[action agent<br/>writes a branch]
G -->|reject| X([recorded, with a reason])
A --> O([outcome recorded])
style G stroke-width:3px
Everything left of the gate reads, thinks and writes to ALENA's own state. Nothing there touches a repository. The action agent is the only thing that does, and it never runs without a recorded acceptance behind it — see modules/improve.
A recommendation moves through these states, and only these transitions are
legal — modules/improve/decide.py rejects anything else:
stateDiagram-v2
[*] --> recommended: scored
recommended --> accepted: you accept
recommended --> rejected: you reject
rejected --> recommended: proposed again
accepted --> implemented: branch written
accepted --> rejected: reconsidered
accepted --> abandoned: dropped
implemented --> successful: it worked
implemented --> unsuccessful: it did not
implemented --> abandoned: dropped
unsuccessful --> accepted: try again
unsuccessful --> abandoned: give up
successful --> [*]
abandoned --> [*]
Rejecting, abandoning and recording a failure all require a reason. The ones that cost something are the ones that have to be explained, so the memory an agent searches before proposing an idea can say why it was turned down.
| Path | What it is |
|---|---|
alena.py |
The CLI entry point: one turn per line of input |
modules/core |
The agent loop, planner client, conversation memory, and the FastAPI controller |
modules/gateway |
Tool catalog, policy, approvals, audit log, pooled MCP sessions |
modules/improve |
The autonomous improvement orchestrator: scan, research, review, decide, act |
modules/mcp/alena-core |
ALENA's own capabilities over MCP, read-only, for Claude / ChatGPT / local models |
modules/mcp/codex-server |
Code generation, analysis and editing via the Codex CLI |
modules/mcp/google-calendar |
Google Calendar read and write |
modules/llm |
The blocking chat client for LM Studio, plus embeddings |
modules/stt |
text-whisperer client |
modules/store |
SQLite connection and migrations (~/.alena/alena.db) |
modules/telegram |
Telegram bot gateway |
modules/voice-assistant |
Voice backend (FastAPI + WebSocket) and Nuxt frontend |
config/ |
tool_policy.yaml and repositories.yaml — who may call what, against which repo |
deploy/launchd |
The nightly cycle job, and the dashboard service |
scripts/ |
Start scripts for each service combination |
Documents/ |
Design documents and contracts |
Twenty-three tools across three MCP servers, plus five readable resources. Every
one is declared in config/tool_policy.yaml with a
side effect and the agents allowed to call it; a tool that is not declared
cannot be called, even if a server advertises it.
| Server | Tools |
|---|---|
| codex-server | codex_generate codex_plan codex_analyze codex_summarize codex_doc_outline codex_test_plan (read-only) · codex_edit codex_refactor (repository write) |
| google-calendar | google_list_events (read) · google_create_event google_update_event (remote write) · google_delete_event (destructive) |
| alena-core | repo.search repo.read_file repo.find_todos repo.get_dependencies repo.get_history memory.search recommendation.search portfolio.search_capability portfolio.dependency_divergence resource.list resource.read — all read-only |
alena-core also exposes alena://repositories, alena://repositories/{id}/profile,
.../architecture, .../recommendations and alena://portfolio/capabilities
as MCP resources. resource.list and resource.read are the doorway for
clients that cannot read resources — which includes any local model, whose only
channel is the tools array.
The planner is offered exactly the tools its agent identity may call, built from the same catalog and the same policy that will later judge the call.
- Python 3.12 or 3.14 (CI tests both; the code targets 3.10+ but that floor is not exercised)
- LM Studio with a model loaded and its server started, port 1234 by default
- Codex CLI on
$PATH, for the Codex MCP server. Tested against codex-cli 0.153; older releases took a--full-autoflag that 0.153 removed, and the runner passes--sandboxinstead - text-whisperer reachable over the tailnet, for voice — see the contract
Only the first is needed to start. The rest degrade to the features that use them.
pip install -r requirements.txt
cp .env.example .env # then fill in what you need
bash scripts/start_alena_with_all_mcps.shAll services read the repo-root .env.
| Command | What it starts |
|---|---|
bash scripts/start_alena_with_all_mcps.sh |
The CLI, with every MCP server |
bash scripts/start_controller_with_mcp.sh |
The controller API on :9000, for the voice backend and Telegram bot |
bash modules/voice-assistant/backend/scripts/start_server.sh |
Voice backend (WebSocket push-to-talk) |
npm install && npm run dev in modules/voice-assistant/frontend |
Nuxt 4 frontend (Tailwind v4, @nuxt/ui) |
bash scripts/start_telegram_with_controller_mcp.sh |
Telegram bot wired to the controller |
bash scripts/start_alena_core_mcp.sh |
alena-core over stdio, for an external MCP client |
bash scripts/start_alena_dashboard.sh |
The improvement dashboard |
bash scripts/alena_improve.sh <command> |
The improvement CLI — cycle, scan, investigate, review, recommend, pending, decide, implement, agents, portfolio, tools, status, and more |
The voice backend imports modules/llm and modules/stt from the repo root,
so run it through its script or set PYTHONPATH to the repo root yourself.
GET /health on that backend reports whether text-whisperer is actually
reachable, which is the first thing to check when voice goes quiet.
First-time Google Calendar setup needs one interactive authorisation:
python modules/mcp/google-calendar/scripts/check_credentials.pyNothing else opens a consent screen — the MCP server is started by tool discovery on every ALENA process, including background jobs, so it never prompts.
Everything is read from the repo-root .env; see
.env.example for the full list.
Inference
| Variable | Default | Meaning |
|---|---|---|
LLM_BASE_URL |
http://localhost:1234 |
LM Studio |
LLM_MODEL |
(whatever is loaded) | Pin a model, or leave blank |
LLM_TIMEOUT |
120 |
Seconds |
LLM_DEBUG |
0 |
Log raw model replies |
ALENA_MAX_TOOL_STEPS |
3 |
Tool calls per turn; the last step is spent on an answer |
Gateway
| Variable | Default | Meaning |
|---|---|---|
ALENA_TOOL_POLICY |
config/tool_policy.yaml |
Who may call what |
ALENA_DB_PATH |
~/.alena/alena.db |
Audit log and state, outside the repo on purpose |
ALENA_ALLOWED_REPO_ROOTS |
(empty) | Roots a path argument must stay inside |
ALENA_AUDIT_ARGUMENTS |
0 |
Also store redacted arguments |
ALENA_GATEWAY_ENABLED |
1 |
0 bypasses every policy check. Escape hatch only |
Services — ALENA_CONTROLLER_URL, LLM_ROUTE (alena for tool-capable
answers, lmstudio for raw model output), TEXT_WHISPERER_URL /
TEXT_WHISPERER_TOKEN, TELEGRAM_BOT_TOKEN / TELEGRAM_TARGET_CHAT_ID /
TELEGRAM_CONTROLLER_ENABLED, CALENDAR_TIMEZONE.
Repositories the improvement system may look at are declared in
config/repositories.yaml. Capability
defaults are asymmetric on purpose: research and analysis default on, anything
that writes defaults off, and merge cannot become true by omission.
pytest -q881 tests, and they run with no .env, no ~/.alena, no LM Studio and no
Codex CLI — anything needing state builds it under tmp_path, and the tests
that need a live model sit behind RUN_INTEGRATION_TESTS=1. CI runs the same
command on push and pull request against Python 3.12 and 3.14.
Some of these tests are enforcement rather than description: a tool cannot ship without a policy entry, alena-core cannot gain a tool that writes, and the executor cannot detach from the gateway. Those are meant to fail the build.
| Document | What it covers |
|---|---|
| Tool Interoperability Standard | Why MCP owns the contract and the policy file owns permission |
| Repository & Agent Tool Architecture Addendum | The registry, grants, and what the gateway must answer |
| Autonomous Codebase Improvement System | The design behind modules/improve |
| Implementation plan | Phases and what is done |
| Research document contract | What a research file must contain to be ingested |
| text-whisperer contract | The transcription API ALENA expects |
- ✅ Local LLM inference with native tool calling
- ✅ Remote STT over the tailnet; web voice MVP; Telegram text and voice
- ✅ Tool Gateway — policy, approvals, audit log, pooled sessions
- ✅ MCP discovery across all three servers; the planner is fed from the catalog
- ✅ Autonomous improvement: scanning, research ingest, two reviewers, a human approval gate, an action agent that writes a branch — nothing is pushed — portfolio intelligence, launchd schedules and a Nuxt review dashboard
- 🚧 More MCP servers
- 🚧 Adaptive learning: currently stateless, with a knowledge base and LLM-generated MCP servers next
ALENA is a control plane for intelligence, not a chatbot. It reasons locally, acts through tools, and grows by adding capabilities — while the data stays where it started.