# EONYX memory: AGENTS.md, sessions and context compaction


Every turn starts with the model receiving context.
EONYX builds it from three parts.
**AGENTS.md** carries the project's standing instructions.
**Sessions** hold the full history on disk.
**Auto-summarization** compacts the older turns as the window fills.

<!--more-->

## 📌 AGENTS.md: project instructions

`AGENTS.md` holds the project's rules and context.
EONYX finds these files while building the system prompt.
It walks **up the directory tree** from the working directory and collects one at every level.

### Traversal rules

- The walk stops at the **project root**, the directory holding `eonyx.json`. Or at a directory with `.git`. Or at the filesystem root.
- Files attach from the outermost inwards. **The nearest one comes last**, so local rules refine the general ones.
- Each file enters the system prompt under `# Project context: <relative path>`.

Keep general rules at the repository root.
Keep specifics next to the code they describe.

```text
my-project/
├── AGENTS.md            ← stack, conventions, prohibitions
└── services/
    └── billing/
        └── AGENTS.md    ← specifics of a particular service
```

### What to write

```markdown
# Project: My App

## Stack
- Go 1.26, PostgreSQL via pgx.

## Rules
- Run `go test ./...` and `go vet ./...` before committing.
- Do not refactor what you were not asked to.

## Prohibited
- Do not edit .env directly.
- Never run migrations without confirmation.
```

{{< admonition type="note" title="Replacing the prompt entirely" >}}
The generated system prompt can be replaced wholesale.
Use the `system_prompt` field in `eonyx.json`, or the `--system-prompt` flag of `eonyx run`.
The flag beats the field, and the field beats generation.
In that case AGENTS.md files are not attached at all.
{{< /admonition >}}

## 💾 Conversation memory

### Sessions on disk

Every conversation is a **session**, fully persisted.
The format is JSONL: one message per line, tool calls and results included.

| File                     | Contents                                          |
| ------------------------ | ------------------------------------------------- |
| `sessions/<id>.jsonl`    | messages of a single session                      |
| `sessions.json`          | index: id, title, message counter, dates          |

The store is shared across projects and sits with the global data.
On Windows that is `%LOCALAPPDATA%\eonyx\`.
On Linux and macOS it is `$XDG_DATA_HOME/eonyx/`, which defaults to `~/.local/share/eonyx/`.
Session ids sort naturally: `20260705T091500-1a2b3c4d`.

Writing is the source of truth.
If a message cannot be saved, the turn aborts rather than running past the disk.
Empty sessions, the ones that never got past the system prompt, are cleaned up on their own.

### Working in the TUI

EONYX **resumes the most recent session** on startup.
From there:

| Action                | How                                                       |
| --------------------- | --------------------------------------------------------- |
| Session manager       | `Ctrl+S` or `/sessions`                                   |
| Search across sessions| `/sessions <query>` — over titles and message text        |
| New session           | `/new`                                                    |
| Clear the context     | `/clear` — wipes the history, keeping the id and title    |
| Rename                | `r` in the session list or `/rename <title>`              |
| Fork                  | `f` in the session list or `/fork` — a conversation branch|
| Delete                | `d` in the session list                                   |
| Export to markdown    | `/export` → `.eonyx/exports/eonyx-<id>.md`                |

New sessions get plain sequential titles: `1`, `2`, `3`.
They never change on their own — only an explicit rename touches them.

### Working from the console

```bash
eonyx sessions                  # list: id, date, message count, title
eonyx sessions --search parser  # search over titles and text
eonyx sessions delete <id>      # delete

eonyx run -c "continue"         # continue the most recent session
eonyx run -s <id> "and now…"    # continue a specific one
```

{{< admonition type="tip" title="A fork as a save point" >}}
`Fork` copies the message file into a new session.
The original stays untouched.
Fork before a risky direction, try it, and fall back if it did not work out.
{{< /admonition >}}

### Auto-summarization

Set `context_window` on a model and EONYX starts tracking the history size.
It estimates about 4 characters per token and shows `{ctx N%}` in the header.
At **80%** the indicator lights up, and summarization fires at the same threshold.

How it works:

- The last **3 user turns** stay verbatim. Everything older goes to the model to be compacted into a running summary: decisions, facts, changed files, open tasks.
- The summary is appended to the system prompt under `# Summary of earlier conversation`. The next compaction folds in the previous summary.
- The boundary runs **only along user messages**, so a tool call is never split from its result.
- All of this is **in memory only**. The JSONL on disk keeps the full history, so an export or a reopened session shows the whole conversation.

{{< admonition type="warning" title="Without context_window there is no summarization" >}}
The `context_window` field is the switch for this machinery.
Leave it out and the history grows until the API rejects it.
Put the model's real window size in `eonyx.json`.
{{< /admonition >}}

## 📋 Summary

{{< admonition type="abstract" title="Cheat sheet" >}}
- **AGENTS.md**
  - project rules; collected up the tree to the project root or `.git`
  - the closest file comes last
- **Sessions**
  - JSONL on disk, shared across all projects
  - resuming (`-c`/`-s`), search, fork (`f`), rename (`r`), export (`/export`)
  - titles are sequential `1`, `2`, `3`; they change only via an explicit `/rename`
- **Summarization**
  - at 80% of `context_window`: older turns are compacted into a summary in the system prompt
  - the disk keeps everything; watch `{ctx N%}` in the header
{{< /admonition >}}

