Content
# Aelios
> Give AI a cross-window long-term memory brain. Switch windows, switch clients, switch models, and the memory follows you.
This README is divided into two parts. **The upper half is for humans**: even an amoeba can understand it, and by following it, you can use it. **The lower half is for AI**: endpoints, MCP, pipeline details for Codex / Claude Code / Cursor maintenance and debugging.
- I'm a human who wants to deploy and use → see [Human Version](#human-version)
- I'm an AI assistant who wants to maintain and debug → see [AI Version](#ai-version-maintenance-and-debugging)
---
# Human Version
## One Sentence
Aelios is a memory service running on Cloudflare. After your AI client (Chatbox, Cherry Studio, web page, script) connects to it, AI can **remember forever** your preferences, rules, project background, and important things - not just remember this time, but also the next time, and the next time.
## What It Solves for You
- Every time you open a new window, AI forgets → it saves the chat, automatically organizes it into long-term memory, and recalls it automatically next time.
- Too much memory makes AI stupid → it coarse screens, then fine sorts, and compresses, finally only putting a few hundred words into the context.
- Changing clients and memory disappears → memory exists in your own Cloudflare, and changing clients only changes one address.
- Want to add memory to Claude Code / Codex → it can be used as an MCP tool, crossing devices and following you.
## Three Steps to Get Started
### 1. Deployment
1. Fork [wusaki0723/Aelios](https://github.com/wusaki0723/Aelios) to your GitHub.
2. Cloudflare Dashboard → Workers & Pages → Create application → Connect your GitHub → Select your fork.
3. Fill in the configuration:
- Project name: `companion-memory-proxy`
- Production branch: `main`
- Root directory: `/`
- **Build command:** `npm ci`
- **Deploy command:** `npm run deploy:cloudflare`
> ⚠️ It must be `npm run deploy:cloudflare`, **not** `npm run deploy`, **not** `wrangler deploy`. This command will automatically create a D1 database + Vectorize vector library + Queue. If you use the wrong command, the database will not be created.
### 2. Set a Key
After deployment, go to the Worker Settings → Variables and Secrets and add three:
| Variable Name | Type | What to Fill |
|---|---|---|
| `CLOUDFLARE_ACCOUNT_ID` | Variable | Your Cloudflare Account ID |
| `CLOUDFLARE_API_TOKEN` | Secret | Your Cloudflare API Token |
| `CHATBOX_API_KEY` | Secret | Create a password yourself, such as `sk-my-aelios` |
> Names with `KEY` / `TOKEN` must be selected as **Secret** (encrypted, not into git), not Variable. See [SECRETS.md](./SECRETS.md) for details.
After saving, redeploy. You will get an address: `https://companion-memory-proxy.<your-subdomain>.workers.dev`
### 3. Connect Client
Take Chatbox as an example:
- **Base URL:** `https://<your-worker-address>/v1`
- **API Key:** Your set `CHATBOX_API_KEY`
- **Model:** `companion`
Try saying: "Please remember: my test password is apple star-0428." After a while, ask: "What is my test password?" If it answers correctly, it's working.
## Management Panel (Recommended)
With the panel, **daily memory management doesn't require typing commands**. Open the browser:
```
https://<your-worker-address>/admin
```
Fill in the Worker URL and API Key, and you'll enter a visual interface with 5 tabs at the bottom:
| Tab | What It Does |
|---|---|
| **Today** | What was discussed today, L1 summary, yesterday's log, today's message, memory type statistics, all at a glance |
| **Audit Queue** | AI automatically extracts low-confidence memories every 4 hours and puts them here. You click **pass / discard / merge / replace**, preventing garbage memories from polluting the memory library |
| **Important Memories** | All long-term memories, sorted by type, browsed, searched, edited, and deleted |
| **More** | Precious memories (original text that only increases), black talk table (terminology aliases), world knowledge, maintenance tools |
| **Settings** | Theme, address, key |
**What you want AI to remember, forget, or change, all in the panel**. No need to adjust the API.
## Want a Complete Chat Gateway (Optional)
If you only want a memory library, skip this step. If you want Aelios to act as a chat forwarding gateway:
1. Cloudflare → AI → AI Gateway → Create a gateway, copy the address.
2. In AI Gateway's Provider Keys, add your model API key.
3. Go back to Worker → Variables and Secrets and add:
| Variable Name | What to Fill |
|---|---|
| `AI_GATEWAY_BASE_URL` | The Gateway Endpoint you just copied |
| `CF_AIG_TOKEN` | AI Gateway call token |
Save and redeploy.
> ⚠️ **Using OpenRouter to adjust Claude must go through 'custom providers' and add keys**, not using the official provider path. The official provider path sends requests in the Anthropic native format, conflicting with OpenRouter's OpenAI compatible format, causing cache invalidation and format confusion. In AI Gateway, select custom-providers and add OpenRouter key, reference: `https://dash.cloudflare.com/?to=/:account/ai/ai-gateway/custom-providers`.
## Add Memory to Claude Code / Codex (Optional)
Add the environment variable `MEMORY_MCP_API_KEY`, and then fill in the MCP configuration of the client:
```
URL: https://<your-worker-address>/mcp?token=<MEMORY_MCP_API_KEY>
```
Your official client will have cross-device memory.
## Image Mode (Optional)
Can't see images with pure text models? Add `GUIDE_DOG_API_KEY`, and change the client to:
- **Base URL:** `https://<your-worker-address>/v1/guide-dog`
- **API Key:** `GUIDE_DOG_API_KEY`
- **Model:** `companion`
The guide dog only interprets images, does not write memories, and does not store chats.
## ⚠️ Note Workers AI Free Quota Will Be Consumed by Memory Compression
The memory recall link has an **LLM compression model** (default `llama-3.3-70b`, running on Cloudflare Workers AI). Every chat round, as long as it recalls memory, it will compress the candidate memory - this is the part that consumes the most and uses the most tokens in the entire link.
Cloudflare Workers AI has a limited free quota, and **the compression model is the main consumer**. Once the quota is consumed, although reranker and embedding usage is very small, and basically won't be used up, they will be disconnected together because they also run on Workers AI, and the entire memory recall will be silent: unable to remember or recall.
If usage is small (tens of chats per day), it's okay, and the default configuration is sufficient. **For large usage, just replace the compression model**, point it to your AI Gateway for paid keys, and don't occupy the Workers AI free quota:
| Variable | Replace with |
|---|---|
| `MEMORY_FILTER_MODEL` | Go to AI Gateway for cheap LLM (such as `deepseek/deepseek-v4-flash`) |
Reranker and embedding usage is very small, staying on Workers AI is fine, no need to change. If you change `EMBEDDING_MODEL`, note that the dimension will change (old vectors are incompatible, need to rebuild Vectorize index, panel "More → Maintenance" has tools).
## Most Easily Stepped Pits
- The deployment command must be `npm run deploy:cloudflare`, others will overwrite variables and not build a library.
- Redeploying variables will not be lost (the command has `--keep-vars`).
- Vectorize index `memo-kb` (768-dimensional cosine) do not manually delete.
- Image viewing will switch to `VISION_MODEL`, pay attention to its price.
That's enough here, and the rest is left to AI.
---
# AI Version (Maintenance and Debugging)
> For Codex / Claude Code / Cursor / Gemini CLI. This section is endpoint, MCP, and memory pipeline precise description for maintenance and debugging.
## Project Positioning
Cloudflare Workers OpenAI-compatible Memory Proxy. Help users deploy **only associate their own fork**, Secrets / Variables are in their own Cloudflare account, not associated with wusaki0723/Aelios.
## Resource Agreement
| Resource | Value |
|---|---|
| Worker | `companion-memory-proxy` |
| D1 | `companion_memory_proxy` |
| Vectorize | `memo-kb` (768-dimensional cosine) |
| Queue | `companion-memory` |
| Embedding | `workers-ai/@cf/google/embeddinggemma-300m` |
| Dimensions | 768 (covering `EMBEDDING_MODEL` output dimension needs to match) |
Memory library defaults to **v2** (`MEMORY_LIFECYCLE_ENABLED` implicitly enabled): D1 is the body, and Vectorize is the mirror. Compatible / fallback switch defaults to hidden.
## Three Mode Boundaries
| Mode | Entrance | What to Do | What Not to Do |
|---|---|---|---|
| Full Version | `POST /v1/chat/completions` | Authentication, model routing, memory recall/inject, message storage D1, Queue maintenance, D1 cleanup, Claude cache | — |
| Pure Memory MCP | `/mcp` | Expose memory tools | Do not proxy chat |
| Guide Dog | `POST /v1/guide-dog/chat/completions` | Forwarding + image viewing | Do not write/read memory, do not store chat |
## Model Routing
```
model=companion → CHAT_MODEL
Request contains image → VISION_MODEL
anthropic/claude* → Anthropic native (/anthropic/v1/messages)
├─ Explicit cache_control anchor stable system prefix (persona_pinned / boot_stable / client_system stable section)
├─ Multi-breakpoint strategy: system anchor + tail anchor + long history bridge anchor, ≤4 markers
├─ dynamic_memory_patch moved to current user block, no cache_control, never destroy cache prefix
└─ rolling user cache default open, automatic cache default close
custom-provider/claude-* → Provider native (/custom-provider/messages)
other → OpenAI compat (/compat/chat/completions)
workers-ai/@cf/... → env.AI.run (do not go through AI Gateway)
```
**Cache Safety Points**: The recall patch (each round changes) is separated from the system blocks as a text block without `cache_control` appended to the end of the current user turn, located after all breakpoints. The recall patch of historical rounds has been solidified into stable history, falling before the tail breakpoint, and can be hit normally by the cache. `verify-cache-strategy.mjs` T14 checks that the number of breakpoints ≤4.
**OpenRouter + Claude Routing Constraints**: OpenRouter adjusting Claude must be in AI Gateway in **custom-provider** mode and add keys, not using the official provider path. The official path sends requests in the Anthropic native format, conflicting with OpenRouter's OpenAI compatible format, causing cache invalidation and format confusion. The model name goes to `custom-provider/claude-*` → Provider native branch.
**Workers AI Quota Risk**: The recall link's compression model ( `MEMORY_FILTER_MODEL`, default `llama-3.3-70b`), reranker (`MEMORY_RERANKER_MODEL`), and embedding (`EMBEDDING_MODEL`) default run on Workers AI, sharing the same free quota. **The compression model is called every recall, is the main consumer**; reranker and embedding usage is very small, basically won't be used up, but the quota is consumed by the compression model and will be disconnected together, causing no memory or recall. For large usage, just change `MEMORY_FILTER_MODEL` to AI Gateway for paid keys; reranker / embedding stay on Workers AI. If changing `EMBEDDING_MODEL`, note that the dimension will change (old vectors are incompatible, need to rebuild Vectorize index).
## REST Endpoints
| Method | Path | Description |
|---|---|---|
| GET | `/health` | Health check |
| GET | `/admin` `/memory-admin` | Management panel (HTML) |
| GET | `/v1/models` | Model list |
| POST | `/v1/chat/completions` | Chat gateway (full version) |
| POST | `/v1/guide-dog/chat/completions` | Guide dog (no memory) |
| GET / POST | `/mcp` `/memory-mcp` | MCP endpoint |
| GET / POST | `/v1/memories` `/v1/memory` | Memory list / new (v2 must bring `fact_key`, go upsert) |
| GET / PATCH / DELETE | `/v1/memories/:id` `/v1/memory/:id` | Single memory operation |
| POST | `/v1/search/memories` | Memory search (recall use, can be compressed and processed; `filter:false` skip reranker, `include_prompt:true` take injectable text) |
| POST | `/v1/ingest/messages` `/v1/messages/ingest` | Write raw chat (v2 only falls raw, no old extraction) |
| GET / PATCH | `/v1/memory_boot` | Cold boot package: digest + yesterday's log + precious + glossary + longtail + today's message + statistics; PATCH write L1 summary |
| GET / POST / DELETE | `/v1/precious` `/v1/precious/:id` | Precious memory (only increase no decrease original text) |
| GET / POST / PATCH / DELETE | `/v1/glossary` `/v1/glossary/:id` | Glossary (term + aliases + definition) |
| GET | `/v1/candidates` | Candidate review queue list (`status` default pending) |
| POST | `/v1/candidates/:id/approve` | Pass candidate → library |
| POST | `/v1/candidates/:id/discard` | Discard candidate |
| POST | `/v1/candidates/:id/merge` | Merge into existing memory (`target_id`) |
| POST | `/v1/candidates/:id/supersede` | Replace existing memory (`target_id`) |
| GET / PUT / DELETE | `/v1/cache/:namespace/:key` | Cache CRUD |
| GET | `/api/memories/export` | Memory export |
| GET | `/v1/debug/cache_health` | Cache health |
| GET | `/v1/debug/vector_health` | Vector library health |
| POST | `/v1/debug/vector_reindex` | Vector rebuild |
All non-`/health` `/admin` `/v1/models` endpoints require `Authorization: Bearer <CHATBOX_API_KEY>`, and are authorized by scope (`memory:read` / `memory:write`).
## MCP Tools (`/mcp`)
v2 exposes 15 tools. `memory_create` has been deprecated, and calling will report an error requiring replacement with `memory_upsert`.
| Tool | Effect | Key Parameters / Remarks |
|---|---|---|
| `memory_search` | Vector search long-term memory | `query`; `min_score` (0–1, default 0.15) |
| `memory_list` | List memory | `type` / `status` / `limit` / `cursor` |
| `memory_export` | Export memory | Return all |
| `memory_get` | Get single | `id` |
| `memory_delete` | Soft delete | `id` |
| `memory_ingest` | Write message + trigger maintenance | v2 only falls raw |
| `memory_boot` | Pull cold boot package | digest + log + precious + glossary + longtail |
| `memory_recall` | Recall and return injectable text | Used for MCP client to piece together context |
| `memory_pin` | Write precious memory | Only increase no decrease |
| `glossary_set` | Write glossary term | term / aliases / definition |
| `memory_upsert` | v2 main write (need `fact_key`) | Collision → supersede / mark-seen |
| `memory_supersede` | Explicit replacement | `old_id` + new content |
| `memory_archive` | Archive | `id` |
| `digest_get` | Read L1 summary | — |
| `digest_set` | Write L1 summary | `content` (truncate 500 words) |
## Memory Pipeline
**Injection (before chat):**
```
Get the last user message → embedding → Vectorize search top K
→ Score floor filtering noise → deduplicate → reranker reordering → compression model simplification
→ Append as dynamic_memory_patch to current user turn (no cache_control)
```
**Extraction (every 4 hours cron `0 */4 * * *`):**
```
Read D1 messages in 4h window (initially without cursor, only process current window, not from 1970)
→ EXTRACT_MODEL extract stable facts with fact_key
→ confidence < EXTRACT_REVIEW_CONFIDENCE(0.76) into candidate review queue
→ With fact_key: collide with key and embedding cosine ≥ DEDUP_COSINE(0.9) → mark-seen; otherwise supersede
→ Without fact_key: judge duplicate by same type Vectorize cosine, hit → mark-seen; otherwise create
→ Cursor advance; window extraction complete, write done mark; model error, not advance, retry next cron
```
**Dreaming (daily at 04:10 local time zone cron `10 20 * * *`):**
```
Scheduled dream:
├─ No longer extract initially (already handled by 4h extractor), memories_to_add defaults to empty
├─ Merge duplicates, replace outdated, update conflicts (memories_to_update / memories_to_delete)
├─ Keep important original excerpts
└─ Rewrite L1 summary (truncate to 500 characters) + yesterday's log
```
**Cleanup (background Queue, 24h throttling):**
```
messages: delete in 14 days
usage_logs: delete in 30 days
memory_events: delete in 30 days
idempotency_keys: delete in 7 days
memories: non-pinned/identity/persona expire in 180 days → synchronous delete Vectorize
hard delete: deleted/superseded/expired over 30 days → delete Vectorize first, then D1
(Vectorize failure, do not delete D1 to avoid mismatch)
```
## Environment Variables Quick Reference
### Minimum Required
| Variable | Description |
|---|---|
| `CLOUDFLARE_ACCOUNT_ID` | Cloudflare Account ID |
| `CLOUDFLARE_API_TOKEN` | Cloudflare API Token (Secret) |
| `CHATBOX_API_KEY` | Client + panel access key (Secret) |
### Complete Gateway
| Variable | Description |
|---|---|
| `AI_GATEWAY_BASE_URL` | AI Gateway Endpoint |
| `CF_AIG_TOKEN` | AI Gateway invocation token |
### Models (all have defaults)
| Variable | Default | Description |
|---|---|---|
| `CHAT_MODEL` | `deepseek/deepseek-v4-pro` | Main chat |
| `EXTRACT_MODEL` | `deepseek/deepseek-v4-flash` | 4h small batch extraction |
| `DREAM_MODEL` | `deepseek/deepseek-v4-pro` | Nightly dreaming |
| `VISION_MODEL` | `google-ai-studio/gemini-3-flash-preview` | Image viewing |
| `EMBEDDING_MODEL` | `workers-ai/@cf/google/embeddinggemma-300m` | Embedding |
| `EMBEDDING_DIMENSIONS` | `768` | Non-Workers AI embedding target dimension |
| `MEMORY_RERANKER_MODEL` | `workers-ai/@cf/baai/bge-reranker-base` | Reranker |
| `ENABLE_MEMORY_RERANKER` | `true` | `false` to skip |
| `MEMORY_FILTER_MODEL` | `workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast` | Compression |
### Memory Extraction / Dreaming
| Variable | Default | Description |
|---|---|---|
| `EXTRACT_MAX_MESSAGES` | `40` | Maximum messages processed per window |
| `EXTRACT_MAX_RUNS` | `4` | Maximum continuous batches per cron |
| `EXTRACT_MAX_TOKENS` | `1200` | Extraction model output limit |
| `EXTRACT_REVIEW_CONFIDENCE` | `0.76` | Below this value into candidate queue |
| `DEDUP_COSINE` | `0.9` | Embedding deduplication threshold |
| `MEMORY_FILTER_MAX_CANDIDATES` | `12` | Maximum candidates into reranker |
| `MEMORY_FILTER_MAX_OUTPUT` | `5` | Injection count |
| `MEMORY_FILTER_OUTPUT_CHARS` | `300` | Compressed character count per entry |
| `MEMORY_FILTER_MAX_TOKENS` | `1400` | Compression model JSON limit |
| `MEMORY_FILTER_MIN_SCORE` | `0.1` | Reranker floor (intentionally low) |
| `MEMORY_MIN_IMPORTANCE` | `0.55` | Minimum importance for writing |
| `DREAM_TIME_ZONE` | `Asia/Singapore` | Natural day cut by this timezone |
| `DREAM_MAX_MESSAGES` | `40` | Maximum messages per dream |
| `DREAM_MAX_RUNS` | `10` | Maximum dream batches per cron |
| `DREAM_MAX_TOKENS` | `8000` | Dream output limit |
| `DREAM_MEMORY_CONTEXT_LIMIT` | `40` | Dream reference old memory count |
| `DREAM_EXCERPT_LIMIT` | `8` | Maximum original excerpt paragraphs per day |
### Claude Cache
| Variable | Default | Description |
|---|---|---|
| `ANTHROPIC_CACHE_ENABLED` | `true` | Prompt cache switch |
| `ANTHROPIC_CACHE_TTL` | `5m` | `5m` / `1h` |
| `ANTHROPIC_AUTO_CACHE_ENABLED` | `true` | Top-level automatic cache |
| `ANTHROPIC_ROLLING_CACHE_ENABLED` | `true` | Rolling checkpoint |
| `ANTHROPIC_ROLLING_CACHE_WINDOW_SIZE` | `20` | Historical window |
| `ANTHROPIC_CACHE_USER_ID` | Empty | Multi-client cache isolation `metadata.user_id` |
| `ANTHROPIC_THINKING_ENABLED` | `false` | Deep thinking |
| `ANTHROPIC_THINKING_BUDGET` | `1024` | Thinking token (1024–32000) |
| `FORCE_ANTHROPIC_NATIVE` | Empty | `true` force Anthropic native |
| `CUSTOM_ANTHROPIC_MESSAGES_PATH` | `messages` | Native messages path |
### Advanced
| Variable | Default | Description |
|---|---|---|
| `MEMORY_TOP_K` | `50` | Vector coarse recall count |
| `MEMORY_MIN_SCORE` | `0.1` | Recall floor (intentionally low, precision to reranker) |
| `MEMORY_FILTER_MAX_CONTENT_CHARS` | `700` | Candidate retained character count |
| `VECTORIZE_INDEX_NAME` | `memo-kb` | Vectorize index name |
| `ENABLE_AUTO_MEMORY` | Empty (enabled) | `false` disable automatic memory |
| `EMPTY_MEMORY_MIN_CHARS` | `4` | Clear short empty memory threshold |
| `PUBLIC_MODEL_NAME` | `companion` | Client-visible model name |
| `IM_API_KEY` | Empty | Second key (IM bot) |
| `DEBUG_API_KEY` | Empty | Debug interface key |
| `MEMORY_MCP_API_KEY` | Empty | Pure memory MCP separate key |
| `GUIDE_DOG_API_KEY` | Empty | Guide dog separate key |
## Local Development and Verification
```bash
npm install
npm run deploy:cloudflare # Build database + upgrade + deploy
npm run worker:test # Main test suite (177 items)
node scripts/verify-extract-pipeline.mjs # 4h extraction pipeline behavior test
node scripts/verify-cache-strategy.mjs # Claude cache breakpoint strategy (15 items)
npx tsc --noEmit # Type checking
```
After modifying memory / cache / extraction related code, run at least these three scripts.
## Memory Library Cleaning
When the old Vectorize has long blocks, multi-theme blocks, or repeated summaries, let LLM generate a cleaning plan:
```bash
AELIOS_BASE_URL="https://<worker>" \
AELIOS_API_KEY="<CHATBOX_API_KEY>" \
AI_GATEWAY_BASE_URL="<AI Gateway Endpoint>" \
CF_AIG_TOKEN="<AI Gateway Token>" \
CLEANUP_MODEL="deepseek/deepseek-v4-flash" \
npm run vectorize:clean:llm
```
## License
MIT
## Communication and Feedback
If you have questions or want to communicate, welcome to QQ group: **1091783659**
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available