Skip to content

Commit ce9606a

Browse files
committed
v1.2 (beta)
1 parent 62ff53d commit ce9606a

56 files changed

Lines changed: 9552 additions & 208 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -111,49 +111,75 @@ LOCAL_MODEL_PATH=/path/to/your/local/model
111111
# --------------------------------------------
112112

113113
# ============================================
114-
# PERFORMANCE TIER (Auto-detected or Manual)
114+
# PERFORMANCE TIER (Manual Configuration Required)
115115
# ============================================
116-
# OpenMemory automatically detects your hardware and selects the optimal tier.
117-
# You can override this by setting OM_TIER manually.
116+
# OpenMemory requires you to manually set the performance tier.
117+
# Set OM_TIER to one of: hybrid, fast, smart, or deep
118118
#
119119
# Available Tiers:
120120
#
121+
# HYBRID - Keyword + Synthetic embeddings (256-dim) with BM25 ranking
122+
# • Recall: ~100% (exact keyword matching) • QPS: 800-1000 • RAM: 0.5GB/10k memories
123+
# • Best for: Exact searches, documentation, code search, personal knowledge
124+
# • Features: Exact phrase matching, BM25 scoring, n-gram matching, 100% accuracy
125+
# • Use when: You need guaranteed exact matches and keyword-based retrieval
126+
#
121127
# FAST - Synthetic embeddings only (256-dim)
122128
# • Recall: ~70-75% • QPS: 700-850 • RAM: 0.6GB/10k memories
123129
# • Best for: Local apps, VS Code extensions, low-end hardware
124-
#Auto-selected: < 4 CPU cores or < 8GB RAM
130+
#Use when: < 4 CPU cores or < 8GB RAM
125131
#
126132
# SMART - Hybrid embeddings (256-dim synthetic + 128-dim compressed semantic = 384-dim)
127133
# • Recall: ~85% • QPS: 500-600 • RAM: 0.9GB/10k memories
128134
# • Best for: Production servers, AI copilots, mid-range hardware
129-
#Auto-selected: 4-7 CPU cores and 8-15GB RAM
135+
#Use when: 4-7 CPU cores and 8-15GB RAM
130136
#
131137
# DEEP - Full AI embeddings (1536-dim OpenAI/Gemini)
132138
# • Recall: ~95-100% • QPS: 350-400 • RAM: 1.6GB/10k memories
133-
# • Best for: Cloud deployments, high-accuracy systems, research
134-
#Auto-selected: 8+ CPU cores and 16+ GB RAM
139+
# • Best for: Cloud deployments, high-accuracy systems, semantic research
140+
#Use when: 8+ CPU cores and 16+ GB RAM
135141
#
136-
# Leave commented to auto-detect, or set manually:
137-
# OM_TIER=fast
138-
# OM_TIER=smart
139-
# OM_TIER=deep
140-
141-
OM_MIN_SCORE=0.3
142-
OM_DECAY_LAMBDA=0.02
142+
# REQUIRED: Set your tier (no auto-detection):
143+
OM_TIER=hybrid
143144

144-
# Decay interval in minutes
145-
# Testing: 0.5 (30s) for rapid benchmarks
146-
# Development: 5 (5min) for realistic decay testing
147-
# Production: 10 (10min) for optimal throughput (3% batch = less disk pressure)
148-
# Recommended: 5-10 minutes to balance decay accuracy vs overhead
149-
OM_DECAY_INTERVAL_MINUTES=10
145+
# Keyword Matching Settings (HYBRID tier only)
146+
# Boost multiplier for keyword matches (default: 2.5)
147+
OM_KEYWORD_BOOST=2.5
148+
# Minimum keyword length for matching (default: 3)
149+
OM_KEYWORD_MIN_LENGTH=3
150150

151-
# Decay ratio (percentage of memories to decay per run, 0.01-0.1)
152-
# Lower = more stable variance, higher = faster decay propagation
153-
OM_DECAY_RATIO=0.03
151+
OM_MIN_SCORE=0.3
154152

