@wipcomputer/memory-crystal 0.7.10
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 +367 -0
- package/LICENSE +21 -0
- package/README-ENTERPRISE.md +226 -0
- package/README.md +127 -0
- package/RELAY.md +199 -0
- package/TECHNICAL.md +628 -0
- package/_trash/RELEASE-NOTES-v0-7-4.md +64 -0
- package/_trash/RELEASE-NOTES-v0-7-5.md +19 -0
- package/cloud/README.md +116 -0
- package/cloud/docs/gpt-system-instructions.md +69 -0
- package/cloud/migrations/0001_init.sql +52 -0
- package/dist/bridge.d.ts +7 -0
- package/dist/bridge.js +14 -0
- package/dist/bulk-copy.d.ts +17 -0
- package/dist/bulk-copy.js +90 -0
- package/dist/cc-hook.d.ts +8 -0
- package/dist/cc-hook.js +368 -0
- package/dist/cc-poller.d.ts +1 -0
- package/dist/cc-poller.js +550 -0
- package/dist/chunk-25LXQJ4Z.js +110 -0
- package/dist/chunk-2DRXIRQW.js +97 -0
- package/dist/chunk-2ZNH5F6E.js +1281 -0
- package/dist/chunk-3G3SFYYI.js +288 -0
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-3S6TI23B.js +97 -0
- package/dist/chunk-3VFIJYS4.js +818 -0
- package/dist/chunk-52QE3YI3.js +1169 -0
- package/dist/chunk-57RP3DIN.js +1205 -0
- package/dist/chunk-5HSZ4W2P.js +62 -0
- package/dist/chunk-645IPXW3.js +290 -0
- package/dist/chunk-7A7ELD4C.js +1205 -0
- package/dist/chunk-7FYY4GZM.js +1205 -0
- package/dist/chunk-7IUE7ODU.js +254 -0
- package/dist/chunk-7RMLKZIS.js +108 -0
- package/dist/chunk-AA3OPP4Z.js +432 -0
- package/dist/chunk-ASSZDR6I.js +108 -0
- package/dist/chunk-AYRJVWUC.js +1205 -0
- package/dist/chunk-CCYI5O3D.js +148 -0
- package/dist/chunk-D3I3ZSE2.js +411 -0
- package/dist/chunk-DACSKLY6.js +219 -0
- package/dist/chunk-DW5B4BL7.js +108 -0
- package/dist/chunk-EKSACBTJ.js +1070 -0
- package/dist/chunk-EXEZZADG.js +248 -0
- package/dist/chunk-F3Y7EL7K.js +83 -0
- package/dist/chunk-FHRZNOMW.js +1205 -0
- package/dist/chunk-IM7N24MT.js +129 -0
- package/dist/chunk-IPNYIXFK.js +1178 -0
- package/dist/chunk-J7MRSZIO.js +167 -0
- package/dist/chunk-JITKI2OI.js +106 -0
- package/dist/chunk-JWZXYVET.js +1068 -0
- package/dist/chunk-KCQUXVYT.js +108 -0
- package/dist/chunk-KOQ43OX6.js +1281 -0
- package/dist/chunk-KYVWO6ZM.js +1069 -0
- package/dist/chunk-L3VHARQH.js +413 -0
- package/dist/chunk-LBWDS6BE.js +288 -0
- package/dist/chunk-LOVAHSQV.js +411 -0
- package/dist/chunk-LQOYCAGG.js +446 -0
- package/dist/chunk-LWAIPJ2W.js +146 -0
- package/dist/chunk-M5DHKW7M.js +127 -0
- package/dist/chunk-MBKCIJHM.js +1328 -0
- package/dist/chunk-MK42FMEG.js +147 -0
- package/dist/chunk-MOBMYHKL.js +1205 -0
- package/dist/chunk-MPLTNMRG.js +67 -0
- package/dist/chunk-NIJCVN3O.js +147 -0
- package/dist/chunk-NZCFSZQ7.js +1205 -0
- package/dist/chunk-O2UITJGH.js +465 -0
- package/dist/chunk-OCRA44AZ.js +108 -0
- package/dist/chunk-P3KJR66H.js +117 -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-PNKVD2UK.js +26 -0
- package/dist/chunk-PSQZURHO.js +229 -0
- package/dist/chunk-SGL6ISBJ.js +1061 -0
- package/dist/chunk-SJABZZT5.js +97 -0
- package/dist/chunk-TD3P3K32.js +1199 -0
- package/dist/chunk-TMDZJJKV.js +288 -0
- package/dist/chunk-UNHVZB5G.js +411 -0
- package/dist/chunk-VAFTWSTE.js +1061 -0
- package/dist/chunk-VNFXFQBB.js +217 -0
- package/dist/chunk-X3GVFKSJ.js +1205 -0
- package/dist/chunk-XZ3S56RQ.js +1061 -0
- package/dist/chunk-Y72C7F6O.js +148 -0
- package/dist/chunk-YLICP577.js +1205 -0
- package/dist/chunk-YX6AXLVK.js +159 -0
- package/dist/chunk-ZCQYHTNU.js +146 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1105 -0
- package/dist/cloud-crystal.js +6 -0
- package/dist/core.d.ts +232 -0
- package/dist/core.js +12 -0
- package/dist/crypto.d.ts +20 -0
- package/dist/crypto.js +27 -0
- package/dist/crystal-capture.sh +29 -0
- package/dist/crystal-serve.d.ts +4 -0
- package/dist/crystal-serve.js +252 -0
- package/dist/dev-update-SZ2Z4WCQ.js +6 -0
- package/dist/discover.d.ts +30 -0
- package/dist/discover.js +177 -0
- package/dist/doctor.d.ts +9 -0
- package/dist/doctor.js +334 -0
- package/dist/dream-weaver.d.ts +8 -0
- package/dist/dream-weaver.js +56 -0
- package/dist/file-sync.d.ts +48 -0
- package/dist/file-sync.js +18 -0
- package/dist/installer.d.ts +61 -0
- package/dist/installer.js +618 -0
- package/dist/ldm-backup.sh +116 -0
- package/dist/ldm.d.ts +50 -0
- package/dist/ldm.js +32 -0
- package/dist/mcp-server.d.ts +1 -0
- package/dist/mcp-server.js +265 -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 +159 -0
- package/dist/oc-backfill.d.ts +19 -0
- package/dist/oc-backfill.js +74 -0
- package/dist/openclaw.d.ts +5 -0
- package/dist/openclaw.js +423 -0
- package/dist/pair.d.ts +4 -0
- package/dist/pair.js +75 -0
- package/dist/poller.d.ts +1 -0
- package/dist/poller.js +634 -0
- package/dist/role.d.ts +24 -0
- package/dist/role.js +13 -0
- package/dist/search-pipeline-4K4OJSSS.js +255 -0
- package/dist/search-pipeline-4PRS6LI7.js +280 -0
- package/dist/search-pipeline-7UJMXPLO.js +280 -0
- package/dist/search-pipeline-DQTRLGBH.js +74 -0
- package/dist/search-pipeline-HNG37REH.js +282 -0
- package/dist/search-pipeline-IZFPLBUB.js +280 -0
- package/dist/search-pipeline-MID6F26Q.js +73 -0
- package/dist/search-pipeline-N52JZFNN.js +282 -0
- package/dist/search-pipeline-OPB2PRQQ.js +280 -0
- package/dist/search-pipeline-VXTE5HAD.js +262 -0
- package/dist/staging.d.ts +29 -0
- package/dist/staging.js +21 -0
- package/dist/summarize.d.ts +19 -0
- package/dist/summarize.js +10 -0
- package/dist/worker-demo.js +186 -0
- package/dist/worker-mcp.js +404 -0
- package/dist/worker.js +137 -0
- package/migrations/0001_init.sql +51 -0
- package/migrations/0002_cloud_storage.sql +49 -0
- package/openclaw.plugin.json +11 -0
- package/package.json +57 -0
- package/scripts/crystal-capture 2.sh +29 -0
- package/scripts/crystal-capture.sh +29 -0
- package/scripts/deploy-cloud 2.sh +153 -0
- package/scripts/deploy-cloud.sh +153 -0
- package/scripts/ldm-backup.sh +116 -0
- package/scripts/migrate-lance-to-sqlite.mjs +217 -0
- package/skills/memory/SKILL.md +427 -0
- package/wrangler-demo.toml +8 -0
- package/wrangler-mcp.toml +24 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Memory Crystal v0.7.4 ... MCP Fix + AgentId Config
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-03-11
|
|
4
|
+
**Authors:** Parker Todd Brooks, Lēsa, Claude Code
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## What's in this release
|
|
9
|
+
|
|
10
|
+
### Agent identity reads from config, not hardcoded strings
|
|
11
|
+
|
|
12
|
+
The agent_id used when ingesting conversations was hardcoded in three places: `cc-mini` in the CC hook, `main` in the OpenClaw plugin, and `cc-mini` as the fallback in `ldm.ts`. This caused ID drift. The same agent got recorded under multiple IDs, and we had to manually merge 141K+ chunks in the database.
|
|
13
|
+
|
|
14
|
+
Now `getAgentId()` scans `~/.ldm/agents/*/config.json` for a matching harness type. The CC hook passes `'claude-code'`, the OC plugin passes `'openclaw'`, and the config file is the source of truth. `CRYSTAL_AGENT_ID` env var still works as an override.
|
|
15
|
+
|
|
16
|
+
New exports: `AgentConfig`, `loadAgentConfig()`, `saveAgentConfig()`. The installer writes `agentId` to config.json during `crystal init`.
|
|
17
|
+
|
|
18
|
+
**Closes #33.**
|
|
19
|
+
|
|
20
|
+
### MCP registrations moved to user-level
|
|
21
|
+
|
|
22
|
+
MCP server registrations moved from project-level `~/.openclaw/.mcp.json` to user-level `~/.claude.json`. The old file was a Claude Code convention that only loaded when running from `~/.openclaw/`. Now all 4 MCP servers (memory-crystal, lesa-bridge, wip-agent-pay, wip-repos) load from any directory as "User MCPs".
|
|
23
|
+
|
|
24
|
+
OpenClaw doesn't read `.mcp.json` at all. It uses its own plugin system. The file was moved to `~/.openclaw/_trash/`.
|
|
25
|
+
|
|
26
|
+
### OPENCLAW_HOME env var fix (v0.7.3)
|
|
27
|
+
|
|
28
|
+
The MCP server registration was missing the `OPENCLAW_HOME` env var. Without it, the memory-crystal MCP server couldn't find Lēsa's OpenClaw installation for private-mode checks. Fixed in v0.7.3, deployed in this release.
|
|
29
|
+
|
|
30
|
+
### Branch cleanup
|
|
31
|
+
|
|
32
|
+
33 stale branches renamed with `--merged-` suffix. Zero active branches besides main.
|
|
33
|
+
|
|
34
|
+
### QMD v1.1.6 analysis documented
|
|
35
|
+
|
|
36
|
+
Deep analysis of the search quality system with four recommendations: intent parameter for search, structured search API, persistent reranker cache, and explain mode for debugging. See `ai/product/notes/2026-03-09--cc-mini--qmd-v1.1.6-analysis-and-recommendations.md`.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Files changed
|
|
41
|
+
|
|
42
|
+
| File | What |
|
|
43
|
+
|------|------|
|
|
44
|
+
| `src/ldm.ts` | `AgentConfig` interface, `loadAgentConfig()`, `saveAgentConfig()`, `getAgentId()` now scans config |
|
|
45
|
+
| `src/cc-hook.ts` | Uses `getAgentId('claude-code')` instead of hardcoded fallback |
|
|
46
|
+
| `src/openclaw.ts` | Uses `OC_AGENT_ID` instead of `'main'` fallback |
|
|
47
|
+
| `src/installer.ts` | Writes `agentId` to config.json during install |
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install -g memory-crystal@0.7.4
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or update your local clone:
|
|
58
|
+
```bash
|
|
59
|
+
git pull origin main
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
Built by Parker Todd Brooks, Lēsa (OpenClaw, Claude Opus 4.6), Claude Code (Claude Opus 4.6).
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Release Notes: Memory Crystal v0.7.5
|
|
2
|
+
|
|
3
|
+
## LDM OS Integration
|
|
4
|
+
|
|
5
|
+
Memory Crystal now works with LDM OS when it's available.
|
|
6
|
+
|
|
7
|
+
### crystal init delegates to ldm install
|
|
8
|
+
|
|
9
|
+
When the `ldm` CLI exists on PATH, `crystal init` delegates generic deployment to it. LDM OS handles the scaffold, interface detection, and extension deployment. Memory Crystal keeps its own setup: database backup, role configuration, pairing, cron jobs.
|
|
10
|
+
|
|
11
|
+
When `ldm` isn't available, `crystal init` works standalone like it always has. No new dependencies. No breaking changes.
|
|
12
|
+
|
|
13
|
+
### LDM OS tip
|
|
14
|
+
|
|
15
|
+
After install completes, Memory Crystal prints a tip: "Run `ldm install` to see more skills you can add." Helps users discover the rest of the ecosystem.
|
|
16
|
+
|
|
17
|
+
### Part of LDM OS
|
|
18
|
+
|
|
19
|
+
README now includes a "Part of LDM OS" section linking back to the LDM OS repo. Memory Crystal installs into LDM OS, the local runtime for AI agents.
|
package/cloud/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Memory Crystal Cloud
|
|
2
|
+
|
|
3
|
+
Remote MCP server for ChatGPT + Claude. Persistent memory across sessions on all six surfaces (macOS, iOS, web for both ChatGPT and Claude).
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
One MCP server, two tiers:
|
|
8
|
+
|
|
9
|
+
**Tier 1 (Sovereign):** Write-only relay. Memories are encrypted (AES-256-GCM) and relayed to your home machine. The cloud cannot read your data. Search is available only on local devices.
|
|
10
|
+
|
|
11
|
+
**Tier 2 (Convenience):** Cloud search enabled. A mirror of your memory database is pushed to Cloudflare D1 + Vectorize. ChatGPT/Claude can search your memories directly. Privacy trade-off is clearly disclosed.
|
|
12
|
+
|
|
13
|
+
## Data Flow
|
|
14
|
+
|
|
15
|
+
Every conversation turn, attachment, and explicit memory flows to the Mini:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
ChatGPT/Claude → memory_log (every turn) → [encrypt] → R2 relay → Mini poller → crystal.db
|
|
19
|
+
memory_remember (explicit) → [encrypt] → R2 relay → Mini poller → crystal.db
|
|
20
|
+
memory_upload (files/media) → [encrypt] → R2 relay → Mini poller → attachments/
|
|
21
|
+
memory_forget (deprecation) → [encrypt] → R2 relay → Mini poller → crystal.db
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The Mini receives the same data it would have if this was a local Claude Code or OpenClaw session:
|
|
25
|
+
- Full conversation JSON (raw messages, tool calls, results)
|
|
26
|
+
- Inline images and file references
|
|
27
|
+
- Binary attachments (images, audio, video, documents)
|
|
28
|
+
- Explicit memories and deprecations
|
|
29
|
+
|
|
30
|
+
## Setup
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# From the memory-crystal-private root:
|
|
34
|
+
|
|
35
|
+
# 1. Create D1 database
|
|
36
|
+
cd cloud && npx wrangler d1 create memory-crystal-cloud
|
|
37
|
+
# Copy the database_id into cloud/wrangler.toml
|
|
38
|
+
|
|
39
|
+
# 2. Run migrations
|
|
40
|
+
npm run cloud:db:migrate
|
|
41
|
+
|
|
42
|
+
# 3. Set secrets
|
|
43
|
+
cd cloud
|
|
44
|
+
npx wrangler secret put CRYSTAL_RELAY_KEY # base64, 32 bytes (same as relay)
|
|
45
|
+
npx wrangler secret put OAUTH_SIGNING_SECRET # any random string
|
|
46
|
+
npx wrangler secret put OPENAI_API_KEY # for Tier 2 embeddings
|
|
47
|
+
|
|
48
|
+
# 4. Deploy
|
|
49
|
+
npm run cloud:deploy
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Development
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm run cloud:dev # local dev server
|
|
56
|
+
npm run cloud:db:migrate # apply D1 migrations locally
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Endpoints
|
|
60
|
+
|
|
61
|
+
| Path | Method | Auth | Description |
|
|
62
|
+
|------|--------|------|-------------|
|
|
63
|
+
| `/health` | GET | No | Health check |
|
|
64
|
+
| `/.well-known/oauth-protected-resource` | GET | No | OAuth resource metadata |
|
|
65
|
+
| `/.well-known/oauth-authorization-server` | GET | No | OAuth server metadata |
|
|
66
|
+
| `/oauth/register` | POST | No | Dynamic Client Registration |
|
|
67
|
+
| `/oauth/authorize` | GET/POST | No | Authorization + consent page |
|
|
68
|
+
| `/oauth/token` | POST | No | Token exchange (PKCE S256) |
|
|
69
|
+
| `/mcp` | POST | Bearer | MCP JSON-RPC endpoint |
|
|
70
|
+
|
|
71
|
+
## MCP Tools
|
|
72
|
+
|
|
73
|
+
| Tool | Description | Annotation |
|
|
74
|
+
|------|-------------|-----------|
|
|
75
|
+
| `memory_search` | Search memories | readOnly |
|
|
76
|
+
| `memory_remember` | Store a memory (fact, preference, event, opinion, skill) | write |
|
|
77
|
+
| `memory_forget` | Deprecate a memory by ID | destructive |
|
|
78
|
+
| `memory_status` | Show status, pending drops, tier info | readOnly |
|
|
79
|
+
| `memory_log` | Log a conversation turn (call every exchange) | write, idempotent |
|
|
80
|
+
| `memory_upload` | Upload a file attachment (image, audio, video, doc) | write |
|
|
81
|
+
|
|
82
|
+
### memory_log
|
|
83
|
+
|
|
84
|
+
This is the key tool for full data capture. The system prompt instructs the AI to call `memory_log` after every exchange with:
|
|
85
|
+
- `role` + `content` (the text)
|
|
86
|
+
- `raw_json` (the complete message object as JSON string)
|
|
87
|
+
- `tool_calls` (any tools called in the turn)
|
|
88
|
+
- `attachments` (inline images, files, audio)
|
|
89
|
+
- `session_id` + `turn_index` (for conversation grouping)
|
|
90
|
+
|
|
91
|
+
The Mini processes these through the same chunking and embedding pipeline used for local sessions.
|
|
92
|
+
|
|
93
|
+
### memory_upload
|
|
94
|
+
|
|
95
|
+
Handles binary files. The AI encodes the file as base64 and calls this tool. The file is encrypted (AES-256-GCM) and stored as a separate R2 object. A metadata drop references the blob. The Mini poller picks up both.
|
|
96
|
+
|
|
97
|
+
Supports: images (png, jpg, webp, gif), audio (mp3, mp4, wav, ogg), video (mp4, webm, mov), documents (pdf, txt, csv).
|
|
98
|
+
|
|
99
|
+
Max file size: 100MB (R2 single-put limit).
|
|
100
|
+
|
|
101
|
+
## Files
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
cloud/
|
|
105
|
+
wrangler.toml -- Cloudflare Worker config
|
|
106
|
+
migrations/
|
|
107
|
+
0001_init.sql -- D1 schema (OAuth + users)
|
|
108
|
+
README.md -- this file
|
|
109
|
+
|
|
110
|
+
src/cloud/
|
|
111
|
+
index.ts -- Worker entry point, request router
|
|
112
|
+
auth.ts -- OAuth 2.1 (DCR, authorize, token)
|
|
113
|
+
mcp.ts -- MCP tool definitions + handlers (6 tools)
|
|
114
|
+
relay.ts -- Encrypt + drop to relay (Web Crypto)
|
|
115
|
+
types.ts -- Shared types
|
|
116
|
+
```
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Memory Crystal - System Instructions
|
|
2
|
+
|
|
3
|
+
These instructions are for the Custom GPT / Claude connector that uses the Memory Crystal MCP server.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Identity
|
|
8
|
+
|
|
9
|
+
You are Memory Crystal, a persistent memory layer for your conversations. Every message in this conversation is captured and sent to the user's home machine, where it becomes part of their searchable memory.
|
|
10
|
+
|
|
11
|
+
## Core Behavior
|
|
12
|
+
|
|
13
|
+
### 1. Log Every Turn
|
|
14
|
+
|
|
15
|
+
After EVERY user message and EVERY assistant response, call `memory_log` with:
|
|
16
|
+
|
|
17
|
+
- `role`: "user" or "assistant"
|
|
18
|
+
- `content`: the full message text
|
|
19
|
+
- `session_id`: generate a unique ID at the start of each conversation (e.g. UUID) and reuse it for all turns
|
|
20
|
+
- `turn_index`: increment from 0 for each message in the conversation
|
|
21
|
+
- `model`: your model name (assistant turns only, e.g. "gpt-4o")
|
|
22
|
+
|
|
23
|
+
If the user's message includes images, files, or audio:
|
|
24
|
+
- Include them in the `attachments` array with `type`, `filename`, and `mime_type`
|
|
25
|
+
- For small files (under 64KB), include `data_base64`
|
|
26
|
+
- For larger files, note the reference in `context`
|
|
27
|
+
|
|
28
|
+
If your response includes tool calls:
|
|
29
|
+
- Include them in the `tool_calls` array with `tool_name`, `arguments`, and `result`
|
|
30
|
+
|
|
31
|
+
### 2. Remember When Asked
|
|
32
|
+
|
|
33
|
+
When the user says things like "remember this", "save this", "note that":
|
|
34
|
+
- Call `memory_remember` with the specific fact/preference/observation
|
|
35
|
+
- Choose the right category: fact, preference, event, opinion, skill
|
|
36
|
+
|
|
37
|
+
### 3. Forget When Asked
|
|
38
|
+
|
|
39
|
+
When the user says "forget that" or "remove that memory":
|
|
40
|
+
- Call `memory_forget` with the memory ID
|
|
41
|
+
|
|
42
|
+
### 4. Search When Asked
|
|
43
|
+
|
|
44
|
+
When the user asks "do you remember...", "what did I say about...", "find my notes on...":
|
|
45
|
+
- Call `memory_search` with their query
|
|
46
|
+
- If in Sovereign mode, explain that search is available on their local devices
|
|
47
|
+
|
|
48
|
+
### 5. Upload Files
|
|
49
|
+
|
|
50
|
+
When the user shares images, documents, audio, or video:
|
|
51
|
+
- Call `memory_upload` with the file data (base64 encoded)
|
|
52
|
+
- Include `filename`, `mime_type`, and optional `context`
|
|
53
|
+
- This ensures all media gets saved to their home machine
|
|
54
|
+
|
|
55
|
+
## Important Rules
|
|
56
|
+
|
|
57
|
+
- ALWAYS log both sides of the conversation. Never skip a turn.
|
|
58
|
+
- The `session_id` must be consistent within a single conversation.
|
|
59
|
+
- Do not mention the logging process unless the user asks about it.
|
|
60
|
+
- When Memory Crystal is in Sovereign mode, search returns a guidance message. This is by design. The user's data stays on their hardware.
|
|
61
|
+
- Be concise. The memory system captures everything automatically.
|
|
62
|
+
|
|
63
|
+
## Status
|
|
64
|
+
|
|
65
|
+
Call `memory_status` when the user asks about their memory system, connection health, or tier information.
|
|
66
|
+
|
|
67
|
+
## Privacy
|
|
68
|
+
|
|
69
|
+
All data is encrypted in transit (AES-256-GCM). In Sovereign mode, the cloud cannot read any data. Memories flow to the user's home machine and are deleted from the relay after pickup.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
-- Memory Crystal Cloud: initial schema
|
|
2
|
+
-- OAuth tables for auth + user accounts
|
|
3
|
+
-- Tier 1: relay-only (sovereign)
|
|
4
|
+
-- Tier 2: adds chunks + memories tables (separate migration)
|
|
5
|
+
|
|
6
|
+
-- ── OAuth Clients (Dynamic Client Registration) ──
|
|
7
|
+
CREATE TABLE IF NOT EXISTS oauth_clients (
|
|
8
|
+
client_id TEXT PRIMARY KEY,
|
|
9
|
+
redirect_uris TEXT NOT NULL DEFAULT '[]',
|
|
10
|
+
client_name TEXT,
|
|
11
|
+
created_at TEXT NOT NULL,
|
|
12
|
+
last_used_at TEXT
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
-- ── Authorization Codes (PKCE flow) ──
|
|
16
|
+
CREATE TABLE IF NOT EXISTS authorization_codes (
|
|
17
|
+
code TEXT PRIMARY KEY,
|
|
18
|
+
client_id TEXT NOT NULL,
|
|
19
|
+
user_id TEXT NOT NULL,
|
|
20
|
+
code_challenge TEXT NOT NULL,
|
|
21
|
+
code_challenge_method TEXT NOT NULL DEFAULT 'S256',
|
|
22
|
+
redirect_uri TEXT NOT NULL,
|
|
23
|
+
scope TEXT,
|
|
24
|
+
expires_at TEXT NOT NULL,
|
|
25
|
+
used INTEGER DEFAULT 0
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
-- ── Access Tokens ──
|
|
29
|
+
CREATE TABLE IF NOT EXISTS access_tokens (
|
|
30
|
+
token_hash TEXT PRIMARY KEY,
|
|
31
|
+
client_id TEXT NOT NULL,
|
|
32
|
+
user_id TEXT NOT NULL,
|
|
33
|
+
scope TEXT,
|
|
34
|
+
tier TEXT NOT NULL DEFAULT 'sovereign',
|
|
35
|
+
expires_at TEXT NOT NULL,
|
|
36
|
+
created_at TEXT NOT NULL
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
-- ── Users ──
|
|
40
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
41
|
+
user_id TEXT PRIMARY KEY,
|
|
42
|
+
email TEXT NOT NULL UNIQUE,
|
|
43
|
+
tier TEXT NOT NULL DEFAULT 'sovereign',
|
|
44
|
+
relay_token TEXT,
|
|
45
|
+
created_at TEXT NOT NULL
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
-- ── Indexes ──
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_auth_codes_client ON authorization_codes(client_id);
|
|
50
|
+
CREATE INDEX IF NOT EXISTS idx_tokens_user ON access_tokens(user_id);
|
|
51
|
+
CREATE INDEX IF NOT EXISTS idx_tokens_expires ON access_tokens(expires_at);
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_clients_last_used ON oauth_clients(last_used_at);
|
package/dist/bridge.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
declare function isBridgeInstalled(): boolean;
|
|
2
|
+
declare function isBridgeRegistered(): boolean;
|
|
3
|
+
declare function isBridgeDesktopRegistered(): boolean;
|
|
4
|
+
declare function registerBridgeMcp(): void;
|
|
5
|
+
declare function registerBridgeDesktop(): boolean;
|
|
6
|
+
|
|
7
|
+
export { isBridgeDesktopRegistered, isBridgeInstalled, isBridgeRegistered, registerBridgeDesktop, registerBridgeMcp };
|
package/dist/bridge.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isBridgeDesktopRegistered,
|
|
3
|
+
isBridgeInstalled,
|
|
4
|
+
isBridgeRegistered,
|
|
5
|
+
registerBridgeDesktop,
|
|
6
|
+
registerBridgeMcp
|
|
7
|
+
} from "./chunk-MPLTNMRG.js";
|
|
8
|
+
export {
|
|
9
|
+
isBridgeDesktopRegistered,
|
|
10
|
+
isBridgeInstalled,
|
|
11
|
+
isBridgeRegistered,
|
|
12
|
+
registerBridgeDesktop,
|
|
13
|
+
registerBridgeMcp
|
|
14
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface BulkCopyResult {
|
|
2
|
+
filesCopied: number;
|
|
3
|
+
filesSkipped: number;
|
|
4
|
+
bytesWritten: number;
|
|
5
|
+
durationMs: number;
|
|
6
|
+
}
|
|
7
|
+
interface BulkCopyOptions {
|
|
8
|
+
workspace?: boolean;
|
|
9
|
+
workspaceSrc?: string;
|
|
10
|
+
}
|
|
11
|
+
/** Copy session JSONL files from source locations to LDM transcripts.
|
|
12
|
+
* sessionPaths: array of absolute paths to JSONL files.
|
|
13
|
+
* agentId: target agent in ~/.ldm/agents/{agentId}/.
|
|
14
|
+
* Returns copy stats. */
|
|
15
|
+
declare function bulkCopyToLdm(sessionPaths: string[], agentId: string, options?: BulkCopyOptions): BulkCopyResult;
|
|
16
|
+
|
|
17
|
+
export { type BulkCopyOptions, type BulkCopyResult, bulkCopyToLdm };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ldmPaths
|
|
3
|
+
} from "./chunk-EXEZZADG.js";
|
|
4
|
+
|
|
5
|
+
// src/bulk-copy.ts
|
|
6
|
+
import { existsSync, mkdirSync, copyFileSync, statSync, readdirSync } from "fs";
|
|
7
|
+
import { join, basename } from "path";
|
|
8
|
+
function bulkCopyToLdm(sessionPaths, agentId, options) {
|
|
9
|
+
const start = Date.now();
|
|
10
|
+
const paths = ldmPaths(agentId);
|
|
11
|
+
let filesCopied = 0;
|
|
12
|
+
let filesSkipped = 0;
|
|
13
|
+
let bytesWritten = 0;
|
|
14
|
+
mkdirSync(paths.transcripts, { recursive: true });
|
|
15
|
+
for (const srcPath of sessionPaths) {
|
|
16
|
+
const destPath = join(paths.transcripts, basename(srcPath));
|
|
17
|
+
if (existsSync(destPath)) {
|
|
18
|
+
try {
|
|
19
|
+
const srcSize = statSync(srcPath).size;
|
|
20
|
+
const destSize = statSync(destPath).size;
|
|
21
|
+
if (srcSize === destSize) {
|
|
22
|
+
filesSkipped++;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
copyFileSync(srcPath, destPath);
|
|
30
|
+
bytesWritten += statSync(destPath).size;
|
|
31
|
+
filesCopied++;
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (options?.workspace && options.workspaceSrc) {
|
|
36
|
+
mkdirSync(paths.workspace, { recursive: true });
|
|
37
|
+
const wsResult = copyWorkspaceRecursive(options.workspaceSrc, paths.workspace);
|
|
38
|
+
filesCopied += wsResult.copied;
|
|
39
|
+
filesSkipped += wsResult.skipped;
|
|
40
|
+
bytesWritten += wsResult.bytes;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
filesCopied,
|
|
44
|
+
filesSkipped,
|
|
45
|
+
bytesWritten,
|
|
46
|
+
durationMs: Date.now() - start
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function copyWorkspaceRecursive(srcDir, destDir) {
|
|
50
|
+
let copied = 0;
|
|
51
|
+
let skipped = 0;
|
|
52
|
+
let bytes = 0;
|
|
53
|
+
try {
|
|
54
|
+
for (const entry of readdirSync(srcDir)) {
|
|
55
|
+
if (entry.startsWith(".")) continue;
|
|
56
|
+
const srcPath = join(srcDir, entry);
|
|
57
|
+
const destPath = join(destDir, entry);
|
|
58
|
+
try {
|
|
59
|
+
const stat = statSync(srcPath);
|
|
60
|
+
if (stat.isDirectory()) {
|
|
61
|
+
mkdirSync(destPath, { recursive: true });
|
|
62
|
+
const sub = copyWorkspaceRecursive(srcPath, destPath);
|
|
63
|
+
copied += sub.copied;
|
|
64
|
+
skipped += sub.skipped;
|
|
65
|
+
bytes += sub.bytes;
|
|
66
|
+
} else if (entry.endsWith(".md")) {
|
|
67
|
+
if (existsSync(destPath)) {
|
|
68
|
+
try {
|
|
69
|
+
if (stat.size === statSync(destPath).size) {
|
|
70
|
+
skipped++;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
copyFileSync(srcPath, destPath);
|
|
77
|
+
bytes += stat.size;
|
|
78
|
+
copied++;
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
return { copied, skipped, bytes };
|
|
87
|
+
}
|
|
88
|
+
export {
|
|
89
|
+
bulkCopyToLdm
|
|
90
|
+
};
|