Skip to content

Commit ae58b9c

Browse files
committed
fix: resolve remaining merge conflicts in graph.js and store interface
2 parents 0063f83 + 986ea4e commit ae58b9c

7 files changed

Lines changed: 136 additions & 128 deletions

File tree

.env.example

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,16 @@ QDRANT_API_KEY=your-qdrant-key-here
2828
EMBEDDING_PROVIDER=openai
2929
OPENAI_API_KEY=your-openai-key-here
3030
# For Gemini:
31+
<<<<<<< HEAD
3132
# EMBEDDING_PROVIDER=gemini
3233
# GEMINI_API_KEY=your-gemini-key-here
3334
# GEMINI_EMBEDDING_MODEL=gemini-embedding-2-preview # default
3435
# GEMINI_EMBEDDING_DIMS=3072 # default (also supports 1536, 768 via Matryoshka)
36+
=======
37+
# GEMINI_API_KEY=your-gemini-key-here
38+
# GEMINI_EMBEDDING_MODEL=gemini-embedding-exp-03-07
39+
# GEMINI_EMBEDDING_DIMS=3072 # 3072 (default), 1536, or 768
40+
>>>>>>> worktree-agent-a1f30793
3541
# For Ollama:
3642
# OLLAMA_URL=http://localhost:11434
3743
# OLLAMA_MODEL=nomic-embed-text
@@ -66,6 +72,14 @@ DECAY_FACTOR=0.98
6672
# --- Event TTL ---
6773
# EVENT_TTL_DAYS=30 # Auto-expire low-importance, never-accessed events after N days (default: 30)
6874

75+
# --- Client Resolver ---
76+
# BASEROW_CLIENTS_TABLE_ID=734 # Baserow table with client_fingerprints field
77+
# BASEROW_CLIENT_TOKEN= # Uses BASEROW_API_KEY if not set
78+
79+
# --- Webhook Notifications ---
80+
# WEBHOOK_URLS=https://n8n.example.com/webhook/brain # Comma-separated webhook URLs for memory events
81+
# WEBHOOK_EVENTS=store,supersede,delete # Which events trigger notifications (default: all)
82+
6983
# --- MCP Server Timeouts ---
7084
# BRAIN_MCP_TIMEOUT=15000 # Default timeout for MCP→API calls in ms (default: 15000)
7185
# BRAIN_MCP_CONSOLIDATION_TIMEOUT=120000 # Timeout for sync consolidation calls in ms (default: 120000)

README.md

Lines changed: 100 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,19 @@ This means you get both "find memories similar to X" *and* "give me all facts wi
187187
│ Memory API (Express) │
188188
│ POST /memory GET /memory/search GET /briefing GET /stats │
189189
│ GET /memory/query POST /webhook/n8n POST /consolidate GET /entities │
190+
│ GET /client/:name GET /export POST /import GET /graph │
190191
├──────────────────────┬────────────────────────────────────────────────────┤
191192
│ Embedding Layer │ LLM Layer │
193+
<<<<<<< HEAD
192194
│ ┌────────┐ ┌──────┐ ┌──────┐│ ┌────────┐ ┌───────────┐ ┌──────┐ ┌──────┐│
193195
│ │ OpenAI │ │Gemini│ │Ollama││ │ OpenAI │ │ Anthropic │ │Gemini│ │Ollama││
194196
│ └────────┘ └──────┘ └──────┘│ └────────┘ └───────────┘ └──────┘ └──────┘│
197+
=======
198+
│ ┌────────┐ ┌──────┐│ ┌────────┐ ┌───────────┐ ┌──────┐ ┌──────┐ │
199+
│ │ OpenAI │ │Gemini││ │ OpenAI │ │ Anthropic │ │Gemini│ │Ollama│ │
200+
│ │ │ │Ollama││ └────────┘ └───────────┘ └──────┘ └──────┘ │
201+
│ └────────┘ └──────┘│ │
202+
>>>>>>> worktree-agent-a1f30793
195203
├──────────────────────┴────────────────────────────────────────────────────┤
196204
│ Storage Layer │
197205
│ ┌────────────────────┐ ┌────────┐ ┌────────┐ ┌───────┐ │
@@ -245,6 +253,7 @@ Entities are automatically extracted from the content and stored in both the Qdr
245253
| `category` | No | `semantic`, `episodic`, or `procedural`. Default: `episodic` |
246254
| `importance` | No | `critical`, `high`, `medium`, or `low`. Default: `medium` |
247255
| `key` | No | For facts: unique key enabling upsert |
256+
| `knowledge_category` | No | `brand`, `strategy`, `meeting`, `content`, `technical`, `relationship`, or `general`. Auto-classified during consolidation if not set. |
248257
| `subject` | No | For statuses: what system this status is about |
249258
| `status_value` | No | For statuses: the current status string |
250259

