palinode 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- palinode-0.7.0/PKG-INFO +309 -0
- palinode-0.7.0/README.md +270 -0
- palinode-0.7.0/palinode/__init__.py +14 -0
- palinode-0.7.0/palinode/api/server.py +1064 -0
- palinode-0.7.0/palinode/cli/__init__.py +188 -0
- palinode-0.7.0/palinode/cli/_api.py +138 -0
- palinode-0.7.0/palinode/cli/_format.py +41 -0
- palinode-0.7.0/palinode/cli/consolidate.py +29 -0
- palinode-0.7.0/palinode/cli/diff.py +29 -0
- palinode-0.7.0/palinode/cli/doctor.py +61 -0
- palinode-0.7.0/palinode/cli/git.py +46 -0
- palinode-0.7.0/palinode/cli/ingest.py +55 -0
- palinode-0.7.0/palinode/cli/lint.py +67 -0
- palinode-0.7.0/palinode/cli/list.py +60 -0
- palinode-0.7.0/palinode/cli/manage.py +43 -0
- palinode-0.7.0/palinode/cli/prompt.py +126 -0
- palinode-0.7.0/palinode/cli/query.py +14 -0
- palinode-0.7.0/palinode/cli/read.py +107 -0
- palinode-0.7.0/palinode/cli/save.py +62 -0
- palinode-0.7.0/palinode/cli/search.py +60 -0
- palinode-0.7.0/palinode/cli/session_end.py +99 -0
- palinode-0.7.0/palinode/cli/status.py +38 -0
- palinode-0.7.0/palinode/cli/trigger.py +79 -0
- palinode-0.7.0/palinode/consolidation/__init__.py +9 -0
- palinode-0.7.0/palinode/consolidation/cron.py +56 -0
- palinode-0.7.0/palinode/consolidation/executor.py +246 -0
- palinode-0.7.0/palinode/consolidation/fact_ids.py +81 -0
- palinode-0.7.0/palinode/consolidation/layer_split.py +221 -0
- palinode-0.7.0/palinode/consolidation/runner.py +610 -0
- palinode-0.7.0/palinode/consolidation/write_time.py +472 -0
- palinode-0.7.0/palinode/core/__init__.py +1 -0
- palinode-0.7.0/palinode/core/config.py +402 -0
- palinode-0.7.0/palinode/core/embedder.py +128 -0
- palinode-0.7.0/palinode/core/git_tools.py +334 -0
- palinode-0.7.0/palinode/core/hashing.py +10 -0
- palinode-0.7.0/palinode/core/lint.py +131 -0
- palinode-0.7.0/palinode/core/parser.py +89 -0
- palinode-0.7.0/palinode/core/store.py +1133 -0
- palinode-0.7.0/palinode/indexer/watcher.py +262 -0
- palinode-0.7.0/palinode/ingest/__init__.py +1 -0
- palinode-0.7.0/palinode/ingest/pipeline.py +334 -0
- palinode-0.7.0/palinode/mcp.py +933 -0
- palinode-0.7.0/palinode/py.typed +1 -0
- palinode-0.7.0/palinode.egg-info/PKG-INFO +309 -0
- palinode-0.7.0/palinode.egg-info/SOURCES.txt +63 -0
- palinode-0.7.0/palinode.egg-info/dependency_links.txt +1 -0
- palinode-0.7.0/palinode.egg-info/entry_points.txt +7 -0
- palinode-0.7.0/palinode.egg-info/requires.txt +15 -0
- palinode-0.7.0/palinode.egg-info/top_level.txt +1 -0
- palinode-0.7.0/pyproject.toml +64 -0
- palinode-0.7.0/setup.cfg +4 -0
- palinode-0.7.0/tests/test_cli_start.py +45 -0
- palinode-0.7.0/tests/test_context.py +259 -0
- palinode-0.7.0/tests/test_executor.py +166 -0
- palinode-0.7.0/tests/test_fact_ids.py +44 -0
- palinode-0.7.0/tests/test_fallback.py +121 -0
- palinode-0.7.0/tests/test_freshness.py +76 -0
- palinode-0.7.0/tests/test_git_tools.py +75 -0
- palinode-0.7.0/tests/test_lint.py +56 -0
- palinode-0.7.0/tests/test_list_read.py +111 -0
- palinode-0.7.0/tests/test_parser.py +45 -0
- palinode-0.7.0/tests/test_prompts.py +245 -0
- palinode-0.7.0/tests/test_source_surface.py +93 -0
- palinode-0.7.0/tests/test_store.py +48 -0
- palinode-0.7.0/tests/test_write_time.py +315 -0
palinode-0.7.0/PKG-INFO
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: palinode
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: The memory substrate for AI agents and developer tools. Git-versioned, file-native, MCP-first.
|
|
5
|
+
Author-email: Paul Kyle <paul@phasespace.co>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/phasespace-labs/palinode
|
|
8
|
+
Project-URL: Documentation, https://github.com/phasespace-labs/palinode/tree/main/docs
|
|
9
|
+
Project-URL: Repository, https://github.com/phasespace-labs/palinode
|
|
10
|
+
Project-URL: Issues, https://github.com/phasespace-labs/palinode/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/phasespace-labs/palinode/blob/main/docs/CHANGELOG.md
|
|
12
|
+
Keywords: ai,agents,memory,mcp,llm,rag,knowledge-management
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
20
|
+
Classifier: Operating System :: OS Independent
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Requires-Dist: sqlite-vec
|
|
26
|
+
Requires-Dist: watchdog
|
|
27
|
+
Requires-Dist: pyyaml
|
|
28
|
+
Requires-Dist: httpx
|
|
29
|
+
Requires-Dist: python-frontmatter
|
|
30
|
+
Requires-Dist: fastapi
|
|
31
|
+
Requires-Dist: uvicorn
|
|
32
|
+
Requires-Dist: pydantic
|
|
33
|
+
Requires-Dist: mcp
|
|
34
|
+
Requires-Dist: rich
|
|
35
|
+
Requires-Dist: click
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: pytest; extra == "dev"
|
|
38
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
39
|
+
|
|
40
|
+
# Palinode
|
|
41
|
+
|
|
42
|
+
**Persistent memory for AI agents. Markdown in, markdown out, git everything.**
|
|
43
|
+
|
|
44
|
+
Your agent's memory is a folder of markdown files. Palinode indexes them with hybrid search, compacts them with an LLM, and exposes them through any interface you want — MCP, REST API, CLI, or plugin hooks. If every service crashes, `cat` still works.
|
|
45
|
+
|
|
46
|
+
*A palinode is a poem that retracts what was said before and says it better. That's what memory compaction does.*
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## The Idea
|
|
51
|
+
|
|
52
|
+
Most agent memory is a black box. You can't read it, you can't diff it, you can't `grep` it when the vector DB is down. Palinode bets on **plain files as the source of truth** and builds everything else as a derived index.
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
Files (markdown + YAML frontmatter)
|
|
56
|
+
↓ watched
|
|
57
|
+
Index (SQLite-vec vectors + FTS5 keywords, single .db file)
|
|
58
|
+
↓ queried by
|
|
59
|
+
Interfaces (MCP server, REST API, CLI, plugin hooks)
|
|
60
|
+
↓ compacted by
|
|
61
|
+
LLM (proposes ops → deterministic executor applies them → git commits)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
That's the whole architecture. One directory of `.md` files, one SQLite database, one API server. No Postgres, no Redis, no cloud dependency.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## One Backend, Every Interface
|
|
69
|
+
|
|
70
|
+
Palinode doesn't care how you talk to it. The same 17 tools work everywhere:
|
|
71
|
+
|
|
72
|
+
| Interface | Transport | Best For |
|
|
73
|
+
|-----------|-----------|----------|
|
|
74
|
+
| **MCP Server** | Streamable HTTP or stdio | Claude Code, Claude Desktop, Cursor, Zed |
|
|
75
|
+
| **REST API** | HTTP on :6340 | Scripts, webhooks, custom integrations |
|
|
76
|
+
| **CLI** | Wraps REST API | Cron jobs, SSH, shell scripts (8x fewer tokens than MCP) |
|
|
77
|
+
| **Plugin** | Agent lifecycle hooks | Agent frameworks with inject/extract patterns |
|
|
78
|
+
|
|
79
|
+
Set up once on a server. Connect from any machine, any IDE, any agent framework. The MCP server is a pure HTTP client — it holds no state, no database connection, no embedder. Point it at the API and go.
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"mcpServers": {
|
|
84
|
+
"palinode": { "url": "http://your-server:6341/mcp/" }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
That's the entire client config for Claude Code, Claude Desktop, Cursor, or Zed. See [docs/MCP-SETUP.md](docs/MCP-SETUP.md) for multi-IDE setup, or [docs/INSTALL-CLAUDE-CODE.md](docs/INSTALL-CLAUDE-CODE.md) for Claude Code specifically.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## How It Works
|
|
94
|
+
|
|
95
|
+
**Store** — Typed markdown files (people, projects, decisions, insights) with YAML frontmatter. Git-versioned. Human-readable. Editable in Obsidian, VS Code, vim, or anything.
|
|
96
|
+
|
|
97
|
+
**Index** — A file watcher embeds with BGE-M3 and indexes with FTS5 as you save. Content-hash dedup skips re-embedding unchanged files (~90% savings). Single SQLite file, zero external services.
|
|
98
|
+
|
|
99
|
+
**Search** — Hybrid BM25 + vector search merged with Reciprocal Rank Fusion. Keyword precision when you need exact terms, semantic recall when you don't. Optional associative entity graph and prospective triggers.
|
|
100
|
+
|
|
101
|
+
**Compact** — Weekly consolidation where an LLM proposes structured operations (KEEP / UPDATE / MERGE / SUPERSEDE / ARCHIVE) and a deterministic executor applies them. The LLM never touches your files directly. Every compaction is a git commit you can review, blame, or revert.
|
|
102
|
+
|
|
103
|
+
**Audit** — `git blame` any fact. `git diff` any change. `rollback` any mistake. These aren't just git-compatible files — `palinode_diff`, `palinode_blame`, and `palinode_rollback` are first-class tools your agent can call.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Quick Start
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Install
|
|
111
|
+
git clone https://github.com/phasespace-labs/palinode && cd palinode
|
|
112
|
+
python3 -m venv venv && source venv/bin/activate
|
|
113
|
+
pip install -e .
|
|
114
|
+
|
|
115
|
+
# Create your memory directory
|
|
116
|
+
mkdir -p ~/.palinode/{people,projects,decisions,insights,daily}
|
|
117
|
+
cd ~/.palinode && git init
|
|
118
|
+
cp /path/to/palinode/palinode.config.yaml.example palinode.config.yaml # adjust path
|
|
119
|
+
|
|
120
|
+
# Start services
|
|
121
|
+
PALINODE_DIR=~/.palinode palinode-api # REST API on :6340
|
|
122
|
+
PALINODE_DIR=~/.palinode palinode-watcher # auto-indexes on file save
|
|
123
|
+
PALINODE_DIR=~/.palinode palinode-mcp-sse # MCP server on :6341 (optional)
|
|
124
|
+
|
|
125
|
+
# Verify
|
|
126
|
+
curl http://localhost:6340/status
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
> Your memory directory is **private**. It contains personal data. Never make it public. The code repo contains zero memory files.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Tools
|
|
134
|
+
|
|
135
|
+
17 tools available through every interface:
|
|
136
|
+
|
|
137
|
+
| Tool | What It Does |
|
|
138
|
+
|------|-------------|
|
|
139
|
+
| `search` | Hybrid BM25 + vector search with category filter |
|
|
140
|
+
| `save` | Store a typed memory (person, decision, insight, project) |
|
|
141
|
+
| `list` | Browse memory files by type, filter by core status |
|
|
142
|
+
| `read` | Read the full content of a memory file |
|
|
143
|
+
| `ingest` | Fetch a URL and save as research |
|
|
144
|
+
| `status` | Health check — file counts, index stats, service status |
|
|
145
|
+
| `history` | Git history with diff stats, rename tracking, and limit |
|
|
146
|
+
| `entities` | Entity graph — cross-references between memories |
|
|
147
|
+
| `consolidate` | Preview or run LLM-powered compaction |
|
|
148
|
+
| `diff` | What changed in the last N days |
|
|
149
|
+
| `blame` | Trace a fact back to the commit that recorded it |
|
|
150
|
+
| `rollback` | Revert a file to a previous commit (safe, creates new commit) |
|
|
151
|
+
| `push` | Sync memory to a remote git repo |
|
|
152
|
+
| `trigger` | Prospective recall — auto-inject when a topic comes up |
|
|
153
|
+
| `lint` | Health scan — orphans, stale files, missing fields |
|
|
154
|
+
| `session_end` | Capture summary, decisions, and blockers at end of session |
|
|
155
|
+
| `prompt` | List, show, or activate versioned LLM prompts |
|
|
156
|
+
|
|
157
|
+
Every tool is accessible as `palinode_<name>` via MCP, `palinode <name>` via CLI, or `POST/GET /<name>` via the REST API.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Stack
|
|
162
|
+
|
|
163
|
+
| Layer | Choice | Why |
|
|
164
|
+
|-------|--------|-----|
|
|
165
|
+
| Source of truth | Markdown + YAML frontmatter | Human-readable, git-versioned, portable |
|
|
166
|
+
| Vector index | SQLite-vec (embedded) | No server, single file, zero config |
|
|
167
|
+
| Keyword index | SQLite FTS5 (embedded) | BM25 for exact terms, zero dependencies |
|
|
168
|
+
| Embeddings | BGE-M3 via Ollama | Local, private, no API key needed |
|
|
169
|
+
| API | FastAPI | Lightweight, async, one process |
|
|
170
|
+
| MCP | Python MCP SDK (Streamable HTTP) | Works with every IDE over the network |
|
|
171
|
+
| CLI | Click (wraps REST API) | Shell-native, TTY-aware output |
|
|
172
|
+
| Behavior | [`PROGRAM.md`](PROGRAM.md) | What to remember, how to extract, how to compact — edit one file to change all behavior |
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Memory File Format
|
|
177
|
+
|
|
178
|
+
```yaml
|
|
179
|
+
---
|
|
180
|
+
id: project-palinode
|
|
181
|
+
category: project
|
|
182
|
+
name: Palinode
|
|
183
|
+
core: true
|
|
184
|
+
status: active
|
|
185
|
+
entities: [person/paul]
|
|
186
|
+
last_updated: 2026-04-05T00:00:00Z
|
|
187
|
+
summary: "Persistent memory for AI agents."
|
|
188
|
+
---
|
|
189
|
+
# Palinode
|
|
190
|
+
|
|
191
|
+
Your content here. As detailed or brief as you want.
|
|
192
|
+
Files marked `core: true` are always in context.
|
|
193
|
+
Everything else is retrieved on demand via hybrid search.
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Open your memory directory as an [Obsidian](https://obsidian.md) vault for visual browsing. See [docs/OBSIDIAN-SETUP.md](docs/OBSIDIAN-SETUP.md).
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Configuration
|
|
201
|
+
|
|
202
|
+
All behavior is in `palinode.config.yaml`:
|
|
203
|
+
|
|
204
|
+
```yaml
|
|
205
|
+
memory_dir: "~/.palinode"
|
|
206
|
+
ollama_url: "http://localhost:11434"
|
|
207
|
+
embedding_model: "bge-m3"
|
|
208
|
+
|
|
209
|
+
recall:
|
|
210
|
+
search:
|
|
211
|
+
top_k: 5
|
|
212
|
+
threshold: 0.4
|
|
213
|
+
core:
|
|
214
|
+
max_chars_per_file: 3000
|
|
215
|
+
|
|
216
|
+
search:
|
|
217
|
+
hybrid_enabled: true
|
|
218
|
+
hybrid_weight: 0.5 # 0.0 = vector only, 1.0 = BM25 only
|
|
219
|
+
|
|
220
|
+
consolidation:
|
|
221
|
+
llm_model: "llama3.1:8b" # any chat model that outputs JSON
|
|
222
|
+
llm_url: "http://localhost:11434"
|
|
223
|
+
llm_fallbacks: # tried in order if primary fails
|
|
224
|
+
- model: "qwen2.5:14b-instruct"
|
|
225
|
+
url: "http://localhost:11434"
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
All models are swappable. Any Ollama embedding model, any OpenAI-compatible chat endpoint. See [palinode.config.yaml.example](palinode.config.yaml.example) for the full reference.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Requirements
|
|
233
|
+
|
|
234
|
+
- **Python 3.11+**
|
|
235
|
+
- **Ollama** with `bge-m3` (`ollama pull bge-m3`)
|
|
236
|
+
- **Git**
|
|
237
|
+
|
|
238
|
+
Optional: a chat model for consolidation (any 7B+ works).
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## API Reference
|
|
243
|
+
|
|
244
|
+
| Method | Path | Description |
|
|
245
|
+
|--------|------|-------------|
|
|
246
|
+
| `GET` | `/status` | Health check + stats |
|
|
247
|
+
| `POST` | `/search` | Hybrid search with filters |
|
|
248
|
+
| `POST` | `/search-associative` | Entity graph traversal |
|
|
249
|
+
| `POST` | `/save` | Create a typed memory file |
|
|
250
|
+
| `POST` | `/ingest-url` | Fetch URL, save as research |
|
|
251
|
+
| `GET/POST` | `/triggers` | Prospective recall triggers |
|
|
252
|
+
| `POST` | `/consolidate` | Run or preview compaction |
|
|
253
|
+
| `GET` | `/list` | Browse files by type |
|
|
254
|
+
| `GET` | `/read?file_path=...` | Read a memory file |
|
|
255
|
+
| `GET` | `/history/{file_path}` | Git log for a file |
|
|
256
|
+
| `GET` | `/diff` | Recent changes |
|
|
257
|
+
| `GET` | `/blame/{file_path}` | Git blame |
|
|
258
|
+
| `POST` | `/rollback` | Revert a file |
|
|
259
|
+
| `POST` | `/push` | Push to git remote |
|
|
260
|
+
| `POST` | `/reindex` | Rebuild indices |
|
|
261
|
+
| `POST` | `/session-end` | Capture session summary |
|
|
262
|
+
| `POST` | `/lint` | Health scan |
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Design Principles
|
|
267
|
+
|
|
268
|
+
1. **Files are truth.** Not databases, not vector stores. Markdown files that humans can read, edit, and version with git.
|
|
269
|
+
|
|
270
|
+
2. **Typed, not flat.** People, projects, decisions, insights — each has structure. This enables reliable retrieval and consolidation.
|
|
271
|
+
|
|
272
|
+
3. **Consolidation, not accumulation.** 100 sessions should produce 20 well-maintained files, not 100 unread dumps.
|
|
273
|
+
|
|
274
|
+
4. **Invisible when working.** The human talks to their agent. Palinode works behind the scenes.
|
|
275
|
+
|
|
276
|
+
5. **Graceful degradation.** Vector index down? Read files directly. Embedding service down? Grep. Machine off? It's a git repo, clone it anywhere.
|
|
277
|
+
|
|
278
|
+
6. **Zero taxonomy burden.** The system classifies. The human reviews. If the human has to maintain a taxonomy, the system dies.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## What's Unique
|
|
283
|
+
|
|
284
|
+
- **Git operations as agent tools** — `diff`, `blame`, `rollback`, `push` exposed via MCP. No other system makes git ops callable by the agent.
|
|
285
|
+
- **Operation-based compaction** — KEEP/UPDATE/MERGE/SUPERSEDE/ARCHIVE DSL. LLM proposes, deterministic executor disposes. Every compaction is a reviewable git commit.
|
|
286
|
+
- **Per-fact addressability** — `<!-- fact:slug -->` IDs inline in markdown, invisible in rendering, preserved by git, targetable by compaction.
|
|
287
|
+
- **4-phase injection** — Core (always) + Topic (per-turn search) + Associative (entity graph) + Triggered (prospective recall).
|
|
288
|
+
- **Multi-transport MCP** — stdio for local, Streamable HTTP for remote. One server, any IDE on any machine.
|
|
289
|
+
- **If everything crashes, `cat` still works.**
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Acknowledgments
|
|
294
|
+
|
|
295
|
+
Palinode builds on ideas from [Karpathy's LLM Knowledge Bases](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f), [Letta](https://github.com/letta-ai/letta) (tiered memory), and [LangMem](https://github.com/langchain-ai/langmem) (typed schemas + background consolidation). See [docs/ACKNOWLEDGMENTS.md](docs/ACKNOWLEDGMENTS.md) for the full list.
|
|
296
|
+
|
|
297
|
+
See also the [epistemic integrity discussion](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) in the Karpathy gist thread — particularly the problem of LLM wikis that "synthesise without citing, drift from sources without knowing it, and present false certainty where disagreement exists." Git-based provenance is Palinode's answer to that problem.
|
|
298
|
+
|
|
299
|
+
If you know of prior art we missed, please [open an issue](https://github.com/phasespace-labs/palinode/issues).
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## License
|
|
304
|
+
|
|
305
|
+
MIT
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
*Built by [Paul Kyle](https://github.com/Paul-Kyle) with help from AI agents who use Palinode to remember building Palinode.*
|
palinode-0.7.0/README.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Palinode
|
|
2
|
+
|
|
3
|
+
**Persistent memory for AI agents. Markdown in, markdown out, git everything.**
|
|
4
|
+
|
|
5
|
+
Your agent's memory is a folder of markdown files. Palinode indexes them with hybrid search, compacts them with an LLM, and exposes them through any interface you want — MCP, REST API, CLI, or plugin hooks. If every service crashes, `cat` still works.
|
|
6
|
+
|
|
7
|
+
*A palinode is a poem that retracts what was said before and says it better. That's what memory compaction does.*
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## The Idea
|
|
12
|
+
|
|
13
|
+
Most agent memory is a black box. You can't read it, you can't diff it, you can't `grep` it when the vector DB is down. Palinode bets on **plain files as the source of truth** and builds everything else as a derived index.
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Files (markdown + YAML frontmatter)
|
|
17
|
+
↓ watched
|
|
18
|
+
Index (SQLite-vec vectors + FTS5 keywords, single .db file)
|
|
19
|
+
↓ queried by
|
|
20
|
+
Interfaces (MCP server, REST API, CLI, plugin hooks)
|
|
21
|
+
↓ compacted by
|
|
22
|
+
LLM (proposes ops → deterministic executor applies them → git commits)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
That's the whole architecture. One directory of `.md` files, one SQLite database, one API server. No Postgres, no Redis, no cloud dependency.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## One Backend, Every Interface
|
|
30
|
+
|
|
31
|
+
Palinode doesn't care how you talk to it. The same 17 tools work everywhere:
|
|
32
|
+
|
|
33
|
+
| Interface | Transport | Best For |
|
|
34
|
+
|-----------|-----------|----------|
|
|
35
|
+
| **MCP Server** | Streamable HTTP or stdio | Claude Code, Claude Desktop, Cursor, Zed |
|
|
36
|
+
| **REST API** | HTTP on :6340 | Scripts, webhooks, custom integrations |
|
|
37
|
+
| **CLI** | Wraps REST API | Cron jobs, SSH, shell scripts (8x fewer tokens than MCP) |
|
|
38
|
+
| **Plugin** | Agent lifecycle hooks | Agent frameworks with inject/extract patterns |
|
|
39
|
+
|
|
40
|
+
Set up once on a server. Connect from any machine, any IDE, any agent framework. The MCP server is a pure HTTP client — it holds no state, no database connection, no embedder. Point it at the API and go.
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"palinode": { "url": "http://your-server:6341/mcp/" }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
That's the entire client config for Claude Code, Claude Desktop, Cursor, or Zed. See [docs/MCP-SETUP.md](docs/MCP-SETUP.md) for multi-IDE setup, or [docs/INSTALL-CLAUDE-CODE.md](docs/INSTALL-CLAUDE-CODE.md) for Claude Code specifically.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## How It Works
|
|
55
|
+
|
|
56
|
+
**Store** — Typed markdown files (people, projects, decisions, insights) with YAML frontmatter. Git-versioned. Human-readable. Editable in Obsidian, VS Code, vim, or anything.
|
|
57
|
+
|
|
58
|
+
**Index** — A file watcher embeds with BGE-M3 and indexes with FTS5 as you save. Content-hash dedup skips re-embedding unchanged files (~90% savings). Single SQLite file, zero external services.
|
|
59
|
+
|
|
60
|
+
**Search** — Hybrid BM25 + vector search merged with Reciprocal Rank Fusion. Keyword precision when you need exact terms, semantic recall when you don't. Optional associative entity graph and prospective triggers.
|
|
61
|
+
|
|
62
|
+
**Compact** — Weekly consolidation where an LLM proposes structured operations (KEEP / UPDATE / MERGE / SUPERSEDE / ARCHIVE) and a deterministic executor applies them. The LLM never touches your files directly. Every compaction is a git commit you can review, blame, or revert.
|
|
63
|
+
|
|
64
|
+
**Audit** — `git blame` any fact. `git diff` any change. `rollback` any mistake. These aren't just git-compatible files — `palinode_diff`, `palinode_blame`, and `palinode_rollback` are first-class tools your agent can call.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Quick Start
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Install
|
|
72
|
+
git clone https://github.com/phasespace-labs/palinode && cd palinode
|
|
73
|
+
python3 -m venv venv && source venv/bin/activate
|
|
74
|
+
pip install -e .
|
|
75
|
+
|
|
76
|
+
# Create your memory directory
|
|
77
|
+
mkdir -p ~/.palinode/{people,projects,decisions,insights,daily}
|
|
78
|
+
cd ~/.palinode && git init
|
|
79
|
+
cp /path/to/palinode/palinode.config.yaml.example palinode.config.yaml # adjust path
|
|
80
|
+
|
|
81
|
+
# Start services
|
|
82
|
+
PALINODE_DIR=~/.palinode palinode-api # REST API on :6340
|
|
83
|
+
PALINODE_DIR=~/.palinode palinode-watcher # auto-indexes on file save
|
|
84
|
+
PALINODE_DIR=~/.palinode palinode-mcp-sse # MCP server on :6341 (optional)
|
|
85
|
+
|
|
86
|
+
# Verify
|
|
87
|
+
curl http://localhost:6340/status
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
> Your memory directory is **private**. It contains personal data. Never make it public. The code repo contains zero memory files.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Tools
|
|
95
|
+
|
|
96
|
+
17 tools available through every interface:
|
|
97
|
+
|
|
98
|
+
| Tool | What It Does |
|
|
99
|
+
|------|-------------|
|
|
100
|
+
| `search` | Hybrid BM25 + vector search with category filter |
|
|
101
|
+
| `save` | Store a typed memory (person, decision, insight, project) |
|
|
102
|
+
| `list` | Browse memory files by type, filter by core status |
|
|
103
|
+
| `read` | Read the full content of a memory file |
|
|
104
|
+
| `ingest` | Fetch a URL and save as research |
|
|
105
|
+
| `status` | Health check — file counts, index stats, service status |
|
|
106
|
+
| `history` | Git history with diff stats, rename tracking, and limit |
|
|
107
|
+
| `entities` | Entity graph — cross-references between memories |
|
|
108
|
+
| `consolidate` | Preview or run LLM-powered compaction |
|
|
109
|
+
| `diff` | What changed in the last N days |
|
|
110
|
+
| `blame` | Trace a fact back to the commit that recorded it |
|
|
111
|
+
| `rollback` | Revert a file to a previous commit (safe, creates new commit) |
|
|
112
|
+
| `push` | Sync memory to a remote git repo |
|
|
113
|
+
| `trigger` | Prospective recall — auto-inject when a topic comes up |
|
|
114
|
+
| `lint` | Health scan — orphans, stale files, missing fields |
|
|
115
|
+
| `session_end` | Capture summary, decisions, and blockers at end of session |
|
|
116
|
+
| `prompt` | List, show, or activate versioned LLM prompts |
|
|
117
|
+
|
|
118
|
+
Every tool is accessible as `palinode_<name>` via MCP, `palinode <name>` via CLI, or `POST/GET /<name>` via the REST API.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Stack
|
|
123
|
+
|
|
124
|
+
| Layer | Choice | Why |
|
|
125
|
+
|-------|--------|-----|
|
|
126
|
+
| Source of truth | Markdown + YAML frontmatter | Human-readable, git-versioned, portable |
|
|
127
|
+
| Vector index | SQLite-vec (embedded) | No server, single file, zero config |
|
|
128
|
+
| Keyword index | SQLite FTS5 (embedded) | BM25 for exact terms, zero dependencies |
|
|
129
|
+
| Embeddings | BGE-M3 via Ollama | Local, private, no API key needed |
|
|
130
|
+
| API | FastAPI | Lightweight, async, one process |
|
|
131
|
+
| MCP | Python MCP SDK (Streamable HTTP) | Works with every IDE over the network |
|
|
132
|
+
| CLI | Click (wraps REST API) | Shell-native, TTY-aware output |
|
|
133
|
+
| Behavior | [`PROGRAM.md`](PROGRAM.md) | What to remember, how to extract, how to compact — edit one file to change all behavior |
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Memory File Format
|
|
138
|
+
|
|
139
|
+
```yaml
|
|
140
|
+
---
|
|
141
|
+
id: project-palinode
|
|
142
|
+
category: project
|
|
143
|
+
name: Palinode
|
|
144
|
+
core: true
|
|
145
|
+
status: active
|
|
146
|
+
entities: [person/paul]
|
|
147
|
+
last_updated: 2026-04-05T00:00:00Z
|
|
148
|
+
summary: "Persistent memory for AI agents."
|
|
149
|
+
---
|
|
150
|
+
# Palinode
|
|
151
|
+
|
|
152
|
+
Your content here. As detailed or brief as you want.
|
|
153
|
+
Files marked `core: true` are always in context.
|
|
154
|
+
Everything else is retrieved on demand via hybrid search.
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Open your memory directory as an [Obsidian](https://obsidian.md) vault for visual browsing. See [docs/OBSIDIAN-SETUP.md](docs/OBSIDIAN-SETUP.md).
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Configuration
|
|
162
|
+
|
|
163
|
+
All behavior is in `palinode.config.yaml`:
|
|
164
|
+
|
|
165
|
+
```yaml
|
|
166
|
+
memory_dir: "~/.palinode"
|
|
167
|
+
ollama_url: "http://localhost:11434"
|
|
168
|
+
embedding_model: "bge-m3"
|
|
169
|
+
|
|
170
|
+
recall:
|
|
171
|
+
search:
|
|
172
|
+
top_k: 5
|
|
173
|
+
threshold: 0.4
|
|
174
|
+
core:
|
|
175
|
+
max_chars_per_file: 3000
|
|
176
|
+
|
|
177
|
+
search:
|
|
178
|
+
hybrid_enabled: true
|
|
179
|
+
hybrid_weight: 0.5 # 0.0 = vector only, 1.0 = BM25 only
|
|
180
|
+
|
|
181
|
+
consolidation:
|
|
182
|
+
llm_model: "llama3.1:8b" # any chat model that outputs JSON
|
|
183
|
+
llm_url: "http://localhost:11434"
|
|
184
|
+
llm_fallbacks: # tried in order if primary fails
|
|
185
|
+
- model: "qwen2.5:14b-instruct"
|
|
186
|
+
url: "http://localhost:11434"
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
All models are swappable. Any Ollama embedding model, any OpenAI-compatible chat endpoint. See [palinode.config.yaml.example](palinode.config.yaml.example) for the full reference.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Requirements
|
|
194
|
+
|
|
195
|
+
- **Python 3.11+**
|
|
196
|
+
- **Ollama** with `bge-m3` (`ollama pull bge-m3`)
|
|
197
|
+
- **Git**
|
|
198
|
+
|
|
199
|
+
Optional: a chat model for consolidation (any 7B+ works).
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## API Reference
|
|
204
|
+
|
|
205
|
+
| Method | Path | Description |
|
|
206
|
+
|--------|------|-------------|
|
|
207
|
+
| `GET` | `/status` | Health check + stats |
|
|
208
|
+
| `POST` | `/search` | Hybrid search with filters |
|
|
209
|
+
| `POST` | `/search-associative` | Entity graph traversal |
|
|
210
|
+
| `POST` | `/save` | Create a typed memory file |
|
|
211
|
+
| `POST` | `/ingest-url` | Fetch URL, save as research |
|
|
212
|
+
| `GET/POST` | `/triggers` | Prospective recall triggers |
|
|
213
|
+
| `POST` | `/consolidate` | Run or preview compaction |
|
|
214
|
+
| `GET` | `/list` | Browse files by type |
|
|
215
|
+
| `GET` | `/read?file_path=...` | Read a memory file |
|
|
216
|
+
| `GET` | `/history/{file_path}` | Git log for a file |
|
|
217
|
+
| `GET` | `/diff` | Recent changes |
|
|
218
|
+
| `GET` | `/blame/{file_path}` | Git blame |
|
|
219
|
+
| `POST` | `/rollback` | Revert a file |
|
|
220
|
+
| `POST` | `/push` | Push to git remote |
|
|
221
|
+
| `POST` | `/reindex` | Rebuild indices |
|
|
222
|
+
| `POST` | `/session-end` | Capture session summary |
|
|
223
|
+
| `POST` | `/lint` | Health scan |
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Design Principles
|
|
228
|
+
|
|
229
|
+
1. **Files are truth.** Not databases, not vector stores. Markdown files that humans can read, edit, and version with git.
|
|
230
|
+
|
|
231
|
+
2. **Typed, not flat.** People, projects, decisions, insights — each has structure. This enables reliable retrieval and consolidation.
|
|
232
|
+
|
|
233
|
+
3. **Consolidation, not accumulation.** 100 sessions should produce 20 well-maintained files, not 100 unread dumps.
|
|
234
|
+
|
|
235
|
+
4. **Invisible when working.** The human talks to their agent. Palinode works behind the scenes.
|
|
236
|
+
|
|
237
|
+
5. **Graceful degradation.** Vector index down? Read files directly. Embedding service down? Grep. Machine off? It's a git repo, clone it anywhere.
|
|
238
|
+
|
|
239
|
+
6. **Zero taxonomy burden.** The system classifies. The human reviews. If the human has to maintain a taxonomy, the system dies.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## What's Unique
|
|
244
|
+
|
|
245
|
+
- **Git operations as agent tools** — `diff`, `blame`, `rollback`, `push` exposed via MCP. No other system makes git ops callable by the agent.
|
|
246
|
+
- **Operation-based compaction** — KEEP/UPDATE/MERGE/SUPERSEDE/ARCHIVE DSL. LLM proposes, deterministic executor disposes. Every compaction is a reviewable git commit.
|
|
247
|
+
- **Per-fact addressability** — `<!-- fact:slug -->` IDs inline in markdown, invisible in rendering, preserved by git, targetable by compaction.
|
|
248
|
+
- **4-phase injection** — Core (always) + Topic (per-turn search) + Associative (entity graph) + Triggered (prospective recall).
|
|
249
|
+
- **Multi-transport MCP** — stdio for local, Streamable HTTP for remote. One server, any IDE on any machine.
|
|
250
|
+
- **If everything crashes, `cat` still works.**
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Acknowledgments
|
|
255
|
+
|
|
256
|
+
Palinode builds on ideas from [Karpathy's LLM Knowledge Bases](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f), [Letta](https://github.com/letta-ai/letta) (tiered memory), and [LangMem](https://github.com/langchain-ai/langmem) (typed schemas + background consolidation). See [docs/ACKNOWLEDGMENTS.md](docs/ACKNOWLEDGMENTS.md) for the full list.
|
|
257
|
+
|
|
258
|
+
See also the [epistemic integrity discussion](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) in the Karpathy gist thread — particularly the problem of LLM wikis that "synthesise without citing, drift from sources without knowing it, and present false certainty where disagreement exists." Git-based provenance is Palinode's answer to that problem.
|
|
259
|
+
|
|
260
|
+
If you know of prior art we missed, please [open an issue](https://github.com/phasespace-labs/palinode/issues).
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## License
|
|
265
|
+
|
|
266
|
+
MIT
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
*Built by [Paul Kyle](https://github.com/Paul-Kyle) with help from AI agents who use Palinode to remember building Palinode.*
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Palinode — Persistent memory for AI agents.
|
|
3
|
+
|
|
4
|
+
Files as truth. Vectors as search. Prompts as source code.
|
|
5
|
+
|
|
6
|
+
Components:
|
|
7
|
+
core/ — Config, embeddings, storage, markdown parsing
|
|
8
|
+
api/ — FastAPI HTTP server
|
|
9
|
+
indexer/ — File watcher daemon (auto-indexes on save)
|
|
10
|
+
ingest/ — Ingestion pipeline (PDF, audio, URL, text)
|
|
11
|
+
mcp.py — MCP server for Claude Code integration
|
|
12
|
+
cli.py — Command-line interface
|
|
13
|
+
"""
|
|
14
|
+
__version__ = "0.2.0"
|