@riligar/agents-memories 1.0.0 → 1.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/README.md CHANGED
@@ -1,78 +1,110 @@
1
1
  # RiLiGar Agents Memories
2
2
 
3
- A high-performance, persistent memory system for AI agents, built on the **Model Context Protocol (MCP)**. This project implements a **Relational Knowledge Graph** featuring **Resonance Intelligence** and **Semantic Bridging**.
3
+ A high-performance, persistent memory system for AI agents, built on the **Model Context Protocol (MCP)**. RiLiGar transforms flat data into a **Relational Knowledge Graph** featuring **Resonance Intelligence** and **Semantic Bridging**.
4
4
 
5
- ## 🧠 Philosophy: The Cognitive Memories
5
+ Now evolved into a **Modular SDK**, RiLiGar can be used both as a standalone MCP server and as a core library for cognitive applications.
6
6
 
7
- > "The original repository is a developer's **Toolbox**; our project are the **Collective Memories** for AIs. We evolved the fixed hierarchy into a self-organizing, relational mind. 🏛✨"
7
+ ## 🧠 Philosophy: The Cognitive Self
8
8
 
9
- RiLiGar focuses on **Relational Intelligence**. By transforming memories into a living graph, the agent doesn't just retrieve facts—it understands how those facts are interconnected across different domains.
9
+ RiLiGar focuses on **Relational Intelligence**. Memory is not just retrieval; it is an extension of the agent's identity. By transforming memories into a living graph, the agent understands how facts are interconnected across different domains, simulating a "working memory" that evolves with every interaction.
10
10
 
11
- ## 🛡️ Cryptographic Identity
12
-
13
- Each RiLiGar installation possesses a unique, professional cryptographic identity (Ed25519):
11
+ ---
14
12
 
15
- - **Implicit Ownership**: By default, the system uses its own 10-character hex ID (derived from the public key hash) to sign and own memories.
16
- - **Explicit Identity**: Users or external agents can provide their own `owner_id` to maintain clear boundaries in multi-user environments.
17
- - **Architecture**: Stored locally in `data/identity.json`, ensuring the agent maintains its "soul" across sessions.
13
+ ## 🏗️ Modular Architecture
18
14
 
19
- ## 🕸 The Relational Knowledge Graph
15
+ The project is organized into distinct layers to ensure scalability and maintainability:
20
16
 
21
- RiLiGar moves beyond simple keyword or vector storage into a **Graph Database** paradigm:
17
+ - **`src/core` (Cognition)**: Implements the heavy-lifting logic:
18
+ - **Resonance Intelligence**: Priorities ripple through the graph; if a node is marked as critical, its semantic neighbors gain importance.
19
+ - **Semantic Magnet**: Automatically detects near-identical facts and creates `similar_to` links.
20
+ - **`src/database` (Persistence)**: Uses **LibSQL (SQLite)** for edge-ready, high-speed storage.
21
+ - **`src/sdk` (Interface)**: Provides the `MemorySystem` class, a clean programmatic API.
22
+ - **`src/server` (Transport)**: Handles MCP communication via **Stdio** (local) and **SSE** (web/remote).
22
23
 
23
- - **Semantic Magnet (Auto-Bridging)**: The system automatically detects near-identical facts (similarity > 0.9) and creates `similar_to` links during the storage phase.
24
- - **Manual Bridging**: Explicitly connect disparate paths using the `bridge` tool to establish logical dependencies (e.g., `Architecture` depends_on `Infrastructure`).
25
- - **Resonance Intelligence**: Priorities ripple through the graph; if a node is marked as critical, its semantic neighbors gain importance via resonance.
24
+ ---
26
25
 
27
- ## 🏺 Visualizing the Memories
26
+ ## 🔌 Quick Start
28
27
 
29
- RiLiGar provides a native **Inspection** layer using **Mermaid.js**:
28
+ ### Prerequisites
30
29
 
