# Alo আলো — your local Claude Code, on your box with your models

Alo (আলো, "light") is a lightweight, project-based AI agent runtime: Python backend,
React/Vite web UI, and any OpenAI-compatible model. Ollama by default, so it runs
offline with no API key — bring OpenAI, Anthropic, Groq, OpenRouter, or a custom
endpoint when you want. It loops over LLM calls with tool use, lives inside one
workspace at a time, and ships skills, memory, cron, sub-agents, multi-workspace, and
an opt-in semantic layer that surfaces relevant notes and skills each turn.

[Quick start](#quick-start) · [How it fits together](#how-it-fits-together) · [Security](#security) · [Docs](#documentation) · [Development](#development) · [Features](#features)

---

## Quick start

```bash
pip install -e ".[all,dev]"   # editable, all providers + dev deps
alo setup                     # first-time setup (provider, model, API key)
alo init                      # scaffold .alo/ in the current project (registers it)
alo                           # interactive REPL
alo 'hello'                   # one-shot query
```

Bring it up as a server with a web UI, API, and cron scheduler:

```bash
cd web && npm install && npm run build   # build the SPA once
alo serve                                # http://0.0.0.0:18080
```

Production (systemd via `alo gateway` / `alo.sh`; user unit by default, `--system`
for system-wide with linger):

```bash
./alo.sh install            # install + enable the gateway unit
./alo.sh start              # start it
./alo.sh restart            # pick up code/config changes
./alo.sh status
./alo.sh logs               # tail the journal (live; Ctrl-C to stop)
./alo.sh stop
./alo.sh uninstall           # stop + disable + remove the unit
# system-wide: alo gateway install --system (needs sudo)
```

Curate which folders the server can target (the trust boundary for switching):

```bash
alo projects list             # registered workspaces
alo projects add <path> [-n NAME] [--init]
alo projects remove <path|name>
alo projects rename <path> <name>
```

### Semantic memory (opt-in)

Off by default → zero cost. Turn it on to passively surface relevant memory + skills
each turn without a tool call:

```bash
alo semantic on --backend ollama   # onnx | ollama | openai  (alo semantic show to inspect)
```

Backends: **onnx** local MiniLM via `fastembed` (`pip install fastembed`, ~90 MB),
**ollama** `nomic-embed-text` (reuse a pulled model), **openai** `text-embedding-3-small`.
The Settings → Semantic Memory card probes each backend's readiness (is `fastembed`
installed? is `nomic-embed-text` pulled? is `OPENAI_API_KEY` set?) with the exact fix
command — enabling still falls back to keyword search until a backend is ready.

## How it fits together

- **One agent, one loop.** `Agent.run(query)` builds a frozen system prompt once per
  session (identity + skills index + tools), sends history to the LLM, executes any
  tool calls, appends results, and loops until a final answer or `max_iterations`.
  `run_stream` mirrors it with `thinking` / `text` / `tool_call` / `tool_result` /
  `error` / `done` events.
- **Providers are interchangeable.** The OpenAI SDK is a universal adapter; every
  provider speaks the Chat Completions format, Anthropic included. A `fallback_providers`
  chain fails over on rate-limit / non-retryable errors, with per-provider cooldowns.
- **Tools self-register.** Each tool module calls `register(...)` at import time;
  `discover_tools()` imports them all. Availability is gated (`check_fn` / `requires_env`,
  30 s TTL) so the model never wastes an iteration calling something that can't run.
  Output is capped from the middle so a test runner's tail summary survives.
- **Skills are progressive disclosure.** A frozen one-line index sits in the system
  prompt; the model loads a matching skill's full `SKILL.md` via `skill_view` on demand.
  Drop a folder with `SKILL.md` into `.alo/skills/` — it loads.
- **Memory is tool-gated.** The live `memory_search` is a substring scan over
  `.alo/memory/*.md`. The semantic layer (above) is the passive, opt-in path.
- **Workspaces are the scope.** Alo walks up from cwd to find `.alo/`; every file op
  resolves inside it (path-traversal guarded). The agent injects its active workspace
  into every tool call, so file/shell/grep/memory follow the agent — not the process
  cwd — even under the server's concurrent requests. One server can drive many
  registered workspaces via the `X-Alo-Workspace` header.
- **Context compacts.** Past `compact_at_tokens`, older turns are summarized
  (cap → compact → stop after `compact_thrash_limit`); a 3-identical-call guard forces
  a summary; the active todo list is re-injected after compaction.
