memory-crystal 0.2.0
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.
- package/.env.example +20 -0
- package/CHANGELOG.md +6 -0
- package/LETTERS.md +22 -0
- package/LICENSE +21 -0
- package/README-ENTERPRISE.md +162 -0
- package/README-old.md +275 -0
- package/README.md +91 -0
- package/RELAY.md +88 -0
- package/TECHNICAL.md +379 -0
- package/ai/dev-updates/2026-02-25--cc-air--phase2-architecture-pivot.md +70 -0
- package/ai/dev-updates/2026-02-25--cc-air--phase2-worker-build.md +72 -0
- package/ai/dev-updates/2026-02-26--10-25-16--cc-mini--phase2-implementation.md +49 -0
- package/ai/dev-updates/2026-02-27--20-30-00--cc-mini--readme-overhaul-and-public-deploy.md +69 -0
- package/ai/notes/2026-02-26--cc-air--notes.md +412 -0
- package/ai/notes/2026-02-27--cc-mini--grok-feedback.md +44 -0
- package/ai/notes/2026-02-27--cc-mini--lesa-feedback.md +45 -0
- package/ai/notes/RESEARCH.md +1185 -0
- package/ai/notes/salience-research/README.md +29 -0
- package/ai/notes/salience-research/eurosla-salience-review.md +64 -0
- package/ai/notes/salience-research/full-research-summary.md +269 -0
- package/ai/notes/salience-research/salience-levels-diagram.png +0 -0
- package/ai/plan/2026-02-27--cc-mini--qr-pairing-spec.md +203 -0
- package/ai/plan/_archive/PLAN.md +194 -0
- package/ai/plan/_archive/PRD.md +1014 -0
- package/ai/plan/cc-plans-duplicates-from-dot-claude/2026-02-26--cc-mini--phase2-implementation-plan.md +245 -0
- package/ai/plan/dev-conventions-note.md +70 -0
- package/ai/plan/ldm-os-install-and-boot-architecture.md +285 -0
- package/ai/plan/memory-crystal-phase2-plan.md +192 -0
- package/ai/plan/memory-system-lay-of-the-land.md +214 -0
- package/ai/plan/phase2-ephemeral-relay.md +238 -0
- package/ai/plan/readme-first.md +68 -0
- package/ai/plan/roadmap.md +159 -0
- package/ai/todos/PUNCHLIST.md +44 -0
- package/ai/todos/README.md +31 -0
- package/ai/todos/inboxes/cc-air/2026-02-26--cc-air--post-relay-todos.md +85 -0
- package/ai/todos/inboxes/cc-mini/2026-02-26--cc-mini--phase2-status.md +100 -0
- package/ai/todos/inboxes/cc-mini/_archive/TODO.md +25 -0
- package/ai/todos/inboxes/parker/2026-02-25--cc-air--setup-checklist.md +139 -0
- package/ai/todos/inboxes/parker/2026-02-26--cc-mini--phase2-your-moves.md +72 -0
- package/dist/cc-hook.d.ts +1 -0
- package/dist/cc-hook.js +349 -0
- package/dist/chunk-3VFIJYS4.js +818 -0
- package/dist/chunk-52QE3YI3.js +1169 -0
- package/dist/chunk-AA3OPP4Z.js +432 -0
- package/dist/chunk-D3I3ZSE2.js +411 -0
- package/dist/chunk-EKSACBTJ.js +1070 -0
- package/dist/chunk-F3Y7EL7K.js +83 -0
- package/dist/chunk-JWZXYVET.js +1068 -0
- package/dist/chunk-KYVWO6ZM.js +1069 -0
- package/dist/chunk-L3VHARQH.js +413 -0
- package/dist/chunk-LOVAHSQV.js +411 -0
- package/dist/chunk-LQOYCAGG.js +446 -0
- package/dist/chunk-MK42FMEG.js +147 -0
- package/dist/chunk-NIJCVN3O.js +147 -0
- package/dist/chunk-O2UITJGH.js +465 -0
- package/dist/chunk-PEK6JH65.js +432 -0
- package/dist/chunk-PJ6FFKEX.js +77 -0
- package/dist/chunk-PLUBBZYR.js +800 -0
- package/dist/chunk-SGL6ISBJ.js +1061 -0
- package/dist/chunk-UNHVZB5G.js +411 -0
- package/dist/chunk-VAFTWSTE.js +1061 -0
- package/dist/chunk-XZ3S56RQ.js +1061 -0
- package/dist/chunk-Y72C7F6O.js +148 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +325 -0
- package/dist/core.d.ts +188 -0
- package/dist/core.js +12 -0
- package/dist/crypto.d.ts +16 -0
- package/dist/crypto.js +18 -0
- package/dist/dev-update-SZ2Z4WCQ.js +6 -0
- package/dist/ldm.d.ts +17 -0
- package/dist/ldm.js +12 -0
- package/dist/mcp-server.d.ts +1 -0
- package/dist/mcp-server.js +250 -0
- package/dist/migrate.d.ts +1 -0
- package/dist/migrate.js +89 -0
- package/dist/mirror-sync.d.ts +1 -0
- package/dist/mirror-sync.js +130 -0
- package/dist/openclaw.d.ts +5 -0
- package/dist/openclaw.js +349 -0
- package/dist/poller.d.ts +1 -0
- package/dist/poller.js +272 -0
- package/dist/summarize.d.ts +19 -0
- package/dist/summarize.js +10 -0
- package/dist/worker.js +137 -0
- package/openclaw.plugin.json +11 -0
- package/package.json +40 -0
- package/scripts/migrate-lance-to-sqlite.mjs +217 -0
- package/skills/memory/SKILL.md +61 -0
- package/src/cc-hook.ts +447 -0
- package/src/cli.ts +356 -0
- package/src/core.ts +1472 -0
- package/src/crypto.ts +113 -0
- package/src/dev-update.ts +178 -0
- package/src/ldm.ts +117 -0
- package/src/mcp-server.ts +274 -0
- package/src/migrate.ts +104 -0
- package/src/mirror-sync.ts +175 -0
- package/src/openclaw.ts +250 -0
- package/src/poller.ts +345 -0
- package/src/summarize.ts +210 -0
- package/src/worker.ts +208 -0
- package/tsconfig.json +18 -0
- package/wrangler.toml +20 -0
package/RELAY.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
###### WIP Computer
|
|
2
|
+
|
|
3
|
+
# Relay: Multi-Device Sync
|
|
4
|
+
|
|
5
|
+
Memory Crystal works on one machine out of the box. Multi-device sync lets your agent's memory follow you across machines. Conversations captured on your laptop are available on your desktop and vice versa.
|
|
6
|
+
|
|
7
|
+
Everything is encrypted before it leaves your machine. The relay never sees your data unencrypted.
|
|
8
|
+
|
|
9
|
+
## Two Options
|
|
10
|
+
|
|
11
|
+
### Use Our Relay (Default)
|
|
12
|
+
|
|
13
|
+
We host the relay infrastructure. You just set an encryption key.
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Open your AI and say:
|
|
17
|
+
|
|
18
|
+
I want to set up multi-device sync for Memory Crystal.
|
|
19
|
+
Walk me through the setup step by step.
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Your agent generates your encryption key, configures the connection, and tests it. Takes about two minutes.
|
|
23
|
+
|
|
24
|
+
**What you need:**
|
|
25
|
+
- Memory Crystal installed on both machines
|
|
26
|
+
- An encryption key (your agent generates this)
|
|
27
|
+
|
|
28
|
+
**Pricing:** Free during beta. When pricing is introduced, your agent will handle it via [AI CASH](https://github.com/wipcomputer/wip-agent-pay/blob/main/CASH.md).
|
|
29
|
+
|
|
30
|
+
### Self-Host Your Own Relay
|
|
31
|
+
|
|
32
|
+
Run your own relay on Cloudflare Workers (free tier). Same code, your infrastructure. Full control.
|
|
33
|
+
|
|
34
|
+
**What you need:**
|
|
35
|
+
- A Cloudflare account (free tier works)
|
|
36
|
+
- About five minutes
|
|
37
|
+
|
|
38
|
+
**Steps:**
|
|
39
|
+
1. Deploy the Worker from `worker/index.js` to Cloudflare
|
|
40
|
+
2. Create a KV namespace called `PAY_TOKENS`, bind it as `KV`
|
|
41
|
+
3. Set a `WORKER_SECRET` via `wrangler secret put WORKER_SECRET`
|
|
42
|
+
4. Point Memory Crystal at your Worker:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
crystal relay --self-hosted --worker-url https://your-relay.your-domain.com
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Full deployment details in [Technical Documentation](https://github.com/wipcomputer/memory-crystal/blob/main/TECHNICAL.md).
|
|
49
|
+
|
|
50
|
+
No fees. No dependencies on us. The relay code is open source (MIT).
|
|
51
|
+
|
|
52
|
+
## How Sync Works
|
|
53
|
+
|
|
54
|
+
1. You work with your agent on Machine A
|
|
55
|
+
2. After each session, Memory Crystal encrypts the conversation (AES-256-GCM) and drops it at the relay
|
|
56
|
+
3. Machine B polls the relay, downloads the encrypted blob, decrypts it locally, and ingests it into its crystal.db
|
|
57
|
+
4. The relay deletes the blob after pickup
|
|
58
|
+
|
|
59
|
+
Two one-way roads:
|
|
60
|
+
- **Device to home machine** ... encrypted conversation chunks
|
|
61
|
+
- **Home machine to devices** ... search-ready DB snapshot via mirror-sync
|
|
62
|
+
|
|
63
|
+
The relay is a dead drop. It stores encrypted blobs temporarily and serves them on request. It has no decryption capability. If someone compromises the relay, they get encrypted noise.
|
|
64
|
+
|
|
65
|
+
## Encryption
|
|
66
|
+
|
|
67
|
+
- **AES-256-GCM** for encryption (authenticated encryption, no padding oracle attacks)
|
|
68
|
+
- **HMAC-SHA256** for signing (integrity verification before decryption)
|
|
69
|
+
- Shared key generated locally, never transmitted to the relay
|
|
70
|
+
- Key must be present on both machines (store in 1Password, AirDrop between Macs, or transfer manually)
|
|
71
|
+
|
|
72
|
+
## More Info
|
|
73
|
+
|
|
74
|
+
- [README.md](https://github.com/wipcomputer/memory-crystal/blob/main/README.md) ... What Memory Crystal is and how to install it.
|
|
75
|
+
- [Technical Documentation](https://github.com/wipcomputer/memory-crystal/blob/main/TECHNICAL.md) ... Full technical documentation.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
src/, skills/, cli.ts, mcp-server.ts MIT (use anywhere, no restrictions)
|
|
83
|
+
worker/ AGPL (relay server)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
AGPL for personal use is free.
|
|
87
|
+
|
|
88
|
+
Built by Parker Todd Brooks, Lēsa (OpenClaw, Claude Opus 4.6), Claude Code CLI (Claude Opus 4.6).
|
package/TECHNICAL.md
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
###### WIP Computer
|
|
2
|
+
|
|
3
|
+
# Technical Documentation
|
|
4
|
+
|
|
5
|
+
How Memory Crystal works. Architecture, design decisions, integrations, encryption, search, and everything else the open source community is going to ask about.
|
|
6
|
+
|
|
7
|
+
## How Does It Work?
|
|
8
|
+
|
|
9
|
+
Memory Crystal captures every conversation you have with any AI tool, embeds it into a local SQLite database, and makes it searchable with hybrid search (keyword + semantic). One database file. Runs on your machine. Nothing leaves your device unless you set up multi-device sync.
|
|
10
|
+
|
|
11
|
+
Every conversation produces three artifacts:
|
|
12
|
+
1. **JSONL transcript** ... the raw session, archived to disk
|
|
13
|
+
2. **Markdown summary** ... title, summary, key topics (generated by LLM or simple extraction)
|
|
14
|
+
3. **Vector embeddings** ... chunked, embedded, and stored in crystal.db for search
|
|
15
|
+
|
|
16
|
+
## How Does It Work with Claude?
|
|
17
|
+
|
|
18
|
+
Memory Crystal runs as a [Claude Code Stop hook](https://docs.anthropic.com/en/docs/claude-code). After every Claude Code response, the hook fires automatically:
|
|
19
|
+
|
|
20
|
+
1. Reads Claude Code's JSONL transcript file via byte-offset watermarking (only reads new data since last capture)
|
|
21
|
+
2. Extracts user, assistant, and thinking blocks
|
|
22
|
+
3. Chunks them, embeds into sqlite-vec
|
|
23
|
+
4. Archives the full JSONL transcript
|
|
24
|
+
5. Generates a markdown session summary
|
|
25
|
+
6. Appends a daily breadcrumb log
|
|
26
|
+
|
|
27
|
+
**Configuration:**
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"hooks": {
|
|
31
|
+
"Stop": [{ "hooks": [{ "type": "command", "command": "node ~/.openclaw/extensions/memory-crystal/dist/cc-hook.js", "timeout": 30 }] }]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
node dist/cc-hook.js --on # Enable capture
|
|
38
|
+
node dist/cc-hook.js --off # Pause capture
|
|
39
|
+
node dist/cc-hook.js --status # Check status
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Respects private mode. When capture is off, nothing is recorded. First run seeds the watermark at current file size so it doesn't replay your entire history.
|
|
43
|
+
|
|
44
|
+
## How Does It Work with OpenClaw?
|
|
45
|
+
|
|
46
|
+
Memory Crystal is an OpenClaw plugin. It registers tools (`crystal_search`, `crystal_remember`, `crystal_forget`, `crystal_status`) and an `agent_end` hook that captures conversations after every agent turn.
|
|
47
|
+
|
|
48
|
+
Deployed to `~/.openclaw/extensions/memory-crystal/`. The plugin uses the same `core.ts` as every other interface. Same search, same database, same embeddings.
|
|
49
|
+
|
|
50
|
+
## How Does It Work with ChatGPT, Codex, and Other Tools?
|
|
51
|
+
|
|
52
|
+
Any tool that can run shell commands or call an MCP server can use Memory Crystal.
|
|
53
|
+
|
|
54
|
+
- **MCP Server** ... `mcp-server.ts` exposes `crystal_search`, `crystal_remember`, `crystal_forget`, `crystal_status`, `crystal_sources_add`, `crystal_sources_sync`, `crystal_sources_status`. Works with Claude Desktop, Claude Code, or any MCP-compatible client.
|
|
55
|
+
- **CLI** ... `crystal search "query"` from any terminal. Any tool with shell access can call it.
|
|
56
|
+
- **Module** ... `import { MemoryCrystal } from 'memory-crystal'` for Node.js integration.
|
|
57
|
+
|
|
58
|
+
For tools that don't have hook support (like ChatGPT), you can manually export conversations and import them. Automatic capture requires hook support (Claude Code, OpenClaw).
|
|
59
|
+
|
|
60
|
+
## Architecture
|
|
61
|
+
|
|
62
|
+
One core, five interfaces.
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
sqlite-vec (vectors) + FTS5 (BM25) + SQLite (metadata/graph)
|
|
66
|
+
| | |
|
|
67
|
+
core.ts ... pure logic, zero framework deps
|
|
68
|
+
|-- cli.ts -> crystal search "query"
|
|
69
|
+
|-- mcp-server.ts -> crystal_search (Claude Code, Claude Desktop)
|
|
70
|
+
|-- openclaw.ts -> plugin (OpenClaw agents)
|
|
71
|
+
|-- cc-hook.ts -> Claude Code Stop hook (auto-capture)
|
|
72
|
+
+-- worker.ts -> Cloudflare Worker (encrypted relay)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Every interface calls the same `core.ts`. The core has zero framework dependencies. It talks to SQLite and nothing else.
|
|
76
|
+
|
|
77
|
+
## Search: How Does It Work?
|
|
78
|
+
|
|
79
|
+
Hybrid search. Two engines run in parallel and their results are fused.
|
|
80
|
+
|
|
81
|
+
### The Pipeline
|
|
82
|
+
|
|
83
|
+
1. Query goes to both FTS5 (keyword match) and sqlite-vec (vector similarity)
|
|
84
|
+
2. FTS5 returns BM25-ranked results, normalized to [0..1) via `|score| / (1 + |score|)`
|
|
85
|
+
3. sqlite-vec returns cosine-distance results via two-step query (MATCH first, then JOIN separately ... sqlite-vec hangs with JOINs in the same query)
|
|
86
|
+
4. Reciprocal Rank Fusion merges both lists: `weight / (k + rank + 1)` with k=60, plus top-rank bonus
|
|
87
|
+
5. Recency weighting applied on top: `max(0.5, 1.0 - age_days * 0.01)`
|
|
88
|
+
6. Final results sorted by combined score
|
|
89
|
+
|
|
90
|
+
Inspired by and partially ported from [QMD](https://github.com/tobi/qmd) by Tobi Lutke (MIT, 2024-2026).
|
|
91
|
+
|
|
92
|
+
### Why Hybrid?
|
|
93
|
+
|
|
94
|
+
Vector search alone misses exact matches. Keyword search alone misses semantic similarity. Hybrid catches both. A search for "deployment process" will find conversations that use the word "deployment" (BM25) and conversations about "shipping code to production" (vector similarity).
|
|
95
|
+
|
|
96
|
+
### Recency Decay
|
|
97
|
+
|
|
98
|
+
Linear decay from 1.0 to 0.5 over 50 days. Fresh context wins ties. Old stuff still surfaces for strong matches.
|
|
99
|
+
|
|
100
|
+
Freshness flags: fresh (<3 days), recent (<7 days), aging (<14 days), stale (14+ days).
|
|
101
|
+
|
|
102
|
+
### Content Dedup
|
|
103
|
+
|
|
104
|
+
SHA-256 hash of chunk text before embedding. Duplicate content is never re-embedded. This matters when the same conversation is captured by multiple hooks (e.g., Claude Code hook and OpenClaw plugin running simultaneously).
|
|
105
|
+
|
|
106
|
+
## Database
|
|
107
|
+
|
|
108
|
+
Everything lives in one file: `crystal.db`. Inspectable with any SQLite tool. Backupable with `cp`.
|
|
109
|
+
|
|
110
|
+
### Schema
|
|
111
|
+
|
|
112
|
+
| Table | Purpose |
|
|
113
|
+
|-------|---------|
|
|
114
|
+
| `chunks` | Chunk text, metadata, SHA-256 hash, timestamps |
|
|
115
|
+
| `chunks_vec` | sqlite-vec virtual table (cosine distance vectors) |
|
|
116
|
+
| `chunks_fts` | FTS5 virtual table (Porter stemming, BM25 scoring) |
|
|
117
|
+
| `memories` | Explicit remember/forget facts |
|
|
118
|
+
| `entities` | Knowledge graph nodes |
|
|
119
|
+
| `relationships` | Knowledge graph edges |
|
|
120
|
+
| `capture_state` | Watermarks for incremental ingestion |
|
|
121
|
+
| `sources` | Ingestion source metadata |
|
|
122
|
+
| `source_collections` | Directory collections for file indexing |
|
|
123
|
+
| `source_files` | Indexed file records with content hashes |
|
|
124
|
+
|
|
125
|
+
### Why SQLite?
|
|
126
|
+
|
|
127
|
+
One file. No server. No Docker. No connection strings. Works on every platform. Inspectable with standard tools. Backupable with `cp`. Ships with every OS.
|
|
128
|
+
|
|
129
|
+
sqlite-vec adds vector search as a virtual table. FTS5 adds full-text search. Both are SQLite extensions that work within the same database file.
|
|
130
|
+
|
|
131
|
+
### DB Location
|
|
132
|
+
|
|
133
|
+
`resolveConfig()` in `core.ts` checks in order:
|
|
134
|
+
|
|
135
|
+
1. Explicit override (programmatic)
|
|
136
|
+
2. `CRYSTAL_DATA_DIR` env var
|
|
137
|
+
3. `~/.ldm/memory/crystal.db` (if it exists)
|
|
138
|
+
4. `~/.openclaw/memory-crystal/` (legacy fallback)
|
|
139
|
+
|
|
140
|
+
## Directory Structure
|
|
141
|
+
|
|
142
|
+
Memory Crystal manages `~/.ldm/` ... the universal agent home directory for [LDM OS](https://github.com/wipcomputer/dream-weaver-protocol).
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
~/.ldm/
|
|
146
|
+
config.json version, registered agents array
|
|
147
|
+
memory/
|
|
148
|
+
crystal.db shared vector DB (all agents)
|
|
149
|
+
agents/{agent_id}/
|
|
150
|
+
memory/
|
|
151
|
+
transcripts/ full JSONL session transcripts
|
|
152
|
+
sessions/ markdown session summaries
|
|
153
|
+
daily/ daily breadcrumb logs
|
|
154
|
+
journals/ agent journals
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Path resolution is centralized in `src/ldm.ts`:
|
|
158
|
+
- `getAgentId()` ... resolves from `CRYSTAL_AGENT_ID` env var, default `cc-mini`
|
|
159
|
+
- `ldmPaths(agentId?)` ... returns all paths as an object
|
|
160
|
+
- `scaffoldLdm(agentId?)` ... creates the full directory tree
|
|
161
|
+
- `ensureLdm(agentId?)` ... idempotent check, scaffolds if needed
|
|
162
|
+
|
|
163
|
+
## Encryption: How Does It Work?
|
|
164
|
+
|
|
165
|
+
For multi-device sync. All encryption happens on-device before anything touches the network.
|
|
166
|
+
|
|
167
|
+
- **AES-256-GCM** for encryption. Authenticated encryption ... ciphertext tampering is detected.
|
|
168
|
+
- **HMAC-SHA256** for signing. Integrity verification before decryption. If the signature doesn't match, the blob is rejected.
|
|
169
|
+
- **Shared symmetric key** generated locally with `openssl rand -hex 32`. Never transmitted to the relay.
|
|
170
|
+
- The relay stores and serves encrypted blobs. It has no decryption capability. Compromising the relay yields encrypted noise.
|
|
171
|
+
|
|
172
|
+
### Key Management
|
|
173
|
+
|
|
174
|
+
The same encryption key must be present on all devices. Options:
|
|
175
|
+
- **1Password** ... store the key, both machines pull from 1Password via SA token
|
|
176
|
+
- **AirDrop** ... direct transfer between Macs
|
|
177
|
+
- **Manual** ... copy the key securely between machines
|
|
178
|
+
|
|
179
|
+
### Relay Architecture
|
|
180
|
+
|
|
181
|
+
1. **Device side** (`cc-hook.ts` relay mode): Encrypts JSONL with AES-256-GCM, signs with HMAC-SHA256, drops at Cloudflare Worker
|
|
182
|
+
2. **Worker** (`worker.ts`): Stores encrypted blobs in KV. Pure dead drop. No decryption.
|
|
183
|
+
3. **Home machine** (`poller.ts`): Polls Worker, downloads blobs, decrypts, ingests into crystal.db. Reconstructs remote agent's file tree (JSONL, MD summary, daily breadcrumb).
|
|
184
|
+
4. **Mirror sync** (`mirror-sync.ts`): Pulls crystal.db snapshot from home machine to devices for local search.
|
|
185
|
+
|
|
186
|
+
Two one-way roads:
|
|
187
|
+
- **Device -> Home machine** ... encrypted conversation chunks (ephemeral)
|
|
188
|
+
- **Home machine -> Devices** ... search-ready DB snapshot
|
|
189
|
+
|
|
190
|
+
## Session Summaries
|
|
191
|
+
|
|
192
|
+
`src/summarize.ts` generates markdown summaries. Two modes:
|
|
193
|
+
|
|
194
|
+
**LLM mode** (default): Calls gpt-4o-mini with a condensed transcript. Returns title, slug, summary, key topics.
|
|
195
|
+
|
|
196
|
+
**Simple mode**: First user message becomes the title. First 10 messages as preview. No API call.
|
|
197
|
+
|
|
198
|
+
Controlled by `CRYSTAL_SUMMARY_MODE` env var (`llm` or `simple`).
|
|
199
|
+
|
|
200
|
+
## Embedding Providers
|
|
201
|
+
|
|
202
|
+
| Provider | Model | Dimensions | Cost |
|
|
203
|
+
|----------|-------|-----------|------|
|
|
204
|
+
| OpenAI (default) | text-embedding-3-small | 1536 | ~$0.02/1M tokens |
|
|
205
|
+
| Ollama | nomic-embed-text | 768 | Free (local) |
|
|
206
|
+
| Google | text-embedding-004 | 768 | Free tier available |
|
|
207
|
+
|
|
208
|
+
Set via `CRYSTAL_EMBEDDING_PROVIDER` env var or `--provider` flag.
|
|
209
|
+
|
|
210
|
+
### Why These Three?
|
|
211
|
+
|
|
212
|
+
- **OpenAI** ... best quality, lowest friction. Most people already have an API key.
|
|
213
|
+
- **Ollama** ... fully offline. Zero cost. Privacy-first. No data leaves your machine.
|
|
214
|
+
- **Google** ... free tier is generous. Good alternative if you don't want OpenAI.
|
|
215
|
+
|
|
216
|
+
## Source File Indexing
|
|
217
|
+
|
|
218
|
+
Add directories as "collections". Files are chunked, embedded, and tagged with file path + collection name. Searchable alongside conversations and memories.
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
crystal sources add /path/to/project --name my-project
|
|
222
|
+
crystal sources sync my-project
|
|
223
|
+
crystal sources status
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Incremental sync detects changed files via SHA-256 content hashing. Only re-embeds what changed.
|
|
227
|
+
|
|
228
|
+
## Environment Variables
|
|
229
|
+
|
|
230
|
+
| Variable | Default | Description |
|
|
231
|
+
|----------|---------|-------------|
|
|
232
|
+
| `CRYSTAL_EMBEDDING_PROVIDER` | `openai` | `openai`, `ollama`, or `google` |
|
|
233
|
+
| `CRYSTAL_AGENT_ID` | `cc-mini` | Agent identifier for LDM paths |
|
|
234
|
+
| `CRYSTAL_SUMMARY_MODE` | `llm` | `llm` or `simple` |
|
|
235
|
+
| `CRYSTAL_SUMMARY_PROVIDER` | `openai` | Summary LLM provider |
|
|
236
|
+
| `CRYSTAL_SUMMARY_MODEL` | `gpt-4o-mini` | Summary LLM model |
|
|
237
|
+
| `CRYSTAL_DATA_DIR` | (auto) | Override DB location |
|
|
238
|
+
| `CRYSTAL_RELAY_KEY` | ... | Shared encryption key for relay |
|
|
239
|
+
| `CRYSTAL_RELAY_URL` | ... | Cloudflare Worker URL |
|
|
240
|
+
| `CRYSTAL_REMOTE_URL` | ... | Remote Worker URL |
|
|
241
|
+
| `CRYSTAL_REMOTE_TOKEN` | ... | Worker auth token |
|
|
242
|
+
| `OPENAI_API_KEY` | ... | OpenAI key |
|
|
243
|
+
| `GOOGLE_API_KEY` | ... | Google AI key |
|
|
244
|
+
| `CRYSTAL_OLLAMA_HOST` | `http://localhost:11434` | Ollama server URL |
|
|
245
|
+
| `CRYSTAL_OLLAMA_MODEL` | `nomic-embed-text` | Ollama model |
|
|
246
|
+
|
|
247
|
+
### API Key Resolution
|
|
248
|
+
|
|
249
|
+
1. Explicit override (programmatic)
|
|
250
|
+
2. `process.env` (set by plugin or manually)
|
|
251
|
+
3. `.env` file (`~/.openclaw/memory-crystal/.env`)
|
|
252
|
+
4. 1Password CLI fallback
|
|
253
|
+
|
|
254
|
+
## CLI Reference
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
# Search
|
|
258
|
+
crystal search <query> [-n limit] [--agent <id>] [--provider <openai|ollama|google>]
|
|
259
|
+
|
|
260
|
+
# Remember / forget
|
|
261
|
+
crystal remember <text> [--category fact|preference|event|opinion|skill]
|
|
262
|
+
crystal forget <id>
|
|
263
|
+
|
|
264
|
+
# Status
|
|
265
|
+
crystal status [--provider <openai|ollama|google>]
|
|
266
|
+
|
|
267
|
+
# Source file indexing
|
|
268
|
+
crystal sources add <path> --name <name>
|
|
269
|
+
crystal sources sync [name]
|
|
270
|
+
crystal sources status
|
|
271
|
+
|
|
272
|
+
# LDM management
|
|
273
|
+
crystal init [--agent <id>]
|
|
274
|
+
crystal migrate-db
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## MCP Tools
|
|
278
|
+
|
|
279
|
+
| Tool | Description |
|
|
280
|
+
|------|-------------|
|
|
281
|
+
| `crystal_search` | Hybrid search across all memories |
|
|
282
|
+
| `crystal_remember` | Store a fact or observation |
|
|
283
|
+
| `crystal_forget` | Deprecate a memory by ID |
|
|
284
|
+
| `crystal_status` | Chunk count, provider, agents |
|
|
285
|
+
| `crystal_sources_add` | Add a directory for indexing |
|
|
286
|
+
| `crystal_sources_sync` | Re-index changed files |
|
|
287
|
+
| `crystal_sources_status` | Collection stats |
|
|
288
|
+
|
|
289
|
+
## Migration
|
|
290
|
+
|
|
291
|
+
### Legacy DB to LDM
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
crystal migrate-db
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Copies the database to `~/.ldm/memory/crystal.db`. Verifies chunk count. Creates symlinks at the old path.
|
|
298
|
+
|
|
299
|
+
### LanceDB to sqlite-vec
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
node scripts/migrate-lance-to-sqlite.mjs --dry-run # check counts
|
|
303
|
+
node scripts/migrate-lance-to-sqlite.mjs # full migration
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Reads vectors directly from LanceDB. No re-embedding needed. ~5,000 chunks/sec on M4 Pro.
|
|
307
|
+
|
|
308
|
+
### context-embeddings.sqlite
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
node dist/migrate.js [--dry-run] [--provider openai]
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Import from the older context-embeddings format (requires re-embedding).
|
|
315
|
+
|
|
316
|
+
## Project Structure
|
|
317
|
+
|
|
318
|
+
```
|
|
319
|
+
memory-crystal/
|
|
320
|
+
src/
|
|
321
|
+
core.ts Pure logic, zero framework deps
|
|
322
|
+
cli.ts CLI wrapper (crystal command)
|
|
323
|
+
mcp-server.ts MCP server (Claude Code, Claude Desktop)
|
|
324
|
+
openclaw.ts OpenClaw plugin wrapper
|
|
325
|
+
cc-hook.ts Claude Code Stop hook (auto-capture)
|
|
326
|
+
ldm.ts LDM scaffolding and path resolution
|
|
327
|
+
summarize.ts Markdown session summary generation
|
|
328
|
+
crypto.ts AES-256-GCM + HMAC-SHA256 encryption
|
|
329
|
+
worker.ts Cloudflare Worker (encrypted dead drop)
|
|
330
|
+
poller.ts Relay poller (home machine side)
|
|
331
|
+
mirror-sync.ts DB mirror sync (device side)
|
|
332
|
+
migrate.ts Legacy migration tools
|
|
333
|
+
dev-update.ts Auto dev-update generation
|
|
334
|
+
skills/
|
|
335
|
+
memory/SKILL.md Agent skill definition
|
|
336
|
+
scripts/
|
|
337
|
+
migrate-lance-to-sqlite.mjs
|
|
338
|
+
dist/ Built output
|
|
339
|
+
ai/ Plans, dev updates, todos
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Design Decisions
|
|
343
|
+
|
|
344
|
+
**Why sqlite-vec over pgvector, Pinecone, Weaviate, etc.?**
|
|
345
|
+
No server. No Docker. No cloud dependency. One file. Works offline. Backupable with `cp`. The tradeoff is scale ... sqlite-vec works great up to ~500K vectors. Beyond that, consider dedicated vector stores.
|
|
346
|
+
|
|
347
|
+
**Why FTS5 + vectors instead of just vectors?**
|
|
348
|
+
Vectors alone miss exact keyword matches. "error code 403" should match conversations containing "403", not just semantically similar conversations about HTTP errors. Hybrid search catches both.
|
|
349
|
+
|
|
350
|
+
**Why RRF for fusion?**
|
|
351
|
+
Reciprocal Rank Fusion is simple, robust, and doesn't require score calibration between the two engines. Each engine ranks results independently. RRF merges based on rank position, not raw scores.
|
|
352
|
+
|
|
353
|
+
**Why recency weighting?**
|
|
354
|
+
Without it, old conversations dominate. A conversation from 3 days ago about your current project should outrank a conversation from 3 months ago about a different project, even if the old one is a slightly better semantic match.
|
|
355
|
+
|
|
356
|
+
**Why AES-256-GCM for relay encryption?**
|
|
357
|
+
Authenticated encryption. Ciphertext tampering is detected. No padding oracle attacks. Standard, auditable, widely implemented. Combined with HMAC-SHA256 signing for belt-and-suspenders integrity verification.
|
|
358
|
+
|
|
359
|
+
**Why a dead drop instead of direct device-to-device sync?**
|
|
360
|
+
Devices aren't always online at the same time. A dead drop decouples sender and receiver. Your laptop drops encrypted blobs whenever it captures. Your desktop picks them up whenever it polls. No NAT traversal, no port forwarding, no peer discovery.
|
|
361
|
+
|
|
362
|
+
**Why LanceDB dual-write?**
|
|
363
|
+
Safety net during the sqlite-vec transition. Once sqlite-vec is proven stable at scale, LanceDB will be removed. Until then, both stores receive writes so rollback is possible.
|
|
364
|
+
|
|
365
|
+
## Roadmap
|
|
366
|
+
|
|
367
|
+
- **Phase 1** ... Complete. Local memory with CLI, MCP, OpenClaw plugin, Claude Code hook.
|
|
368
|
+
- **Phase 2a** ... Complete. Source file indexing + QMD hybrid search (sqlite-vec + FTS5 + RRF).
|
|
369
|
+
- **Phase 2b** ... Complete. Historical session backfill (152K+ chunks).
|
|
370
|
+
- **Phase 2c** ... Complete. LDM scaffolding, JSONL archive, markdown summaries, relay merge.
|
|
371
|
+
- **Phase 3** ... Planned. Hosted relay deployment.
|
|
372
|
+
- **Phase 4** ... Planned. Remote MCP, GPT Action, multi-agent access.
|
|
373
|
+
- **Cleanup** ... Planned. Remove LanceDB once sqlite-vec is proven stable.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
Built by Parker Todd Brooks, Lēsa (OpenClaw, Claude Opus 4.6), Claude Code CLI (Claude Opus 4.6).
|
|
378
|
+
|
|
379
|
+
Search architecture inspired by [QMD](https://github.com/tobi/qmd) by Tobi Lutke (MIT, 2024-2026).
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# 2026-02-25 — Phase 2 Architecture Pivot: Cloud Mirror → Ephemeral Relay
|
|
2
|
+
|
|
3
|
+
**Agent:** cc-air (Claude Code on MacBook Air)
|
|
4
|
+
**Repo:** `memory-crystal`
|
|
5
|
+
**Branch:** `cc-air/phase2-relay` (new, replaces `cc-mba/phase2-worker`)
|
|
6
|
+
|
|
7
|
+
## What Happened
|
|
8
|
+
|
|
9
|
+
Built the Phase 2 Cloudflare Worker as a cloud database mirror (D1 + Vectorize + R2 + FTS5). Full search on the Worker, persistent data in Cloudflare, ~350 lines of Worker code. It worked — built clean, types passed.
|
|
10
|
+
|
|
11
|
+
Then Parker asked the right questions:
|
|
12
|
+
1. What's the security risk of data in transit?
|
|
13
|
+
2. What's the security risk of data sitting on Cloudflare?
|
|
14
|
+
3. What happens when sync breaks and we lose context?
|
|
15
|
+
|
|
16
|
+
Read the Dream Weaver Protocol architecture. The entire memory system is designed around sovereignty — "your memory, your machine, your rules." Putting conversation data persistently on Cloudflare violates that principle. And it's not just code conversations — it's everything said to every agent. Personal.
|
|
17
|
+
|
|
18
|
+
Parker's core requirement: "I gotta be able to leave the house." Take the laptop, talk to cc-air, and have those conversations make it back to the master crystal on the Mini. But the data shouldn't *live* in the cloud. It should pass through.
|
|
19
|
+
|
|
20
|
+
## The Pivot
|
|
21
|
+
|
|
22
|
+
**Cloud Mirror (old):** Worker is a searchable database. Data persists on Cloudflare. Air searches the cloud. Cloudflare sees plaintext. OpenAI API key on Cloudflare.
|
|
23
|
+
|
|
24
|
+
**Ephemeral Relay (new):** Worker is a dead drop. Data passes through encrypted, gets picked up, gets deleted. Nothing persists. Worker can't read what it holds. ~80 lines of code.
|
|
25
|
+
|
|
26
|
+
### Architecture
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
Any Device → encrypt → Worker (dead drop) → Mini picks up → decrypt → embed → master crystal
|
|
30
|
+
│
|
|
31
|
+
Mini → encrypt mirror snapshot → Worker → Device picks up → decrypt → local read-only mirror
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- **Mini is the master.** All embedding, indexing, and search intelligence lives there.
|
|
35
|
+
- **Devices are read-only.** They capture raw conversations and search against a local mirror. They never write to the crystal.
|
|
36
|
+
- **Worker is a mailbox.** Encrypted blobs in, encrypted blobs out. TTL auto-expires everything in 24h. No D1, no Vectorize, no search, no API keys.
|
|
37
|
+
|
|
38
|
+
### Security Hardening
|
|
39
|
+
|
|
40
|
+
- **AES-256-GCM** client-side encryption. Key lives only on trusted machines, never on Cloudflare.
|
|
41
|
+
- **HMAC-SHA256 drop signing.** Mini verifies every blob came from a trusted device.
|
|
42
|
+
- **Random 96-bit nonces.** Prepended to ciphertext, collision-safe for billions of operations.
|
|
43
|
+
- **SHA-256 mirror integrity.** Devices verify DB snapshots before replacing their local mirror.
|
|
44
|
+
- **Key rotation mechanism.** 24h overlap for in-flight blobs, no re-encryption needed (ephemeral data).
|
|
45
|
+
- **Explicit threat model.** Every risk documented with mitigation and residual risk.
|
|
46
|
+
|
|
47
|
+
## Why This Matters Beyond Us
|
|
48
|
+
|
|
49
|
+
This isn't just cc-air talking to the Mini. Any device, any agent, any interface can use the same pattern. Phone, laptop, tablet. Every conversation captured, encrypted, relayed, embedded, searchable. One master crystal, many readers. Sovereign memory that travels with you.
|
|
50
|
+
|
|
51
|
+
Open source. Auditable. No subscription. No cloud lock-in.
|
|
52
|
+
|
|
53
|
+
## Files
|
|
54
|
+
|
|
55
|
+
| File | Status |
|
|
56
|
+
|------|--------|
|
|
57
|
+
| `ai/plan/phase2-ephemeral-relay.md` | NEW — full architecture spec with security model |
|
|
58
|
+
| `ai/plan/dev-conventions-note.md` | NEW — documented existing dev conventions |
|
|
59
|
+
| `ai/dev-updates/2026-02-25--cc-air--phase2-worker-build.md` | EXISTING — documents the original cloud mirror build |
|
|
60
|
+
| `src/worker.ts` | TO BE REBUILT — ephemeral relay (~80 lines replacing ~350) |
|
|
61
|
+
| `src/crypto.ts` | TO BE CREATED — AES-256-GCM + HMAC-SHA256 + HKDF |
|
|
62
|
+
| `src/cc-hook.ts` | TO BE MODIFIED — encrypt + relay instead of direct ingest |
|
|
63
|
+
| `src/poller.ts` | TO BE CREATED — Mini-side pickup + ingest |
|
|
64
|
+
| `src/mirror-sync.ts` | TO BE CREATED — Device-side mirror pull |
|
|
65
|
+
| `wrangler.toml` | TO BE SIMPLIFIED — R2 only, no D1/Vectorize |
|
|
66
|
+
| `schema.sql` | TO BE REMOVED — no database on Worker |
|
|
67
|
+
|
|
68
|
+
## Previous Branch
|
|
69
|
+
|
|
70
|
+
`cc-mba/phase2-worker` — contains the cloud mirror build. Preserved so people can see the progression from "persistent cloud DB" to "ephemeral encrypted relay." The thinking is the product.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# 2026-02-25 — Phase 2 Worker Build
|
|
2
|
+
|
|
3
|
+
**Agent:** cc-a (Claude Code on MacBook Air)
|
|
4
|
+
**Repo:** `memory-crystal`
|
|
5
|
+
**Branch:** `cc-mba/phase2-worker`
|
|
6
|
+
|
|
7
|
+
## What Was Done
|
|
8
|
+
|
|
9
|
+
### 1. Read & Understood the Entire Codebase
|
|
10
|
+
- `core.ts` (1,346 lines) — hybrid search: sqlite-vec + FTS5 + RRF fusion + recency weighting
|
|
11
|
+
- `mcp-server.ts` — 7 MCP tools for Claude Code (search, remember, forget, status, sources)
|
|
12
|
+
- `cc-hook.ts` — Stop hook with byte-offset watermarking, batched ingestion
|
|
13
|
+
- `openclaw.ts` — Lesa's agent_end hook with compaction detection
|
|
14
|
+
- `resolveConfig()` — 4-level config resolution (params → env → .env → 1Password)
|
|
15
|
+
|
|
16
|
+
### 2. Built the Cloudflare Worker (`src/worker.ts`)
|
|
17
|
+
- REST API endpoints: `/search`, `/ingest`, `/remember`, `/forget`, `/status`, `/health`
|
|
18
|
+
- Sync endpoints: `/sync/push` (Mini uploads snapshot to R2), `/sync/pull` (Mini pulls new remote writes)
|
|
19
|
+
- Auth: bearer token per agent (cc-a, cc, lesa) mapped to agent_id
|
|
20
|
+
- Search: FTS5 on D1 + Vectorize cosine similarity + RRF fusion + recency weighting
|
|
21
|
+
- Deduplication: SHA-256 hash check before ingest
|
|
22
|
+
- Embedding: OpenAI text-embedding-3-small via fetch
|
|
23
|
+
|
|
24
|
+
### 3. Created D1 Schema (`schema.sql`)
|
|
25
|
+
- `chunks` table with hash-based dedup, agent_id, source tracking
|
|
26
|
+
- `chunks_fts` virtual table (FTS5 with Porter stemming)
|
|
27
|
+
- Triggers to keep FTS in sync with chunks
|
|
28
|
+
- `memories` table for explicit remember/forget
|
|
29
|
+
- `capture_state` and `sync_log` for sync tracking
|
|
30
|
+
|
|
31
|
+
### 4. Created Wrangler Config (`wrangler.toml`)
|
|
32
|
+
- D1 binding (needs database_id after creation)
|
|
33
|
+
- Vectorize binding (memory-crystal-chunks, 1536 dimensions)
|
|
34
|
+
- R2 bucket binding (memory-crystal-snapshots)
|
|
35
|
+
- Secrets: AUTH_TOKEN_CC_A, AUTH_TOKEN_CC, AUTH_TOKEN_LESA, OPENAI_API_KEY
|
|
36
|
+
|
|
37
|
+
### 5. Added Remote Mode to Core (`core.ts`)
|
|
38
|
+
- New `RemoteCrystal` class — same interface as `Crystal`, talks to Worker via HTTP
|
|
39
|
+
- `createCrystal()` factory — returns `RemoteCrystal` when `remoteUrl` + `remoteToken` are set, otherwise local `Crystal`
|
|
40
|
+
- `chunkText()` method on RemoteCrystal for cc-hook compatibility
|
|
41
|
+
|
|
42
|
+
### 6. Updated MCP Server (`mcp-server.ts`)
|
|
43
|
+
- Uses `createCrystal()` — auto-detects remote mode
|
|
44
|
+
- Shows "(REMOTE)" in status when using cloud mirror
|
|
45
|
+
- Source indexing tools gracefully return "not available in remote mode"
|
|
46
|
+
|
|
47
|
+
### 7. Updated CC Hook (`cc-hook.ts`)
|
|
48
|
+
- Uses `createCrystal()` for remote ingestion support
|
|
49
|
+
- Configurable `CRYSTAL_AGENT_ID` env var (defaults to 'claude-code', set to 'cc-a' on MacBook Air)
|
|
50
|
+
- Type-safe with `Crystal | RemoteCrystal` union
|
|
51
|
+
|
|
52
|
+
### 8. Build Verification
|
|
53
|
+
- `npm run build:local` — succeeds, all types pass
|
|
54
|
+
- `npm run build:worker` — succeeds, worker.js = 12 KB
|
|
55
|
+
|
|
56
|
+
## Current Status
|
|
57
|
+
|
|
58
|
+
**Code: COMPLETE.** All source files written and building clean.
|
|
59
|
+
|
|
60
|
+
**Not yet deployed.** Needs Cloudflare resources created and secrets set.
|
|
61
|
+
|
|
62
|
+
## Files Changed/Created
|
|
63
|
+
|
|
64
|
+
| File | Action |
|
|
65
|
+
|------|--------|
|
|
66
|
+
| `src/worker.ts` | NEW — Cloudflare Worker |
|
|
67
|
+
| `schema.sql` | NEW — D1 database schema |
|
|
68
|
+
| `wrangler.toml` | NEW — Cloudflare config |
|
|
69
|
+
| `src/core.ts` | MODIFIED — added RemoteCrystal + createCrystal() |
|
|
70
|
+
| `src/mcp-server.ts` | MODIFIED — remote mode support |
|
|
71
|
+
| `src/cc-hook.ts` | MODIFIED — remote mode + configurable agent_id |
|
|
72
|
+
| `package.json` | MODIFIED — added build:worker and build:local scripts |
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Phase 2 Implementation
|
|
2
|
+
|
|
3
|
+
*Dev update by cc-mini, 2026-02-26 10:25 PST*
|
|
4
|
+
|
|
5
|
+
**Branch:** `mini/phase2-relay`
|
|
6
|
+
|
|
7
|
+
## What happened
|
|
8
|
+
|
|
9
|
+
Built and merged Memory Crystal Phase 2. The system now produces all three file types (JSONL transcripts, MD session summaries, vector DB) and manages the full `~/.ldm/` directory tree. Also merged cc-air's ephemeral relay code and expanded the poller to reconstruct remote agent file trees.
|
|
10
|
+
|
|
11
|
+
## New modules
|
|
12
|
+
|
|
13
|
+
- **`src/ldm.ts`** -- Central LDM path resolution and scaffolding. `getAgentId()`, `ldmPaths()`, `scaffoldLdm()`, `ensureLdm()`. Every other file imports paths from here.
|
|
14
|
+
- **`src/summarize.ts`** -- MD session summary generation. Two modes: `simple` (no API call, first message becomes title) and `llm` (calls gpt-4o-mini for title + summary + topics). Controlled by `CRYSTAL_SUMMARY_MODE` env var.
|
|
15
|
+
|
|
16
|
+
## Changes to existing modules
|
|
17
|
+
|
|
18
|
+
- **`src/core.ts`** -- `resolveConfig()` checks `~/.ldm/memory/crystal.db` first, falls back to legacy path. Added `RemoteCrystal` class and `createCrystal()` factory from relay merge.
|
|
19
|
+
- **`src/cli.ts`** -- Added `crystal init [--agent]` and `crystal migrate-db` subcommands. Init scaffolds the full directory tree. Migrate copies the DB, verifies chunk count, creates symlink.
|
|
20
|
+
- **`src/cc-hook.ts`** -- Three new behaviors after each capture: (1) archive JSONL transcript to `~/.ldm/agents/{id}/transcripts/`, (2) generate MD session summary to `sessions/`, (3) relay mode from cc-air merge (encrypt + drop at Worker when env vars set).
|
|
21
|
+
- **`src/poller.ts`** -- After decrypting relay drops, now reconstructs the remote agent's full file tree: JSONL transcript, MD summary, daily breadcrumb. This means the Mini has everything even if a device is lost.
|
|
22
|
+
- **`src/mirror-sync.ts`** -- Updated paths to use `ldmPaths()` instead of hardcoded `~/.openclaw/memory-crystal/`.
|
|
23
|
+
- **`src/dev-update.ts`** -- Writes to each repo's own `ai/` folder instead of centralized `wip-dev-updates`. Disabled the git add/commit/push to the old repo.
|
|
24
|
+
- **`src/mcp-server.ts`** -- Uses `createCrystal()` factory. Remote mode guards on source indexing operations.
|
|
25
|
+
|
|
26
|
+
## Portability fix
|
|
27
|
+
|
|
28
|
+
All hardcoded `/Users/lesa` and `/Users/parker` HOME fallbacks replaced with `process.env.HOME || ''` across every source file. The old fallbacks were cc-air writing `/Users/parker` and the Mini having `/Users/lesa`. Neither matters in practice (HOME is always set), but it was wrong for anyone else installing.
|
|
29
|
+
|
|
30
|
+
## Relay merge
|
|
31
|
+
|
|
32
|
+
Merged `origin/cc-air/phase2-relay` into the branch. cc-air built the ephemeral encrypted relay: `src/crypto.ts` (AES-256-GCM + HMAC), `src/worker.ts` (Cloudflare dead drop), `src/poller.ts` (Mini-side ingest), `src/mirror-sync.ts` (device-side DB pull). One conflict in cc-hook.ts (imports + dev-update section), resolved to keep both relay mode and the new summary/archive code.
|
|
33
|
+
|
|
34
|
+
## ai/ folder cleanup
|
|
35
|
+
|
|
36
|
+
Established the inbox convention: `ai/todos/inboxes/{recipient}/`. Each agent or human gets an inbox. Moved salience-research from `artifacts/` to `ai/notes/`. Added `PUNCHLIST.md` for blockers to ship. Updated `wip-dev-resources/DEVELOPMENT-PROCESS.md` with the new convention.
|
|
37
|
+
|
|
38
|
+
## Verified
|
|
39
|
+
|
|
40
|
+
- `npm run build` (local + worker): zero errors, 12 entry points
|
|
41
|
+
- `crystal status`: 159,574 chunks from existing DB
|
|
42
|
+
- `crystal init --agent cc-mini`: scaffolds `~/.ldm/` correctly
|
|
43
|
+
- `crystal search "test"`: returns results
|
|
44
|
+
- `node poller.js --status`: "not configured" (correct)
|
|
45
|
+
- `node mirror-sync.js --status`: shows state
|
|
46
|
+
|
|
47
|
+
## What's next
|
|
48
|
+
|
|
49
|
+
Parker's moves: run `crystal migrate-db`, deploy plugin, merge to main, release. Relay setup (Cloudflare) when ready. See `ai/todos/inboxes/parker/` and `ai/todos/PUNCHLIST.md`.
|