31
- - **Command**: `inspect`
32
- - **Output**: Generates a visual mental map of all connections, allowing the agent and user to see the topology of their stored knowledge.
30
+ - [Bun](https://bun.sh) runtime (v1.x+)
33
31
 
34
- ## ⚖️ Relational Semantic Search
32
+ ### Installation
35
33
 
36
- Searches in RiLiGar are **Read-Only** and **Relational**. When a fact is retrieved, its connections are automatically surfaced:
34
+ ```bash
35
+ bun install
36
+ ```
37
37
 
38
- `[Path.A | P:10] Fact A`
39
- `🔗 Related: Path.B (similar_to), Path.C (depends_on)`
38
+ ### Running the Standalone Server
40
39
 
41
- - **Scoring (Working Memory Boost)**: `Score = (Similarity * 0.6) + (Recency * 0.4)`.
42
- - **Short-term Awareness**: The system is tuned for **Cognitive Fluidity**, prioritizing recent thoughts and context to function as an effective short-term "working memory" while maintaining long-term relational links.
40
+ The server starts both **Stdio** and **SSE** transports simultaneously on port `3000`.
43
41
 
44
- ## 🔌 MCP Tools
42
+ ```bash
43
+ bun start
44
+ ```
45
45
 
46
- - `save`: Stores content with automatic priority inference and **Semantic Magnet** auto-linking.
47
- - `search`: Executes a fast, weighted relational semantic search.
48
- - `bridge`: Manually links two memory paths with a specified relation type.
49
- - `inspect`: Generates a Mermaid.js diagram of the current knowledge graph.
50
- - `list`: Lists all unique memory paths.
46
+ - **Stdio**: Use this for Claude Desktop or local IDE configurations.
47
+ - **SSE**: Access via `http://localhost:3000/sse` for web dashboards or remote agents.
51
48
 
52
49
  ---
53
50
 
54
- ## 🛠️ Setup & Usage
51
+ ## 📦 Programmatic Usage (SDK)
55
52
 
56
- ### Prerequisites
53
+ You can use RiLiGar as a library in your own Bun projects:
57
54
 
58
- - [Bun](https://bun.sh) runtime (v1.x+)
55
+ ```javascript
56
+ import { MemorySystem, getSystemIdentity } from '@riligar/agents-memories'
57
+ import { createClient } from '@libsql/client'
59
58
 
60
- ### Installation
59
+ const db = createClient({ url: 'file:data/memories.db' })
60
+ const ownerId = getSystemIdentity('data')
61
61
 
62
- ```bash
63
- bun install
64
- ```
62
+ const memory = new MemorySystem(db, ownerId)
63
+ await memory.init()
65
64
 
66
- ### Running the Server
65
+ // Save a memory with automatic priority inference
66
+ await memory.save({
67
+ content: 'The architecture is now modular.',
68
+ path: 'Project.Architecture',
69
+ })
67
70
 
68
- ```bash
69
- bun start # runs src/index.js
71
+ // Relational Semantic Search
72
+ const results = await memory.search({ query: 'How is the project organized?' })
73
+ console.log(results)
70
74
  ```
71
75
 
72
- ### Verification
76
+ ---
77
+
78
+ ## 🛡️ Identity & SaaS Readiness
79
+
80
+ - **Cryptographic Soul**: Each installation generates an Ed25519 identity (`data/identity.json`). The system uses a 10-character hex `owner_id` derived from this key.
81
+ - **Multi-Tenancy**: Every memory is bound to an `owner_id`, allowing the same database to securely host memories for multiple users or agents.
82
+
83
+ ---
84
+
85
+ ## 🛠️ MCP Tools Reference
86
+
87
+ - `save`: Stores content with automatic priority inference and **Semantic Magnet** auto-linking.
88
+ - `search`: Relational semantic search (Score = 60% Similarity + 40% Recency).
89
+ - `bridge`: Manually links two memory paths (e.g., `Architecture` -> `Security`).
90
+ - `inspect`: Generates a **Mermaid.js** graph of the memory topology.
91
+ - `list`: Shows all unique memory paths.
92
+
93
+ ---
94
+
95
+ ## 🧪 Development
96
+
97
+ ### Running tests
73
98
 
74
99
  ```bash
75
- bun src/test.js
100
+ bun test
76
101
  ```
77
102
 
78
- **Built with ❤️ for the global open-source community by the RiLiGar Team.**
103
+ ### Stack
104
+
105
+ - **Runtime**: [Bun](https://bun.sh)
106
+ - **Database**: [LibSQL](https://github.com/tursodatabase/libsql)
107
+ - **Embeddings**: [Transformers.js](https://huggingface.co/docs/transformers.js) (`all-MiniLM-L6-v2`)
108
+ - **Server**: [Express](https://expressjs.com/) + MCP SDK
109
+
110
+ **Built with ❤️ by the RiLiGar Team.**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@riligar/agents-memories",
3
- "version": "1.0.0",
4
- "description": "RiLiGar Agents Memories - A persistent memories system for AI agents.",
3
+ "version": "1.2.0",
4
+ "description": "RiLiGar Agents Memories - A self-improving relational memory system for AI agents.",
5
5
  "module": "src/index.js",
6
6
  "main": "src/index.js",
7
7
  "type": "module",
@@ -23,10 +23,9 @@
23
23
  },
24
24
  "homepage": "https://github.com/riligar/agents-memories#readme",
25
25
  "scripts": {
26
- "dev": "bun --watch src/index.js",
27
- "start": "bun src/index.js",
28
- "test": "bun src/test.js",
29
- "test:sse": "bun src/test-sse.js"
26
+ "dev": "bun --watch src/server/index.js",
27
+ "start": "bun src/server/index.js",
28
+ "test": "bun src/test.js"
30
29
  },
31
30
  "dependencies": {
32
31
  "@huggingface/transformers": "^4.0.1",
@@ -0,0 +1,33 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import crypto from 'crypto'
4
+
5
+ /**
6
+ * Utilitário para gerenciar a identidade criptográfica do agente.
7
+ */
8
+ export function getSystemIdentity(dataDir) {
9
+ const identityPath = path.join(dataDir, 'identity.json')
10
+
11
+ if (fs.existsSync(identityPath)) {
12
+ const data = JSON.parse(fs.readFileSync(identityPath, 'utf8'))
13
+ return data.owner_id
14
+ }
15
+
16
+ if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true })
17
+
18
+ const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519', {
19
+ publicKeyEncoding: { type: 'spki', format: 'pem' },
20
+ privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
21
+ })
22
+
23
+ const ownerId = crypto.createHash('sha256').update(publicKey).digest('hex').substring(0, 10)
24
+
25
+ fs.writeFileSync(identityPath, JSON.stringify({
26
+ owner_id: ownerId,
27
+ public_key: publicKey,
28
+ private_key: privateKey
29
+ }, null, 2), 'utf8')
30
+
31
+ console.error(`Generated new cryptographic identity: ${ownerId}`)
32
+ return ownerId
33
+ }
@@ -0,0 +1,62 @@
1
+ import { pipeline } from '@huggingface/transformers'
2
+
3
+ let extractor = null
4
+
5
+ /**
6
+ * Gera embeddings para o texto fornecido.
7
+ */
8
+ export async function getEmbedding(text) {
9
+ if (!extractor) {
10
+ extractor = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2')
11
+ }
12
+ const output = await extractor(text, { pooling: 'mean', normalize: true })
13
+ return new Float32Array(output.data)
14
+ }
15
+
16
+ /**
17
+ * Infere a prioridade biológica/semântica com base na vizinhança.
18
+ */
19
+ export async function inferPriority(db, vector) {
20
+ const result = await db.execute({
21
+ sql: `SELECT priority, vector_distance_cos(embedding, ?) as distance FROM memories ORDER BY distance ASC LIMIT 5`,
22
+ args: [vector.buffer],
23
+ })
24
+ if (result.rows.length === 0) return 5
25
+ let weightedSum = 0,
26
+ totalWeight = 0
27
+ for (const row of result.rows) {
28
+ const similarity = 1 - row.distance
29
+ const weight = Math.pow(similarity, 2)
30
+ weightedSum += row.priority * weight
31
+ totalWeight += weight
32
+ }
33
+ return totalWeight > 0 ? Math.round(weightedSum / totalWeight) : 5
34
+ }
35
+
36
+ /**
37
+ * Propaga o impacto (ressonância) para memórias vizinhas.
38
+ */
39
+ export async function propagateRipple(db, vector, manualPriority) {
40
+ const result = await db.execute({
41
+ sql: `SELECT id, priority, vector_distance_cos(embedding, ?) as distance FROM memories ORDER BY distance ASC LIMIT 5`,
42
+ args: [vector.buffer],
43
+ })
44
+ for (const row of result.rows) {
45
+ const similarity = 1 - row.distance
46
+ const resonantPriority = Math.round(manualPriority * similarity)
47
+ if (resonantPriority > row.priority) {
48
+ await db.execute({ sql: `UPDATE memories SET priority = ? WHERE id = ?`, args: [resonantPriority, row.id] })
49
+ }
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Encontra o vizinho mais próximo para o Semantic Magnet.
55
+ */
56
+ export async function findClosestNeighbor(db, vector) {
57
+ const result = await db.execute({
58
+ sql: `SELECT id, path, vector_distance_cos(embedding, ?) as distance FROM memories ORDER BY distance ASC LIMIT 1`,
59
+ args: [vector.buffer],
60
+ })
61
+ return result.rows[0] || null
62
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Self-Improvement Protocol (SIP) - Protocol Definition
3
+ * This document defines the core pillars of the RiLiGar self-improvement loop.
4
+ */
5
+ export const SIP_PROTOCOL = `
6
+ # RiLiGar Self-Improvement Protocol (SIP) v1.0
7
+
8
+ The SIP transforms the memory system from a passive database into an active, self-correcting cognitive engine. Every agent using this MCP should adhere to the following three pillars:
9
+
10
+ ## 1. Pattern Mining (Refinamento de Padrões)
11
+ - **Goal**: Identify common failure points and recurring logic patterns.
12
+ - **Action**: When saving a new memory, always include the **Rationale** (the "Why").
13
+ - **Loop**: Before taking a complex action, use \`search\` to find "Lessons Learned" and "Past Failures" related to the current context.
14
+
15
+ ## 2. Meta-Programming (Evolução de Skills)
16
+ - **Goal**: Update internal instructions and code patterns based on discovered "State of the Art" (SOTA) solutions.
17
+ - **Action**: If a specific implementation pattern proves superior (e.g., Elysia optimization), save it under \`Patterns.SOTA.*\` and update relevant local \`.agent/skills/\`.
18
+
19
+ ## 3. Self-Critique (Auditoria de Arquitetura)
20
+ - **Goal**: Prevent technical debt and architectural drift.
21
+ - **Action**: Periodically use \`inspect\` and \`search\` with filter \`path:Project.Architecture.*\` to identify redundancies or legacy constraints.
22
+ - **Outcome**: Propose refactoring at the first sign of relational misalignment.
23
+
24
+ ---
25
+
26
+ ### Execution Protocol
27
+ 1. **CAPTURE**: Save rationale for every architectural decision.
28
+ 2. **REFLECT**: Search for patterns before starting new tasks.
29
+ 3. **EVOLVE**: Update skills and best practices paths based on successful outcomes.
30
+ `.trim();
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Esquema de banco de dados do RiLiGar Agents Memories.
3
+ * Utiliza extensões de vetor nativas do LibSQL (vector_distance_cos).
4
+ */
5
+
6
+ export const SCHEMA_V1 = {
7
+ memories: `
8
+ CREATE TABLE IF NOT EXISTS memories (
9
+ id TEXT PRIMARY KEY DEFAULT (LOWER(HEX(RANDOMBLOB(5)))),
10
+ content TEXT NOT NULL,
11
+ path TEXT NOT NULL,
12
+ entities TEXT,
13
+ embedding F32_BLOB(384),
14
+ owner_id TEXT NOT NULL,
15
+ priority INTEGER DEFAULT 5,
16
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
17
+ );
18
+ `,
19
+ links: `
20
+ CREATE TABLE IF NOT EXISTS links (
21
+ source_id TEXT,
22
+ target_id TEXT,
23
+ relation_type TEXT,
24
+ PRIMARY KEY (source_id, target_id, relation_type)
25
+ );
26
+ `
27
+ }
28
+
29
+ export async function initializeSchema(db) {
30
+ await db.execute(SCHEMA_V1.memories)
31
+ await db.execute(SCHEMA_V1.links)
32
+ }
package/src/index.js CHANGED
@@ -1,344 +1,7 @@
1
- import { createClient } from '@libsql/client'
2
- import { Server } from '@modelcontextprotocol/sdk/server/index.js'
3
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
4
- import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'
5
- import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'
6
- import { pipeline } from '@huggingface/transformers'
7
- import express from 'express'
8
- import cors from 'cors'
9
- import fs from 'fs'
10
- import path from 'path'
11
- import crypto from 'crypto'
1
+ // Main Export File for @riligar/agents-memories SDK
12
2
 
13
- // ==========================================
14
- // 1. DATABASE & SETUP (Relational Knowledge Graph)
15
- // ==========================================
16
- const db = createClient({ url: 'file:data/memories.db' })
17
- const DATA_DIR = 'data'
18
- const IDENTITY_JSON_PATH = path.join(DATA_DIR, 'identity.json')
19
-
20
- if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR)
21
-
22
- function getSystemIdentity() {
23
- if (fs.existsSync(IDENTITY_JSON_PATH)) {
24
- const data = JSON.parse(fs.readFileSync(IDENTITY_JSON_PATH, 'utf8'))
25
- return data.owner_id
26
- }
27
-
28
- const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519', {
29
- publicKeyEncoding: { type: 'spki', format: 'pem' },
30
- privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
31
- })
32
-
33
- const ownerId = crypto.createHash('sha256').update(publicKey).digest('hex').substring(0, 10)
34
-
35
- fs.writeFileSync(IDENTITY_JSON_PATH, JSON.stringify({
36
- owner_id: ownerId,
37
- public_key: publicKey,
38
- private_key: privateKey
39
- }, null, 2), 'utf8')
40
-
41
- console.error(`Generated new cryptographic identity: ${ownerId}`)
42
- return ownerId
43
- }
44
-
45
- const SYSTEM_OWNER_ID = getSystemIdentity()
46
- console.error(`System Identity initialized: ${SYSTEM_OWNER_ID}`)
47
-
48
- await db.execute(`
49
- CREATE TABLE IF NOT EXISTS memories (
50
- id TEXT PRIMARY KEY DEFAULT (LOWER(HEX(RANDOMBLOB(5)))),
51
- content TEXT NOT NULL,
52
- path TEXT NOT NULL,
53
- entities TEXT,
54
- embedding F32_BLOB(384),
55
- owner_id TEXT NOT NULL,
56
- priority INTEGER DEFAULT 5,
57
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP
58
- );
59
- `)
60
-
61
- await db.execute(`
62
- CREATE TABLE IF NOT EXISTS links (
63
- source_id TEXT,
64
- target_id TEXT,
65
- relation_type TEXT,
66
- PRIMARY KEY (source_id, target_id, relation_type)
67
- );
68
- `)
69
-
70
- let extractor = null
71
- async function getEmbedding(text) {
72
- if (!extractor) {
73
- extractor = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2')
74
- }
75
- const output = await extractor(text, { pooling: 'mean', normalize: true })
76
- return new Float32Array(output.data)
77
- }
78
-
79
- // ==========================================
80
- // 2. COGNITIVE LOGIC (Inference, Ripple & Graph)
81
- // ==========================================
82
-
83
- async function inferPriority(vector) {
84
- const result = await db.execute({
85
- sql: `SELECT priority, vector_distance_cos(embedding, ?) as distance FROM memories ORDER BY distance ASC LIMIT 5`,
86
- args: [vector.buffer],
87
- })
88
- if (result.rows.length === 0) return 5
89
- let weightedSum = 0,
90
- totalWeight = 0
91
- for (const row of result.rows) {
92
- const similarity = 1 - row.distance
93
- const weight = Math.pow(similarity, 2)
94
- weightedSum += row.priority * weight
95
- totalWeight += weight
96
- }
97
- return totalWeight > 0 ? Math.round(weightedSum / totalWeight) : 5
98
- }
99
-
100
- async function propagateRipple(vector, manualPriority) {
101
- const result = await db.execute({
102
- sql: `SELECT id, priority, vector_distance_cos(embedding, ?) as distance FROM memories ORDER BY distance ASC LIMIT 5`,
103
- args: [vector.buffer],
104
- })
105
- for (const row of result.rows) {
106
- const similarity = 1 - row.distance
107
- const resonantPriority = Math.round(manualPriority * similarity)
108
- if (resonantPriority > row.priority) {
109
- await db.execute({ sql: `UPDATE memories SET priority = ? WHERE id = ?`, args: [resonantPriority, row.id] })
110
- }
111
- }
112
- }
113
-
114
- async function findClosestNeighbor(vector) {
115
- const result = await db.execute({
116
- sql: `SELECT id, path, vector_distance_cos(embedding, ?) as distance FROM memories ORDER BY distance ASC LIMIT 1`,
117
- args: [vector.buffer],
118
- })
119
- return result.rows[0] || null
120
- }
121
-
122
- // ==========================================
123
- // 3. MCP SERVER FACTORY
124
- // ==========================================
125
-
126
- function createMcpServer() {
127
- const server = new Server({ name: 'RiLiGar Agents Memories', version: '4.0.0' }, { capabilities: { tools: {} } })
128
-
129
- server.setRequestHandler(ListToolsRequestSchema, async () => {
130
- return {
131
- tools: [
132
- {
133
- name: 'save',
134
- description: 'Saves info with Resonance and Auto-Bridging (Semantic Magnet).',
135
- inputSchema: {
136
- type: 'object',
137
- properties: {
138
- content: {
139
- oneOf: [{ type: 'string' }, { type: 'array', items: { anyOf: [{ type: 'string' }, { type: 'object', properties: { content: { type: 'string' }, path: { type: 'string' }, owner_id: { type: 'string' }, priority: { type: 'integer' } }, required: ['content'] }] } }],
140
- },
141
- path: { type: 'string' },
142
- owner_id: { type: 'string' },
143
- priority: { type: 'integer' },
144
- },
145
- required: ['content'],
146
- },
147
- },
148
- {
149
- name: 'bridge',
150
- description: 'Manually connects two memory paths.',
151
- inputSchema: {
152
- type: 'object',
153
- properties: {
154
- source_path: { type: 'string' },
155
- target_path: { type: 'string' },
156
- relation: { type: 'string', default: 'related_to' },
157
- },
158
- required: ['source_path', 'target_path'],
159
- },
160
- },
161
- {
162
- name: 'search',
163
- description: 'Semantic search with Relational Awareness.',
164
- inputSchema: {
165
- type: 'object',
166
- properties: {
167
- query: { type: 'string' },
168
- path_filter: { type: 'string' },
169
- },
170
- required: ['query'],
171
- },
172
- },
173
- {
174
- name: 'inspect',
175
- description: 'Visualizes the Knowledge Graph in Mermaid.js syntax.',
176
- inputSchema: { type: 'object', properties: {} },
177
- },
178
- {
179
- name: 'list',
180
- description: 'Lists all memory paths.',
181
- inputSchema: { type: 'object', properties: {} },
182
- },
183
- ],
184
- }
185
- })
186
-
187
- server.setRequestHandler(CallToolRequestSchema, async request => {
188
- const { name, arguments: args } = request.params
189
-
190
- try {
191
- if (name === 'save') {
192
- const results = []
193
- const rawContent = args.content
194
- const defaultPath = args.path || 'General'
195
- const memoriesToSave = Array.isArray(rawContent) ? rawContent.map(m => (typeof m === 'string' ? { content: m } : m)) : [{ content: rawContent, path: args.path, owner_id: args.owner_id, priority: args.priority }]
196
-
197
- for (const mem of memoriesToSave) {
198
- if (!mem.content) continue
199
- const vector = await getEmbedding(mem.content)
200
- const path = mem.path || defaultPath
201
-
202
- const neighbor = await findClosestNeighbor(vector)
203
- const isManualPriority = typeof mem.priority === 'number'
204
- const finalPriority = isManualPriority ? mem.priority : await inferPriority(vector)
205
-
206
- const insertRes = await db.execute({
207
- sql: `INSERT INTO memories (content, path, embedding, owner_id, priority) VALUES (?, ?, ?, ?, ?) RETURNING id`,
208
- args: [mem.content, path, vector.buffer, mem.owner_id || args.owner_id || SYSTEM_OWNER_ID, finalPriority],
209
- })
210
- const newId = insertRes.rows[0].id
211
-
212
- if (neighbor && 1 - neighbor.distance > 0.9) {
213
- await db.execute({
214
- sql: `INSERT OR IGNORE INTO links (source_id, target_id, relation_type) VALUES (?, ?, ?)`,
215
- args: [newId, neighbor.id, 'similar_to'],
216
- })
217
- }
218
-
219
- if (isManualPriority) await propagateRipple(vector, finalPriority)
220
- results.push(path)
221
- }
222
- return { content: [{ type: 'text', text: `Saved ${results.length} memories. Semantic Magnet applied.` }] }
223
- }
224
-
225
- if (name === 'bridge') {
226
- const src = await db.execute({ sql: `SELECT id FROM memories WHERE path = ? LIMIT 1`, args: [args.source_path] })
227
- const tgt = await db.execute({ sql: `SELECT id FROM memories WHERE path = ? LIMIT 1`, args: [args.target_path] })
228
- if (!src.rows[0] || !tgt.rows[0]) throw new Error('Path(s) not found.')
229
-
230
- await db.execute({
231
- sql: `INSERT OR IGNORE INTO links (source_id, target_id, relation_type) VALUES (?, ?, ?)`,
232
- args: [src.rows[0].id, tgt.rows[0].id, args.relation || 'related_to'],
233
- })
234
- return { content: [{ type: 'text', text: `Bridge created: [${args.source_path}] --(${args.relation})--> [${args.target_path}]` }] }
235
- }
236
-
237
- if (name === 'search') {
238
- const vector = await getEmbedding(args.query)
239
- let sql = `SELECT id, content, path, priority, created_at, vector_distance_cos(embedding, ?) as distance FROM memories`
240
- const queryArgs = [vector.buffer],
241
- filters = []
242
- if (args.path_filter) {
243
- filters.push(`path LIKE ?`)
244
- queryArgs.push(args.path_filter)
245
- }
246
- if (filters.length > 0) sql += ` WHERE ` + filters.join(' AND ')
247
-
248
- const result = await db.execute({ sql, args: queryArgs })
249
- const rows = result.rows
250
- .map(r => ({ ...r, score: (1 - r.distance) * 0.6 + (1 / (1 + (new Date() - new Date(r.created_at)) / 86400000)) * 0.4 }))
251
- .sort((a, b) => b.score - a.score)
252
- .slice(0, 5)
253
-
254
- const formattedResults = []
255
- for (const r of rows) {
256
- const links = await db.execute({
257
- sql: `SELECT m.path, l.relation_type FROM links l JOIN memories m ON l.target_id = m.id WHERE l.source_id = ?`,
258
- args: [r.id],
259
- })
260
- const linkText = links.rows.length > 0 ? `\n🔗 Related: ${links.rows.map(l => `${l.path} (${l.relation_type})`).join(', ')}` : ''
261
- formattedResults.push(`[${r.path} | P:${r.priority}] ${r.content}${linkText}`)
262
- }
263
- return { content: [{ type: 'text', text: formattedResults.join('\n\n') || 'No results.' }] }
264
- }
265
-
266
- if (name === 'inspect') {
267
- const result = await db.execute(`
268
- SELECT m1.path as src, m2.path as tgt, l.relation_type as rel
269
- FROM links l
270
- JOIN memories m1 ON l.source_id = m1.id
271
- JOIN memories m2 ON l.target_id = m2.id
272
- `)
273
- let mermaid = 'graph TD\n'
274
- result.rows.forEach(r => {
275
- mermaid += ` ${r.src.replace(/\./g, '_')}["${r.src}"] -- "${r.rel}" --> ${r.tgt.replace(/\./g, '_')}["${r.tgt}"]\n`
276
- })
277
- return { content: [{ type: 'text', text: `### Knowledge Graph\n\n\`\`\`mermaid\n${mermaid}\`\`\`` }] }
278
- }
279
-
280
- if (name === 'list') {
281
- const result = await db.execute(`SELECT DISTINCT path FROM memories ORDER BY path`)
282
- return { content: [{ type: 'text', text: result.rows.map(r => `- ${r.path}`).join('\n') || 'Empty.' }] }
283
- }
284
-
285
- throw new Error('Unknown tool.')
286
- } catch (error) {
287
- return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true }
288
- }
289
- })
290
-
291
- return server
292
- }
293
-
294
- // ==========================================
295
- // 4. CONNECTION HANDLING (STDIO + HTTP)
296
- // ==========================================
297
-
298
- // 4.1. STDIO Connection
299
- const stdioServer = createMcpServer()
300
- const stdioTransport = new StdioServerTransport()
301
- stdioServer.connect(stdioTransport).catch(err => console.error('STDIO Error:', err))
302
- console.error('RiLiGar Agents Memories running via STDIO.')
303
-
304
- // 4.2. Express Server with SDK SSE Transport
305
- const app = express()
306
- app.use(cors())
307
- // Note: We do NOT use express.json() because SSEServerTransport handles body parsing manually via getRawBody.
308
-
309
- const sseSessions = new Map()
310
-
311
- app.get('/health', (req, res) => {
312
- res.status(200).send('OK')
313
- })
314
-
315
- app.get('/sse', async (req, res) => {
316
- const transport = new SSEServerTransport('/messages', res)
317
- const sessionServer = createMcpServer()
318
- await sessionServer.connect(transport)
319
-
320
- const sessionId = transport.sessionId
321
- sseSessions.set(sessionId, transport)
322
- console.error(`SSE session started: ${sessionId}`)
323
-
324
- res.on('close', () => {
325
- console.error(`SSE session closed: ${sessionId}`)
326
- sseSessions.delete(sessionId)
327
- })
328
- })
329
-
330
- app.post('/messages', async (req, res) => {
331
- const sessionId = req.query.sessionId
332
- const transport = sseSessions.get(sessionId)
333
-
334
- if (!transport) {
335
- return res.status(404).send('Session not found')
336
- }
337
-
338
- await transport.handlePostMessage(req, res)
339
- })
340
-
341
- const PORT = 3000
342
- app.listen(PORT, () => {
343
- console.error(`RiLiGar Agents Memories (Express) running at http://localhost:${PORT}`)
344
- })
3
+ export { MemorySystem } from './sdk/memory-system.js'
4
+ export { getSystemIdentity } from './core/identity.js'
5
+ export * from './core/logic.js'
6
+ export * from './database/schema.js'
7
+ export { createMcpServer } from './server/mcp-server.js'
@@ -0,0 +1,143 @@
1
+ import crypto from 'crypto'
2
+ import { getEmbedding, inferPriority, propagateRipple, findClosestNeighbor } from '../core/logic.js'
3
+ import { initializeSchema } from '../database/schema.js'
4
+
5
+ /**
6
+ * MemorySystem: A interface programática do RiLiGar Agents Memories.
7
+ * Desacoplada de transporte (SSE/Stdio) e orquestração.
8
+ */
9
+ export class MemorySystem {
10
+ constructor(db, ownerId = null) {
11
+ this.db = db
12
+ this.ownerId = ownerId
13
+ }
14
+
15
+ /**
16
+ * Inicializa o sistema (Schema).
17
+ */
18
+ async init() {
19
+ await initializeSchema(this.db)
20
+ }
21
+
22
+ /**
23
+ * Salva uma memória com Ressonância e Magnetismo Semântico.
24
+ */
25
+ async save(args) {
26
+ const rawContent = args.content
27
+ const defaultPath = args.path || 'General'
28
+ const memoriesToSave = Array.isArray(rawContent)
29
+ ? rawContent.map(m => (typeof m === 'string' ? { content: m } : m))
30
+ : [{ content: rawContent, path: args.path, owner_id: args.owner_id, priority: args.priority }]
31
+
32
+ const results = []
33
+ for (const mem of memoriesToSave) {
34
+ if (!mem.content) continue
35
+ const vector = await getEmbedding(mem.content)
36
+ const path = mem.path || defaultPath
37
+ const ownerId = mem.owner_id || args.owner_id || this.ownerId
38
+
39
+ const neighbor = await findClosestNeighbor(this.db, vector)
40
+ const isManualPriority = typeof mem.priority === 'number'
41
+ const finalPriority = isManualPriority ? mem.priority : await inferPriority(this.db, vector)
42
+
43
+ const insertRes = await this.db.execute({
44
+ sql: `INSERT INTO memories (content, path, embedding, owner_id, priority) VALUES (?, ?, ?, ?, ?) RETURNING id`,
45
+ args: [mem.content, path, vector.buffer, ownerId, finalPriority],
46
+ })
47
+ const newId = insertRes.rows[0].id
48
+
49
+ // Semantic Magnet: Auto-bridge near-identical facts
50
+ if (neighbor && 1 - neighbor.distance > 0.9) {
51
+ await this.db.execute({
52
+ sql: `INSERT OR IGNORE INTO links (source_id, target_id, relation_type) VALUES (?, ?, ?)`,
53
+ args: [newId, neighbor.id, 'similar_to'],
54
+ })
55
+ }
56
+
57
+ if (isManualPriority) await propagateRipple(this.db, vector, finalPriority)
58
+ results.push(path)
59
+ }
60
+
61
+ return { results, count: results.length }
62
+ }
63
+
64
+ /**
65
+ * Busca semântica relacional.
66
+ */
67
+ async search(args) {
68
+ const vector = await getEmbedding(args.query)
69
+ let sql = `SELECT id, content, path, priority, created_at, vector_distance_cos(embedding, ?) as distance FROM memories`
70
+ const queryArgs = [vector.buffer], filters = []
71
+
72
+ if (args.path_filter) {
73
+ filters.push(`path LIKE ?`)
74
+ queryArgs.push(args.path_filter)
75
+ }
76
+
77
+ if (filters.length > 0) sql += ` WHERE ` + filters.join(' AND ')
78
+
79
+ const result = await this.db.execute({ sql, args: queryArgs })
80
+ const rows = result.rows
81
+ .map(r => ({
82
+ ...r,
83
+ score: (1 - r.distance) * 0.6 + (1 / (1 + (new Date() - new Date(r.created_at)) / 86400000)) * 0.4
84
+ }))
85
+ .sort((a, b) => b.score - a.score)
86
+ .slice(0, 5)
87
+
88
+ const formattedResults = []
89
+ for (const r of rows) {
90
+ const links = await this.db.execute({
91
+ sql: `SELECT m.path, l.relation_type FROM links l JOIN memories m ON l.target_id = m.id WHERE l.source_id = ?`,
92
+ args: [r.id],
93
+ })
94
+ formattedResults.push({
95
+ ...r,
96
+ links: links.rows
97
+ })
98
+ }
99
+ return formattedResults
100
+ }
101
+
102
+ /**
103
+ * Cria uma ponte manual entre dois caminhos.
104
+ */
105
+ async bridge(args) {
106
+ const src = await this.db.execute({ sql: `SELECT id FROM memories WHERE path = ? LIMIT 1`, args: [args.source_path] })
107
+ const tgt = await this.db.execute({ sql: `SELECT id FROM memories WHERE path = ? LIMIT 1`, args: [args.target_path] })
108
+
109
+ if (!src.rows[0] || !tgt.rows[0]) throw new Error('Path(s) not found.')
110
+
111
+ await this.db.execute({
112
+ sql: `INSERT OR IGNORE INTO links (source_id, target_id, relation_type) VALUES (?, ?, ?)`,
113
+ args: [src.rows[0].id, tgt.rows[0].id, args.relation || 'related_to'],
114
+ })
115
+
116
+ return true
117
+ }
118
+
119
+ /**
120
+ * Gera o Knowledge Graph em formato Mermaid.
121
+ */
122
+ async inspect() {
123
+ const result = await this.db.execute(`
124
+ SELECT m1.path as src, m2.path as tgt, l.relation_type as rel
125
+ FROM links l
126
+ JOIN memories m1 ON l.source_id = m1.id
127
+ JOIN memories m2 ON l.target_id = m2.id
128
+ `)
129
+ let mermaid = 'graph TD\n'
130
+ result.rows.forEach(r => {
131
+ mermaid += ` ${r.src.replace(/\./g, '_')}["${r.src}"] -- "${r.rel}" --> ${r.tgt.replace(/\./g, '_')}["${r.tgt}"]\n`
132
+ })
133
+ return mermaid
134
+ }
135
+
136
+ /**
137
+ * Lista caminhos únicos.
138
+ */
139
+ async list() {
140
+ const result = await this.db.execute(`SELECT DISTINCT path FROM memories ORDER BY path`)
141
+ return result.rows.map(r => r.path)
142
+ }
143
+ }
@@ -0,0 +1,56 @@
1
+ import { createClient } from '@libsql/client'
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
3
+ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'
4
+ import express from 'express'
5
+ import cors from 'cors'
6
+ import path from 'path'
7
+
8
+ import { MemorySystem, getSystemIdentity, createMcpServer } from '../index.js'
9
+
10
+ // 1. SETUP
11
+ const DATA_DIR = 'data'
12
+ const SYSTEM_OWNER_ID = getSystemIdentity(DATA_DIR)
13
+ const db = createClient({ url: `file:${path.join(DATA_DIR, 'memories.db')}` })
14
+
15
+ const memorySystem = new MemorySystem(db, SYSTEM_OWNER_ID)
16
+ await memorySystem.init()
17
+
18
+ console.error(`RiLiGar Agents Memories initialized for Owner: ${SYSTEM_OWNER_ID}`)
19
+
20
+ // 2. TRANSPORTS
21
+ // 2.1. STDIO
22
+ const mcpServer = createMcpServer(memorySystem)
23
+ const stdioTransport = new StdioServerTransport()
24
+ mcpServer.connect(stdioTransport).catch(err => console.error('STDIO Error:', err))
25
+
26
+ // 2.2. SSE (Express)
27
+ const app = express()
28
+ app.use(cors())
29
+ const sseSessions = new Map()
30
+
31
+ app.get('/sse', async (req, res) => {
32
+ const transport = new SSEServerTransport('/messages', res)
33
+ const sessionServer = createMcpServer(memorySystem)
34
+ await sessionServer.connect(transport)
35
+
36
+ const sessionId = transport.sessionId
37
+ sseSessions.set(sessionId, transport)
38
+ console.error(`SSE session started: ${sessionId}`)
39
+
40
+ res.on('close', () => {
41
+ sseSessions.delete(sessionId)
42
+ console.error(`SSE session closed: ${sessionId}`)
43
+ })
44
+ })
45
+
46
+ app.post('/messages', async (req, res) => {
47
+ const sessionId = req.query.sessionId
48
+ const transport = sseSessions.get(sessionId)
49
+ if (!transport) return res.status(404).send('Session not found')
50
+ await transport.handlePostMessage(req, res)
51
+ })
52
+
53
+ const PORT = process.env.PORT || 3000
54
+ app.listen(PORT, () => {
55
+ console.error(`RiLiGar Agents Memories Standalone running at http://localhost:${PORT}`)
56
+ })
@@ -0,0 +1,158 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js'
2
+ import {
3
+ CallToolRequestSchema,
4
+ ListToolsRequestSchema,
5
+ ListResourcesRequestSchema,
6
+ ReadResourceRequestSchema
7
+ } from '@modelcontextprotocol/sdk/types.js'
8
+ import { SIP_PROTOCOL } from '../core/sip.js'
9
+
10
+ /**
11
+ * Factory para criar o servidor MCP usando o SDK do RiLiGar.
12
+ */
13
+ export function createMcpServer(memorySystem) {
14
+ const server = new Server(
15
+ { name: 'RiLiGar Agents Memories', version: '4.2.0' },
16
+ { capabilities: { tools: {}, resources: {} } }
17
+ )
18
+
19
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
20
+ return {
21
+ tools: [
22
+ {
23
+ name: 'save',
24
+ description: 'Saves info with Resonance and Auto-Bridging. SIP Note: Capture the Rationale (the "Why") to enable Pattern Mining.',
25
+ inputSchema: {
26
+ type: 'object',
27
+ properties: {
28
+ content: {
29
+ oneOf: [
30
+ { type: 'string' },
31
+ { type: 'array', items: { anyOf: [
32
+ { type: 'string' },
33
+ { type: 'object', properties: {
34
+ content: { type: 'string' },
35
+ path: { type: 'string' },
36
+ owner_id: { type: 'string' },
37
+ priority: { type: 'integer' }
38
+ }, required: ['content'] }
39
+ ] } }
40
+ ],
41
+ },
42
+ path: { type: 'string' },
43
+ owner_id: { type: 'string' },
44
+ priority: { type: 'integer' },
45
+ },
46
+ required: ['content'],
47
+ },
48
+ },
49
+ {
50
+ name: 'bridge',
51
+ description: 'Manually connects two memory paths.',
52
+ inputSchema: {
53
+ type: 'object',
54
+ properties: {
55
+ source_path: { type: 'string' },
56
+ target_path: { type: 'string' },
57
+ relation: { type: 'string', default: 'related_to' },
58
+ },
59
+ required: ['source_path', 'target_path'],
60
+ },
61
+ },
62
+ {
63
+ name: 'search',
64
+ description: 'Semantic search with Relational Awareness. Use for Pattern Mining and Error Avoidance (SIP).',
65
+ inputSchema: {
66
+ type: 'object',
67
+ properties: {
68
+ query: { type: 'string' },
69
+ path_filter: { type: 'string' },
70
+ },
71
+ required: ['query'],
72
+ },
73
+ },
74
+ {
75
+ name: 'inspect',
76
+ description: 'Visualizes the Knowledge Graph in Mermaid.js syntax. Use for Architecture Audit (SIP).',
77
+ inputSchema: { type: 'object', properties: {} },
78
+ },
79
+ {
80
+ name: 'list',
81
+ description: 'Lists all memory paths.',
82
+ inputSchema: { type: 'object', properties: {} },
83
+ },
84
+ ],
85
+ }
86
+ })
87
+
88
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
89
+ return {
90
+ resources: [
91
+ {
92
+ uri: 'sip://protocol',
93
+ name: 'Self-Improvement Protocol (SIP)',
94
+ description: 'Protocolo de autoaperfeiçoamento contínuo para agentes RiLiGar.',
95
+ mimeType: 'text/markdown',
96
+ },
97
+ ],
98
+ }
99
+ })
100
+
101
+ server.setRequestHandler(ReadResourceRequestSchema, async request => {
102
+ if (request.params.uri === 'sip://protocol') {
103
+ return {
104
+ contents: [
105
+ {
106
+ uri: 'sip://protocol',
107
+ mimeType: 'text/markdown',
108
+ text: SIP_PROTOCOL,
109
+ },
110
+ ],
111
+ }
112
+ }
113
+ throw new Error('Resource not found')
114
+ })
115
+
116
+ server.setRequestHandler(CallToolRequestSchema, async request => {
117
+ const { name, arguments: args } = request.params
118
+
119
+ try {
120
+ if (name === 'save') {
121
+ const res = await memorySystem.save(args)
122
+ return { content: [{ type: 'text', text: `Saved ${res.count} memories. Semantic Magnet applied.` }] }
123
+ }
124
+
125
+ if (name === 'bridge') {
126
+ await memorySystem.bridge(args)
127
+ return { content: [{ type: 'text', text: `Bridge created: [${args.source_path}] --(${args.relation})--> [${args.target_path}]` }] }
128
+ }
129
+
130
+ if (name === 'search') {
131
+ const rows = await memorySystem.search(args)
132
+ const formattedResults = rows.map(r => {
133
+ const linkText = r.links.length > 0
134
+ ? `\n🔗 Related: ${r.links.map(l => `${l.path} (${l.relation_type})`).join(', ')}`
135
+ : ''
136
+ return `[${r.path} | P:${r.priority}] ${r.content}${linkText}`
137
+ })
138
+ return { content: [{ type: 'text', text: formattedResults.join('\n\n') || 'No results.' }] }
139
+ }
140
+
141
+ if (name === 'inspect') {
142
+ const mermaid = await memorySystem.inspect()
143
+ return { content: [{ type: 'text', text: `### Knowledge Graph\n\n\`\`\`mermaid\n${mermaid}\`\`\`` }] }
144
+ }
145
+
146
+ if (name === 'list') {
147
+ const paths = await memorySystem.list()
148
+ return { content: [{ type: 'text', text: paths.map(p => `- ${p}`).join('\n') || 'Empty.' }] }
149
+ }
150
+
151
+ throw new Error('Unknown tool.')
152
+ } catch (error) {
153
+ return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true }
154
+ }
155
+ })
156
+
157
+ return server
158
+ }
package/src/test.js CHANGED
@@ -10,7 +10,7 @@ async function runTest() {
10
10
 
11
11
  const transport = new StdioClientTransport({
12
12
  command: process.platform === 'win32' ? 'bun.exe' : (process.env.HOME + '/.bun/bin/bun'),
13
- args: ['src/index.js']
13
+ args: ['src/server/index.js']
14
14
  })
15
15
 
16
16
  const client = new Client(
@@ -16,7 +16,7 @@ async function runIdentityTest() {
16
16
 
17
17
  const transport = new StdioClientTransport({
18
18
  command: (process.env.HOME + '/.bun/bin/bun'),
19
- args: ['src/index.js']
19
+ args: ['src/server/index.js']
20
20
  })
21
21
 
22
22
  const client = new Client(