@@ -412,6 +421,63 @@ Entity types: `client`, `person`, `system`, `service`, `domain`, `technology`, `
412421

413422
Requires SQLite or Postgres structured storage backend. Baserow users get entity data in Qdrant payloads but not the entity graph endpoints.
414423

424+
### `GET /client/:name` — Client briefing
425+
426+
```bash
427+
curl "http://localhost:8084/client/acme-corp" -H "X-Api-Key: YOUR_KEY"
428+
```
429+
430+
Returns a comprehensive client briefing: all memories tagged with the resolved client_id, grouped by knowledge_category (brand, strategy, meeting, content, technical, relationship, general). Supports fuzzy name resolution — "acme", "Acme Corp", and "acme-corp" all resolve to the same client.
431+
432+
| Param | Description |
433+
|-------|-------------|
434+
| `name` | Client name or slug (fuzzy matched against fingerprints) |
435+
| `category` | Filter by knowledge_category |
436+
| `limit` | Max memories per category (default 20) |
437+
438+
### `GET /export` — Export memories
439+
440+
```bash
441+
curl "http://localhost:8084/export?format=json" -H "X-Api-Key: YOUR_KEY" -o backup.json
442+
```
443+
444+
Exports all active memories as JSON. Useful for backup before embedding provider migration or system upgrades.
445+
446+
| Param | Description |
447+
|-------|-------------|
448+
| `format` | `json` (default) |
449+
| `client_id` | Filter export to a specific client |
450+
| `type` | Filter by memory type |
451+
452+
### `POST /import` — Import memories
453+
454+
```bash
455+
curl -X POST "http://localhost:8084/import" \
456+
-H "Content-Type: application/json" \
457+
-H "X-Api-Key: YOUR_KEY" \
458+
-d @backup.json
459+
```
460+
461+
Imports memories from a previous export. Handles deduplication (skips exact hash matches) and batch processes embeddings. Safe for embedding provider migration — re-embeds all content with the current provider.
462+
463+
### `GET /graph` — Entity relationship graph
464+
465+
```bash
466+
# JSON data
467+
curl "http://localhost:8084/graph?format=json" -H "X-Api-Key: YOUR_KEY"
468+
469+
# Interactive D3.js visualization (open in browser)
470+
# http://localhost:8084/graph
471+
```
472+
473+
Returns entity relationships as a node/edge graph. Co-occurrence relationships are automatically detected during consolidation. The HTML format serves an interactive D3.js force-directed visualization with dark theme, search, and zoom.
474+
475+
| Param | Description |
476+
|-------|-------------|
477+
| `format` | `html` (default, interactive viz) or `json` (raw data) |
478+
| `entity` | Center graph on a specific entity |
479+
| `depth` | Relationship traversal depth (default 2) |
480+
415481
### Backfill Existing Memories
416482

417483
After upgrading to v1.2.0, run the backfill script once to extract entities from existing memories:
@@ -445,7 +511,7 @@ docker exec shared-brain-api node scripts/reindex-embeddings.js
445511

446512
### MCP Server (Claude Code, Cursor, Windsurf)
447513

448-
The MCP server exposes 8 tools: `brain_store`, `brain_search`, `brain_briefing`, `brain_query`, `brain_stats`, `brain_consolidate`, `brain_entities`, `brain_delete`.
514+
The MCP server exposes 12 tools: `brain_store`, `brain_search`, `brain_briefing`, `brain_query`, `brain_stats`, `brain_consolidate`, `brain_entities`, `brain_delete`, `brain_client`, `brain_export`, `brain_import`, `brain_graph`.
449515

450516
**Claude Code (`~/.claude.json`):**
451517
```json
@@ -553,8 +619,13 @@ All configuration is via environment variables. Copy `.env.example` to `.env` an
553619
| `EMBEDDING_PROVIDER` | `openai` | `openai`, `gemini`, or `ollama` |
554620
| `OPENAI_API_KEY` || Required when using OpenAI embeddings |
555621
| `GEMINI_API_KEY` || Required when using Gemini embeddings |
622+
<<<<<<< HEAD
556623
| `GEMINI_EMBEDDING_MODEL` | `gemini-embedding-2-preview` | Gemini embedding model name |
557624
| `GEMINI_EMBEDDING_DIMS` | `3072` | Output dimensions (3072, 1536, or 768 via Matryoshka) |
625+
=======
626+
| `GEMINI_EMBEDDING_MODEL` | `gemini-embedding-exp-03-07` | Gemini embedding model |
627+
| `GEMINI_EMBEDDING_DIMS` | `3072` | Output dimensions (3072, 1536, or 768 — Matryoshka support) |
628+
>>>>>>> worktree-agent-a1f30793
558629
| `OLLAMA_URL` | `http://localhost:11434` | Ollama server URL |
559630
| `OLLAMA_MODEL` | `nomic-embed-text` | Ollama embedding model name |
560631