155-
# Sleep between segment processing (ms) to avoid lock contention
156-
OM_DECAY_SLEEP_MS=200
153+
# ============================================
154+
# Smart Decay Settings (Time-Based Algorithm)
155+
# ============================================
156+
# Decay interval in minutes - how often the decay cycle runs
157+
# The new algorithm uses time-based decay with daily lambda rates (hot=0.005/day, warm=0.02/day, cold=0.05/day)
158+
# Unlike batch-based systems, running more frequently doesn't increase decay speed
159+
# Decay is calculated from: decay_factor = exp(-lambda * days_since_access / (salience + 0.1))
160+
#
161+
# Recommended intervals:
162+
# • Testing: 30 minutes (for rapid validation)
163+
# • Development: 60-120 minutes (balanced testing)
164+
# • Production: 120-180 minutes (optimal - captures meaningful decay deltas while minimizing overhead)
165+
#
166+
# At 2-3 hours: hot tier decays ~0.04-0.06%, warm ~0.16-0.24%, cold ~0.4-0.6% per cycle
167+
OM_DECAY_INTERVAL_MINUTES=120
168+
169+
# Number of parallel decay worker threads (default: 3)
170+
OM_DECAY_THREADS=3
171+
# Cold tier threshold - memories below this salience get fingerprinted (default: 0.25)
172+
OM_DECAY_COLD_THRESHOLD=0.25
173+
# Reinforce memory salience when queried (default: true)
174+
OM_DECAY_REINFORCE_ON_QUERY=true
175+
# Enable regeneration of cold memories on query hits (default: true)
176+
OM_REGENERATION_ENABLED=true
177+
# Maximum vector dimensions (default: 1536)
178+
OM_MAX_VECTOR_DIM=1536
179+
# Minimum vector dimensions for compression (default: 64)
180+
OM_MIN_VECTOR_DIM=64
181+
# Number of summary compression layers 1-3 (default: 3)
182+
OM_SUMMARY_LAYERS=3
157183

158184
# Full Semantic Graph MVP Settings
159185
# Use summary-only storage (≤300 chars, intelligent extraction)

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,3 +360,4 @@ dist
360360
!.yarn/versions
361361

362362
benchmark
363+
temp

CHANGELOG.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
# Changelog
22

3-
## [Unreleased]
3+
## 1.2
44

55
### Added
66

7+
- **HYBRID Tier Performance Mode**
8+
9+
- New tier achieving 100% keyword match accuracy with synthetic embeddings
10+
- BM25 scoring algorithm for relevance ranking
11+
- Exact phrase matching with case-insensitive search
12+
- N-gram keyword extraction (unigrams, bigrams, trigrams)
13+
- Performance: 800-1000 QPS, 0.5GB RAM per 10k memories
14+
- Configurable via `OM_TIER=hybrid`, `OM_KEYWORD_BOOST`, `OM_KEYWORD_MIN_LENGTH`
15+
- Best for: Documentation search, code search, technical references
16+
717
- **Memory Compression Engine**: Auto-compresses chat/memory content to reduce tokens and latency
818

919
- 5 compression algorithms: whitespace, filler, semantic, aggressive, balanced
@@ -50,6 +60,23 @@
5060

5161
### Fixed
5262

63+
- **MCP Tool Names (Breaking Change)**: Changed from dot notation to underscores for Windsurf IDE compatibility
64+
65+
- `openmemory.query``openmemory_query`
66+
- `openmemory.store``openmemory_store`
67+
- `openmemory.reinforce``openmemory_reinforce`
68+
- `openmemory.list``openmemory_list`
69+
- `openmemory.get``openmemory_get`
70+
- Complies with MCP naming rule: `^[a-zA-Z0-9_-]{1,64}$`
71+
72+
- **PostgreSQL Custom Table Name**: Fixed hardcoded `memories` table in `openmemory://config` resource
73+
- Now correctly uses `OM_PG_TABLE` environment variable
74+
- Exports `memories_table` from database module with fully-qualified name
75+
- Fixes "relation 'memories' does not exist" error with custom table names
76+
- Works for both PostgreSQL (with schema) and SQLite
77+
78+
### Fixed
79+
5380
- VS Code extension connection issues (health endpoint)
5481
- MCP protocol integration for AI tools
5582
- Extension now properly passes MCP flag to all writers