- **Sub-agents delegate.** `subagent` spawns a fresh `Agent` with restricted tools,
  sync or async (background daemon thread + REPL watcher that surfaces results).
- **Cron runs unattended.** APScheduler, two stores: per-workspace and global
  (`ALO_HOME/cron.json`, runs regardless of selection). Event-triggered runs wake an
  agent only when a sensor script produced output.

## Security

- **Workspace confinement.** All file ops resolve inside the active workspace via
  `resolve_path`; paths outside raise. The shell tool runs from the workspace root with
  a timeout and a configured `safety_level` (blocklist / allowlist / ask).
- **Trust boundary for switching.** Only paths in the global registry
  (`~/.alo/projects.json`) may become the active workspace. The web header resolver
  rejects unregistered paths with `400` — a web user can't point the agent at `/etc`.
- **Opt-in, global auth.** `alo user add` creates users in `ALO_HOME/users.json`; zero
  users → protected endpoints open, the moment any user exists → protected endpoints
  require a Bearer token, one login works across all workspaces.
- **Treat inbound and cron text as untrusted input.** Telegram messages, webhook
  payloads, and `no_agent` sensor-script output are attacker-controlled text reaching
  an agent with shell access. Telegram uses a chat whitelist; sensor runs wake an agent
  only on non-empty output. Sanitize and validate before acting on any of it.

## Documentation

| Goal | Start here |
|---|---|
| What's shipped | [`docs/FEATURES.md`](docs/FEATURES.md) |
| The agent loop, compaction, sub-agents | [`docs/agent-workflow.md`](docs/agent-workflow.md) |
| Authoring, validating, and fixing skills | [`docs/skills.md`](docs/skills.md) |
| Multi-workspace, registry, cron scopes | [`docs/workspace.md`](docs/workspace.md) |
| Architecture guidance for Claude Code | [`CLAUDE.md`](CLAUDE.md) |

## Development

```bash
pip install -e ".[all,dev]"
pytest tests/ -v                          # full suite
pytest tests/test_config.py::test_get_model -v   # single test
ruff check . && ruff format .             # lint + format
python -m alo.agent                       # module self-check
```

Serve for local dev:

```bash
alo serve                                 # default http://0.0.0.0:18080
cd web && npm run build                   # rebuild the SPA after frontend edits
```

## Features

- **Multi-provider** — Ollama (default), OpenAI, Anthropic, Groq, OpenRouter, custom
  endpoints; fallback chain with per-provider rate-limit cooldowns.
- **Markdown-driven identity** — `IDENTITY.md`, `SOUL.md`, `USER.md`, `MEMORY.md`,
  `PROJECT_CONTEXT.md` injected as a frozen system prompt; no JSON schemas.
- **Project context scan** — auto-generates `.alo/PROJECT_CONTEXT.md`: a runbook
  (run/test, npm/make/just/shell entries, CI, ports) + Code map + Database + Config
  audit. Refresh with `alo context`.
- **21+ tools** — file ops (paginated reads, fuzzy-edit fallback), grep, shell, git
  (read-only), pytest, memory, plan, sub-agent, cron, telegram, fetch.
- **Skills** — drop a folder with `SKILL.md`, it loads; progressive disclosure via
  `skill_view`; `alo doctor` validates and auto-repairs.
- **Semantic memory (opt-in)** — passive embedding-based retrieval of memory + skills
  each turn, cache-safe; CLI (`alo semantic`), `alo menu`, and Settings card with a
  backend-readiness probe.
- **Multi-workspace** — one server, many workspaces; registry-gated switching,
  per-request resolution, agent tools to add/switch.
- **Sessions & memory** — JSONL sessions with resume; durable Markdown memory with
  daily promotion into `.alo/MEMORY.md`.
- **Sub-agents** — depth-capped delegation, sync or async, restricted toolset,
  per-job status, REPL background-completion watcher.
- **Cron** — per-workspace + global stores, cron/interval/once schedules, `no_agent`
  scripts, pause/resume, history, event-triggered agent runs.
- **Auth & Telegram** — opt-in global auth; Telegram bot with chat whitelist and
  per-chat session continuity.
- **Operations** — `alo doctor` health check, `alo menu` interactive launcher,
  systemd deploy via `alo gateway` / `alo.sh`.
- **SDK** — `from alo import Agent; agent = Agent.from_workspace(path); agent.run("…")`.
- **Local-first** — Ollama by default, cloud providers optional.

## License

MIT