@@ -590,6 +661,24 @@ Entity graph tables (entities, aliases, memory links) are automatically created
590661

591662
Only affects `fact` and `status` types. Events and decisions don't decay.
592663

664+
### Client Resolver
665+
666+
| Variable | Default | Description |
667+
|----------|---------|-------------|
668+
| `BASEROW_CLIENTS_TABLE_ID` || Baserow table ID containing client records with `client_fingerprints` field |
669+
| `BASEROW_CLIENT_TOKEN` || API token for client table access. Falls back to `BASEROW_API_KEY` if not set |
670+
671+
When configured, the client resolver auto-tags `client_id` on incoming memories by matching content against client fingerprints (accent-normalized, fuzzy). This enables automatic client attribution without agents needing to specify `client_id` explicitly.
672+
673+
### Webhook Notifications
674+
675+
| Variable | Default | Description |
676+
|----------|---------|-------------|
677+
| `WEBHOOK_URLS` || Comma-separated webhook URLs for memory event notifications |
678+
| `WEBHOOK_EVENTS` | `store,supersede,delete` | Which events trigger notifications |
679+
680+
When configured, the system dispatches real-time HTTP POST notifications to the specified URLs on memory store, supersede, and delete events. Useful for triggering n8n workflows, Slack alerts, or external dashboards.
681+
593682
## Deployment
594683

595684
### Docker (Recommended)
@@ -644,12 +733,19 @@ multi-agent-memory/
644733
│ │ │ ├── stats.js # Memory + entity health dashboard
645734
│ │ │ ├── consolidation.js# Consolidation trigger/status
646735
│ │ │ ├── webhook.js # n8n webhook with entity extraction
647-
│ │ │ └── entities.js # Entity graph endpoints
736+
│ │ │ ├── entities.js # Entity graph endpoints
737+
│ │ │ ├── client.js # Client briefing endpoint
738+
│ │ │ ├── export.js # Import/export endpoints
739+
│ │ │ └── graph.js # Entity relationship graph + D3.js viz
740+
│ │ ├── templates/
741+
│ │ │ └── graph.html # Interactive D3.js force-directed graph
648742
│ │ └── services/
649743
│ │ ├── qdrant.js # Vector store + entity index
650744
│ │ ├── scrub.js # Credential scrubbing
651745
│ │ ├── entities.js # Entity extraction, alias cache, linking
652746
│ │ ├── consolidation.js# LLM consolidation + entity refinement
747+
│ │ ├── client-resolver.js # Fingerprint-based client identification
748+
│ │ ├── notifications.js# Webhook notification dispatch
653749
│ │ ├── embedders/ # Pluggable embedding providers
654750
│ │ │ ├── interface.js
655751
│ │ │ ├── openai.js
@@ -673,7 +769,7 @@ multi-agent-memory/
673769
│ ├── Dockerfile
674770
│ └── package.json
675771
├── mcp-server/ # MCP server for Claude/Cursor
676-
│ ├── src/index.js # 8 tools: store, search, briefing, query, stats, consolidate, entities, delete
772+
│ ├── src/index.js # 12 tools: store, search, briefing, query, stats, consolidate, entities, delete, client, export, import, graph
677773
│ └── package.json
678774
├── adapters/
679775
│ ├── bash/ # CLI adapter (curl + jq)
@@ -689,12 +785,10 @@ multi-agent-memory/
689785
## Roadmap
690786