IDE/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ Backend server required.
2727

2828
- `openmemory.backendUrl`: Backend URL (default: `http://localhost:8080`)
2929
- `openmemory.apiKey`: API key for auth (optional)
30-
- `openmemory.useMCP`: Use MCP protocol mode (default: `false`)
30+
- `openmemory.useMCP`: Use MCP protocol mode (default: `false`) - connects to backend MCP server with tools: `openmemory_query`, `openmemory_store`, `openmemory_list`, `openmemory_get`, `openmemory_reinforce`
31+
- `openmemory.mcpServerPath`: Path to backend MCP server (default: `backend/dist/ai/mcp.js`)
3132

3233
## Commands
3334

IDE/src/extension.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,11 @@ async function auto_link_all() {
123123
const mcpPath = await writeMCPConfig(backend_url, api_key);
124124
configs.push(mcpPath);
125125
}
126-
configs.push(await writeCursorConfig(backend_url, api_key, use_mcp));
127-
configs.push(await writeClaudeConfig(backend_url, api_key, use_mcp));
128-
configs.push(await writeWindsurfConfig(backend_url, api_key, use_mcp));
129-
configs.push(await writeCopilotConfig(backend_url, api_key, use_mcp));
130-
configs.push(await writeCodexConfig(backend_url, api_key, use_mcp));
126+
configs.push(await writeCursorConfig(backend_url, api_key, use_mcp, mcp_server_path));
127+
configs.push(await writeClaudeConfig(backend_url, api_key, use_mcp, mcp_server_path));
128+
configs.push(await writeWindsurfConfig(backend_url, api_key, use_mcp, mcp_server_path));
129+
configs.push(await writeCopilotConfig(backend_url, api_key, use_mcp, mcp_server_path));
130+
configs.push(await writeCodexConfig(backend_url, api_key, use_mcp, mcp_server_path));
131131

132132
const mode = use_mcp ? 'MCP protocol' : 'Direct HTTP';
133133
vscode.window.showInformationMessage(`✅ Auto-linked OpenMemory to AI tools (${mode})`);

IDE/src/writers/claude.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@ export interface ClaudeConfig {
1515
api_key?: string;
1616
}
1717

18-
export function generateClaudeConfig(backendUrl: string, apiKey?: string, useMCP = false): ClaudeConfig {
18+
export function generateClaudeConfig(backendUrl: string, apiKey?: string, useMCP = false, mcpServerPath?: string): ClaudeConfig {
1919
if (useMCP) {
20+
const backendMcpPath = mcpServerPath || path.join(process.cwd(), 'backend', 'dist', 'ai', 'mcp.js');
2021
return {
2122
mcpServers: {
2223
openmemory: {
2324
command: 'node',
24-
args: [path.join(os.homedir(), '.mcp', 'memory.mcp.json')],
25-
env: apiKey ? { OPENMEMORY_API_KEY: apiKey } : undefined
25+
args: [backendMcpPath],
26+
env: apiKey ? { OM_API_KEY: apiKey } : undefined
2627
}
2728
}
2829
};
@@ -36,15 +37,15 @@ export function generateClaudeConfig(backendUrl: string, apiKey?: string, useMCP
3637
return config;
3738
}
3839

39-
export async function writeClaudeConfig(backendUrl: string, apiKey?: string, useMCP = false): Promise<string> {
40+
export async function writeClaudeConfig(backendUrl: string, apiKey?: string, useMCP = false, mcpServerPath?: string): Promise<string> {
4041
const claudeDir = path.join(os.homedir(), '.claude', 'providers');
4142
const configFile = path.join(claudeDir, 'openmemory.json');
4243

4344
if (!fs.existsSync(claudeDir)) {
4445
fs.mkdirSync(claudeDir, { recursive: true });
4546
}
4647

47-
const config = generateClaudeConfig(backendUrl, apiKey, useMCP);
48+
const config = generateClaudeConfig(backendUrl, apiKey, useMCP, mcpServerPath);
4849
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
4950

5051
return configFile;

IDE/src/writers/codex.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,19 @@ export interface CodexConfig {
2121
};
2222
}
2323

24-
export function generateCodexConfig(backendUrl: string, apiKey?: string, useMCP = false): CodexConfig {
24+
export function generateCodexConfig(backendUrl: string, apiKey?: string, useMCP = false, mcpServerPath?: string): CodexConfig {
2525
if (useMCP) {
26-
const mcpConfigPath = path.join(os.homedir(), '.mcp', 'memory.mcp.json');
26+
const backendMcpPath = mcpServerPath || path.join(process.cwd(), 'backend', 'dist', 'ai', 'mcp.js');
2727
const config: CodexConfig = {
2828
mcpServers: {
2929
openmemory: {
3030
command: 'node',
31-
args: [mcpConfigPath]
31+
args: [backendMcpPath]
3232
}
3333
}
3434
};
3535
if (apiKey) {
36-
config.mcpServers!.openmemory.env = { OPENMEMORY_API_KEY: apiKey };
36+
config.mcpServers!.openmemory.env = { OM_API_KEY: apiKey };
3737
}
3838
return config;
3939
}
@@ -54,15 +54,15 @@ export function generateCodexConfig(backendUrl: string, apiKey?: string, useMCP
5454
};
5555
}
5656

57-
export async function writeCodexConfig(backendUrl: string, apiKey?: string, useMCP = false): Promise<string> {
57+
export async function writeCodexConfig(backendUrl: string, apiKey?: string, useMCP = false, mcpServerPath?: string): Promise<string> {
5858
const codexDir = path.join(os.homedir(), '.codex');
5959
const configFile = path.join(codexDir, 'context.json');
6060

6161
if (!fs.existsSync(codexDir)) {
6262
fs.mkdirSync(codexDir, { recursive: true });
6363
}
6464

65-
const config = generateCodexConfig(backendUrl, apiKey, useMCP);
65+
const config = generateCodexConfig(backendUrl, apiKey, useMCP, mcpServerPath);
6666
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
6767

6868
return configFile;

IDE/src/writers/copilot.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,19 @@ export interface CopilotConfig {
1717
};
1818
}
1919

20-
export function generateCopilotConfig(backendUrl: string, apiKey?: string, useMCP = false): CopilotConfig {
20+
export function generateCopilotConfig(backendUrl: string, apiKey?: string, useMCP = false, mcpServerPath?: string): CopilotConfig {
2121
if (useMCP) {
22-
const mcpConfigPath = path.join(os.homedir(), '.mcp', 'memory.mcp.json');
22+
const backendMcpPath = mcpServerPath || path.join(process.cwd(), 'backend', 'dist', 'ai', 'mcp.js');
2323
const config: CopilotConfig = {
2424
name: 'OpenMemory',
2525
type: 'mcp',
2626
mcpServer: {
2727
command: 'node',
28-
args: [mcpConfigPath]
28+
args: [backendMcpPath]
2929
}
3030
};
3131
if (apiKey) {
32-
config.mcpServer!.env = { OPENMEMORY_API_KEY: apiKey };
32+
config.mcpServer!.env = { OM_API_KEY: apiKey };
3333
}
3434
return config;
3535
}
@@ -50,15 +50,15 @@ export function generateCopilotConfig(backendUrl: string, apiKey?: string, useMC
5050
return config;
5151
}
5252

53-
export async function writeCopilotConfig(backendUrl: string, apiKey?: string, useMCP = false): Promise<string> {
53+
export async function writeCopilotConfig(backendUrl: string, apiKey?: string, useMCP = false, mcpServerPath?: string): Promise<string> {
5454
const copilotDir = path.join(os.homedir(), '.github', 'copilot');
5555
const configFile = path.join(copilotDir, 'openmemory.json');
5656

5757
if (!fs.existsSync(copilotDir)) {
5858
fs.mkdirSync(copilotDir, { recursive: true });
5959
}
6060

61-
const config = generateCopilotConfig(backendUrl, apiKey, useMCP);
61+
const config = generateCopilotConfig(backendUrl, apiKey, useMCP, mcpServerPath);
6262
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
6363

6464
return configFile;

IDE/src/writers/cursor.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@ export interface CursorConfig {
1515
};
1616
}
1717

18-
export function generateCursorConfig(backendUrl: string, apiKey?: string, useMCP = false): CursorConfig {
18+
export function generateCursorConfig(backendUrl: string, apiKey?: string, useMCP = false, mcpServerPath?: string): CursorConfig {
1919
if (useMCP) {
20+
const backendMcpPath = mcpServerPath || path.join(process.cwd(), 'backend', 'dist', 'ai', 'mcp.js');
2021
return {
2122
name: 'OpenMemory',
2223
type: 'mcp',
2324
mcp: {
24-
server: path.join(os.homedir(), '.mcp', 'memory.mcp.json'),
25-
tools: ['queryMemory', 'searchMemory', 'getPatterns', 'storeMemory']
25+
server: backendMcpPath,
26+
tools: ['openmemory_query', 'openmemory_store', 'openmemory_list', 'openmemory_get', 'openmemory_reinforce']
2627
}
2728
};
2829
}
@@ -44,15 +45,15 @@ export function generateCursorConfig(backendUrl: string, apiKey?: string, useMCP
4445
};
4546
}
4647

47-
export async function writeCursorConfig(backendUrl: string, apiKey?: string, useMCP = false): Promise<string> {
48+
export async function writeCursorConfig(backendUrl: string, apiKey?: string, useMCP = false, mcpServerPath?: string): Promise<string> {
4849
const cursorDir = path.join(os.homedir(), '.cursor', 'context_providers');
4950
const configFile = path.join(cursorDir, 'openmemory.json');
5051

5152
if (!fs.existsSync(cursorDir)) {
5253
fs.mkdirSync(cursorDir, { recursive: true });
5354
}
5455

55-
const config = generateCursorConfig(backendUrl, apiKey, useMCP);
56+
const config = generateCursorConfig(backendUrl, apiKey, useMCP, mcpServerPath);
5657
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
5758

5859
return configFile;

IDE/src/writers/windsurf.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ export interface WindsurfConfig {
1111
};
1212
}
1313

14-
export function generateWindsurfConfig(backendUrl: string, apiKey?: string, useMCP = false): WindsurfConfig {
14+
export function generateWindsurfConfig(backendUrl: string, apiKey?: string, useMCP = false, mcpServerPath?: string): WindsurfConfig {
1515
if (useMCP) {
16+
const backendMcpPath = mcpServerPath || path.join(process.cwd(), 'backend', 'dist', 'ai', 'mcp.js');
1617
return {
1718
contextProvider: 'openmemory-mcp',
1819
mcp: {
19-
configPath: path.join(os.homedir(), '.mcp', 'memory.mcp.json')
20+
configPath: backendMcpPath
2021
}
2122
};
2223
}
@@ -29,15 +30,15 @@ export function generateWindsurfConfig(backendUrl: string, apiKey?: string, useM
2930
return config;
3031
}
3132

32-
export async function writeWindsurfConfig(backendUrl: string, apiKey?: string, useMCP = false): Promise<string> {
33+
export async function writeWindsurfConfig(backendUrl: string, apiKey?: string, useMCP = false, mcpServerPath?: string): Promise<string> {
3334
const windsurfDir = path.join(os.homedir(), '.windsurf', 'context');
3435
const configFile = path.join(windsurfDir, 'openmemory.json');
3536

3637
if (!fs.existsSync(windsurfDir)) {
3738
fs.mkdirSync(windsurfDir, { recursive: true });
3839
}
3940

40-
const config = generateWindsurfConfig(backendUrl, apiKey, useMCP);
41+
const config = generateWindsurfConfig(backendUrl, apiKey, useMCP, mcpServerPath);
4142
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
4243

4344
return configFile;

0 commit comments

Comments
 (0)