691787
- **Web dashboard** — Browse, search, and manage memories visually
692-
- **Entity relationships** — Typed edges between entities (e.g., "Client X uses Technology Y")
693788
- **Python SDK**`pip install multi-agent-memory`
694789
- **Automatic memory capture** — System learns what's worth remembering vs what's noise
695790
- **Multi-collection support** — Isolated memory spaces per project or team
696-
- **Real-time notifications** — SSE/WebSocket for agents to subscribe to memory updates
697-
- **Memory import/export** — Bulk operations for backup and migration
791+
- **SSE/WebSocket subscriptions** — Real-time streaming for agents to subscribe to memory updates
698792

699793
## Contributing
700794

api/src/routes/graph.js

Lines changed: 0 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,4 @@
11
import { Router } from 'express';
2-
<<<<<<< HEAD
3-
import { findEntity, getRelationships } from '../services/stores/interface.js';
4-
import { isEntityStoreAvailable } from '../services/stores/interface.js';
5-
6-
export const graphRouter = Router();
7-
8-
/**
9-
* GET /graph/:entity — Query entity relationships
10-
* Query params:
11-
* - min_strength (default 1): minimum relationship strength
12-
* - depth (default 1, max 3): how many hops to traverse
13-
*/
14-
graphRouter.get('/:entity', async (req, res) => {
15-
try {
16-
if (!isEntityStoreAvailable()) {
17-
return res.status(501).json({
18-
error: 'Entity graph requires sqlite or postgres backend. Set STRUCTURED_STORE in .env.',
19-
});
20-
}
21-
22-
const entityName = req.params.entity;
23-
const minStrength = parseInt(req.query.min_strength) || 1;
24-
const depth = Math.min(Math.max(parseInt(req.query.depth) || 1, 1), 3);
25-
26-
const entity = await findEntity(entityName);
27-
if (!entity) {
28-
return res.status(404).json({ error: `Entity not found: ${entityName}` });
29-
}
30-
31-
const rootEntity = {
32-
id: entity.id,
33-
name: entity.canonical_name,
34-
type: entity.entity_type,
35-
};
36-
37-
const relationships = await getRelationships(entity.id, minStrength);
38-
39-
if (depth === 1) {
40-
return res.json({
41-
entity: rootEntity,
42-
relationships,
43-
depth,
44-
});
45-
}
46-
47-
// For depth > 1, recursively fetch relationships of connected entities
48-
const visited = new Set([entity.id]);
49-
const enriched = await enrichRelationships(relationships, minStrength, depth - 1, visited);
50-
51-
return res.json({
52-
entity: rootEntity,
53-
relationships: enriched,
54-
depth,
55-
});
56-
=======
572
import { readFileSync } from 'fs';
583
import { fileURLToPath } from 'url';
594
import { dirname, join } from 'path';
@@ -123,73 +68,12 @@ graphRouter.get('/:entity', async (req, res) => {
12368

12469
const graphData = await buildGraphData(entity.canonical_name, depth);
12570
res.json(graphData);
126-
>>>>>>> worktree-agent-a801b191
12771
} catch (err) {
12872
console.error('[graph] Error:', err.message);
12973
res.status(500).json({ error: err.message });
13074
}
13175
});
13276

133-
<<<<<<< HEAD
134-
/**
135-
* Recursively fetch nested relationships up to remaining depth.
136-
* Uses a visited set to avoid cycles.
137-
*/
138-
async function enrichRelationships(relationships, minStrength, remainingDepth, visited) {
139-
if (remainingDepth <= 0) return relationships;
140-
141-
const enriched = [];
142-
for (const rel of relationships) {
143-
const enrichedRel = { ...rel };
144-
if (!visited.has(rel.entity.id)) {
145-
visited.add(rel.entity.id);
146-
const nested = await getRelationships(rel.entity.id, minStrength);
147-
// Filter out already-visited entities from nested results
148-
const filtered = nested.filter(n => !visited.has(n.entity.id));
149-
enrichedRel.relationships = await enrichRelationships(filtered, minStrength, remainingDepth - 1, visited);
150-
} else {
151-
enrichedRel.relationships = [];
152-
}
153-
enriched.push(enrichedRel);
154-
}
155-
return enriched;
156-
}
157-
158-
/**
159-
* GET /graph/:entity/html — Placeholder for D3.js visualization (Task 11)
160-
*/
161-
graphRouter.get('/:entity/html', async (req, res) => {
162-
if (!isEntityStoreAvailable()) {
163-
return res.status(501).json({
164-
error: 'Entity graph requires sqlite or postgres backend. Set STRUCTURED_STORE in .env.',
165-
});
166-
}
167-
168-
const entityName = req.params.entity;
169-
res.setHeader('Content-Type', 'text/html');
170-
res.send(`<!DOCTYPE html>
171-
<html lang="en">
172-
<head>
173-
<meta charset="UTF-8">
174-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
175-
<title>Graph: ${entityName}</title>
176-
<style>
177-
body { font-family: system-ui, sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #1a1a2e; color: #e0e0e0; }
178-
.loading { text-align: center; }
179-
.loading h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }
180-
.loading p { color: #888; }
181-
</style>
182-
</head>
183-
<body>
184-
<div class="loading">
185-
<h1>Graph visualization loading...</h1>
186-
<p>Entity: <strong>${entityName}</strong></p>
187-
<p>Full D3.js interactive visualization coming in the next update.</p>
188-
</div>
189-
</body>
190-
</html>`);
191-
});
192-
=======
19377
// --- Graph building ---
19478

19579
/**
@@ -346,4 +230,3 @@ function escapeHtml(str) {
346230
.replace(/"/g, '&quot;')
347231
.replace(/'/g, '&#039;');
348232
}
349-
>>>>>>> worktree-agent-a801b191

api/src/services/stores/interface.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,6 @@ export async function getEntityStats() {
113113
return requireStore().getEntityStats();
114114
}
115115

116-
<<<<<<< HEAD
117116
export async function createRelationship(sourceId, targetId, type) {
118117
return requireStore().createRelationship(sourceId, targetId, type);
119118
}
@@ -124,9 +123,9 @@ export async function getRelationships(entityId, minStrength) {
124123

125124
export async function listRelationships(filters) {
126125
return requireStore().listRelationships(filters);
127-
=======
126+
}
127+
128128
// Direct store access (used by graph visualization for co-occurrence queries)
129129
export function _getStoreInstance() {
130130
return store;
131-
>>>>>>> worktree-agent-a801b191
132131
}

mcp-server/CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Changelog
22

3+
## 2.0.0 (2026-03-20)
4+
5+
### Features
6+
- **Client knowledge base**: Fingerprint-based client identification with accent normalization, `knowledge_category` field (brand/strategy/meeting/content/technical/relationship/general), `brain_client` tool for one-call client briefings with fuzzy name resolution
7+
- **Import/Export**: `brain_export` and `brain_import` tools for backup and embedding migration safety, with dedup and batch processing
8+
- **Webhook notifications**: Real-time dispatch on memory store/supersede/delete events via configurable webhook URLs
9+
- **Entity graph**: Relationship tracking with co-occurrence detection, `brain_graph` tool, interactive D3.js visualization (dark theme, force-directed, searchable)
10+
- **Consolidation enhancements**: Automatic knowledge_category reclassification and entity relationship type classification during 6h consolidation pass
11+
- **Auto-resolve client_id**: Memory store auto-tags client_id from content using fingerprint matching when not explicitly provided
12+
- **Gemini Embedding 2**: New pluggable embedder with task-type-aware embeddings (RETRIEVAL_DOCUMENT/RETRIEVAL_QUERY), Matryoshka support (3072/1536/768 dims)
13+
14+
## 1.5.0
15+
16+
### Long-Term Memory Hygiene
17+
- **Access-weighted search** — search results factor in access count alongside similarity and confidence, rewarding frequently-accessed memories
18+
- **Insight removal** — consolidation-generated insights can now be removed when source memories are deleted
19+
- **Entity fix** — fixed entity extraction for memories with no client_id
20+
321
## 1.4.0
422

523
### Token Optimization

0 commit comments

Comments
 (0)