memory-lucia 2.5.2 → 2.5.4

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
@@ -4,7 +4,7 @@
4
4
  [![GitHub](https://img.shields.io/badge/github-memory--lucia-blue.svg)](https://github.com/wen521/memory-lucia-)
5
5
  [![License](https://img.shields.io/npm/l/memory-lucia.svg)](LICENSE)
6
6
 
7
- Advanced memory system for OpenClaw agents with priority analysis, learning tracking, decision recording, and skill evolution.
7
+ Local SQLite-based memory system for OpenClaw agents. All data stored locally with no external dependencies.
8
8
 
9
9
  ## šŸ“¦ Installation
10
10
 
@@ -104,7 +104,7 @@ SQLite backend with tables:
104
104
 
105
105
  ## šŸ“‹ Version
106
106
 
107
- Current: 2.5.2
107
+ Current: 2.5.4
108
108
 
109
109
  ## šŸ“„ License
110
110
 
package/SKILL.md CHANGED
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  name: memory-lucia
3
- version: 2.5.2
3
+ version: 2.5.4
4
4
  description: |
5
- Memory Lucia V2.5 - Advanced memory system with hybrid query, enhanced learning tracking,
6
- autonomous decision recording, and skill evolution monitoring.
7
- Use when: (1) Tracking learning progress with milestones, (2) Recording decisions with review scheduling,
8
- (3) Analyzing message priorities, (4) Monitoring skill evolution with proficiency levels,
9
- (5) Hybrid query with local-first + API fallback.
5
+ Memory Lucia - Local SQLite-based memory system for OpenClaw agents.
6
+ Features: (1) Priority analysis and tracking, (2) Learning progress with milestones,
7
+ (3) Decision recording with review scheduling, (4) Skill evolution monitoring,
8
+ (5) Local text search across all memory tables, (6) Automatic database backups.
9
+ All data stored locally in SQLite database. No external API calls.
10
10
  ---
11
11
 
12
12
  # Memory V2 Skill
@@ -184,8 +184,27 @@ FROM memory_learning l
184
184
  WHERE progress_status IN ('started', 'in_progress')
185
185
  ORDER BY updated_at DESC;
186
186
 
187
+ -- View: Weekly Learning Report
188
+ CREATE VIEW IF NOT EXISTS v_weekly_learning_report AS
189
+ SELECT
190
+ learning_topic,
191
+ topic_category,
192
+ progress_status,
193
+ progress_percentage,
194
+ milestone_count,
195
+ completed_milestones,
196
+ updated_at,
197
+ CASE
198
+ WHEN progress_percentage = 100 THEN 'completed'
199
+ WHEN progress_percentage > 0 THEN 'in_progress'
200
+ ELSE 'not_started'
201
+ END as status_label
202
+ FROM memory_learning
203
+ WHERE updated_at >= datetime('now', '-7 days')
204
+ ORDER BY updated_at DESC;
205
+
187
206
  -- View: High Priority Items
188
- CREATE VIEW IF NOT EXISTS v_high_priority AS
207
+ CREATE VIEW IF NOT EXISTS v_high_priority_messages AS
189
208
  SELECT
190
209
  'priority' as type,
191
210
  id,
@@ -0,0 +1 @@
1
+ V2.5 uses a new schema. No migration from V1/V2 is supported. Use init-demo.js for fresh setup.
package/modules/query.js CHANGED
@@ -1,15 +1,13 @@
1
1
  /**
2
- * Memory V2.5 - Hybrid Query Module
2
+ * Memory Query Module
3
3
  *
4
4
  * Features:
5
- * - Local-first query (SQLite)
6
- * - Semantic search (Qdrant)
7
- * - API fallback (LLM)
8
- * - Smart caching
9
- * - Cost tracking
5
+ * - Local SQLite query with text search
6
+ * - Multi-table search
7
+ * - Result ranking
10
8
  *
11
9
  * @module QueryModule
12
- * @version 2.5.0
10
+ * @version 2.5.3
13
11
  */
14
12
 
15
13
  const sqlite3 = require('sqlite3').verbose();
@@ -18,123 +16,166 @@ class QueryModule {
18
16
  constructor(options = {}) {
19
17
  this.dbPath = options.dbPath || './memory-v2.5.db';
20
18
  this.db = null;
21
- this.cache = new Map();
22
- this.cacheEnabled = options.cacheEnabled !== false;
23
- this.useAPIFallback = options.useAPIFallback !== false;
24
-
25
- this.costStats = {
26
- localQueries: 0,
27
- apiFallbacks: 0,
28
- totalTokens: 0
19
+ this.stats = {
20
+ totalQueries: 0,
21
+ resultsFound: 0
29
22
  };
30
23
  }
31
24
 
32
25
  async init() {
33
26
  this.db = new sqlite3.Database(this.dbPath);
34
- console.log('āœ… Query Module V2.5 initialized');
27
+ console.log('āœ… Query Module initialized');
35
28
  return this;
36
29
  }
37
30
 
38
- async query(queryText, options = {}) {
31
+ /**
32
+ * Search across all memory tables
33
+ * @param {string} queryText - Search text
34
+ * @param {Object} options - Search options
35
+ * @returns {Object} Search results
36
+ */
37
+ async search(queryText, options = {}) {
39
38
  const startTime = Date.now();
40
39
 
41
- console.log(`\nšŸ” Query: "${queryText}"`);
42
-
43
- // Try local first
44
- const localResults = await this.localQuery(queryText, options);
45
- if (localResults.length > 0) {
46
- this.costStats.localQueries++;
47
- return {
48
- source: 'local',
49
- results: localResults,
50
- time: Date.now() - startTime,
51
- cost: 0
52
- };
53
- }
54
-
55
- // Fallback to API if enabled
56
- if (this.useAPIFallback) {
57
- this.costStats.apiFallbacks++;
58
- // API call placeholder
59
- return {
60
- source: 'api',
61
- results: [],
62
- time: Date.now() - startTime,
63
- cost: 'tokens'
64
- };
65
- }
40
+ console.log(`\nšŸ” Search: "${queryText}"`);
66
41
 
67
- return {
68
- source: 'none',
69
- results: [],
70
- time: Date.now() - startTime,
71
- cost: 0
72
- };
73
- }
74
-
75
- async localQuery(queryText, options) {
76
- const pattern = `%${queryText}%`;
77
42
  const results = [];
78
43
 
79
- // Query learning table (handle missing table gracefully)
44
+ // Search learning table
80
45
  try {
81
- const learning = await this.queryTable('memory_learning', ['learning_topic', 'notes'], pattern);
82
- results.push(...learning.map(r => ({ ...r, _source: 'learning' })));
46
+ const learning = await this.searchLearning(queryText);
47
+ results.push(...learning.map(r => ({ ...r, _source: 'learning', _score: this.calculateScore(r, queryText) })));
83
48
  } catch (err) {
84
- // Table might not exist yet, skip
49
+ // Table might not exist
85
50
  }
86
51
 
87
- // Query priorities table
52
+ // Search priorities table
88
53
  try {
89
- const priorities = await this.queryTable('memory_priorities', ['message_text', 'reasoning'], pattern);
90
- results.push(...priorities.map(r => ({ ...r, _source: 'priority' })));
54
+ const priorities = await this.searchPriorities(queryText);
55
+ results.push(...priorities.map(r => ({ ...r, _source: 'priority', _score: this.calculateScore(r, queryText) })));
91
56
  } catch (err) {
92
- // Table might not exist yet, skip
57
+ // Table might not exist
93
58
  }
94
59
 
95
- // Query decisions table
60
+ // Search decisions table
96
61
  try {
97
- const decisions = await this.queryTable('memory_decisions', ['decision_summary', 'context'], pattern);
98
- results.push(...decisions.map(r => ({ ...r, _source: 'decision' })));
62
+ const decisions = await this.searchDecisions(queryText);
63
+ results.push(...decisions.map(r => ({ ...r, _source: 'decision', _score: this.calculateScore(r, queryText) })));
99
64
  } catch (err) {
100
- // Table might not exist yet, skip
65
+ // Table might not exist
101
66
  }
102
67
 
103
- // Query evolution table
68
+ // Search evolution table
104
69
  try {
105
- const evolution = await this.queryTable('memory_evolution', ['skill_name'], pattern);
106
- results.push(...evolution.map(r => ({ ...r, _source: 'evolution' })));
70
+ const evolution = await this.searchEvolution(queryText);
71
+ results.push(...evolution.map(r => ({ ...r, _source: 'evolution', _score: this.calculateScore(r, queryText) })));
107
72
  } catch (err) {
108
- // Table might not exist yet, skip
73
+ // Table might not exist
109
74
  }
110
75
 
111
- return results.slice(0, options.limit || 10);
76
+ // Sort by score and limit
77
+ results.sort((a, b) => b._score - a._score);
78
+ const limited = results.slice(0, options.limit || 10);
79
+
80
+ this.stats.totalQueries++;
81
+ this.stats.resultsFound += limited.length;
82
+
83
+ return {
84
+ query: queryText,
85
+ results: limited,
86
+ total: results.length,
87
+ time: Date.now() - startTime
88
+ };
112
89
  }
113
90
 
114
- async queryTable(table, fields, pattern) {
91
+ /**
92
+ * Search learning table
93
+ */
94
+ async searchLearning(queryText) {
95
+ const pattern = `%${queryText}%`;
115
96
  return new Promise((resolve, reject) => {
116
- // Build OR query for multiple fields
117
- const whereClause = fields.map(f => `${f} LIKE ?`).join(' OR ');
118
- const params = fields.map(() => pattern);
119
- const sql = `SELECT * FROM ${table} WHERE ${whereClause} LIMIT 10`;
120
-
121
- this.db.all(sql, params, (err, rows) => {
122
- if (err) {
123
- // Return empty array for missing tables
124
- if (err.message.includes('no such table')) {
125
- resolve([]);
126
- } else {
127
- reject(err);
128
- }
129
- } else {
130
- resolve(rows || []);
131
- }
97
+ const sql = `SELECT * FROM memory_learning
98
+ WHERE learning_topic LIKE ? OR notes LIKE ?
99
+ LIMIT 10`;
100
+ this.db.all(sql, [pattern, pattern], (err, rows) => {
101
+ if (err) reject(err);
102
+ else resolve(rows || []);
103
+ });
104
+ });
105
+ }
106
+
107
+ /**
108
+ * Search priorities table
109
+ */
110
+ async searchPriorities(queryText) {
111
+ const pattern = `%${queryText}%`;
112
+ return new Promise((resolve, reject) => {
113
+ const sql = `SELECT * FROM memory_priorities
114
+ WHERE context_summary LIKE ? OR keywords LIKE ?
115
+ LIMIT 10`;
116
+ this.db.all(sql, [pattern, pattern], (err, rows) => {
117
+ if (err) reject(err);
118
+ else resolve(rows || []);
119
+ });
120
+ });
121
+ }
122
+
123
+ /**
124
+ * Search decisions table
125
+ */
126
+ async searchDecisions(queryText) {
127
+ const pattern = `%${queryText}%`;
128
+ return new Promise((resolve, reject) => {
129
+ const sql = `SELECT * FROM memory_decisions
130
+ WHERE decision_question LIKE ? OR decision_context LIKE ? OR rationale LIKE ?
131
+ LIMIT 10`;
132
+ this.db.all(sql, [pattern, pattern, pattern], (err, rows) => {
133
+ if (err) reject(err);
134
+ else resolve(rows || []);
135
+ });
136
+ });
137
+ }
138
+
139
+ /**
140
+ * Search evolution table
141
+ */
142
+ async searchEvolution(queryText) {
143
+ const pattern = `%${queryText}%`;
144
+ return new Promise((resolve, reject) => {
145
+ const sql = `SELECT * FROM memory_evolution
146
+ WHERE skill_name LIKE ? OR skill_category LIKE ?
147
+ LIMIT 10`;
148
+ this.db.all(sql, [pattern, pattern], (err, rows) => {
149
+ if (err) reject(err);
150
+ else resolve(rows || []);
132
151
  });
133
152
  });
134
153
  }
135
154
 
155
+ /**
156
+ * Calculate relevance score
157
+ */
158
+ calculateScore(row, queryText) {
159
+ const query = queryText.toLowerCase();
160
+ let score = 0;
161
+
162
+ // Check all text fields
163
+ for (const key of Object.keys(row)) {
164
+ if (typeof row[key] === 'string') {
165
+ const value = row[key].toLowerCase();
166
+ if (value.includes(query)) {
167
+ score += 1;
168
+ // Bonus for exact match
169
+ if (value === query) score += 2;
170
+ }
171
+ }
172
+ }
173
+
174
+ return score;
175
+ }
176
+
136
177
  getStats() {
137
- return this.costStats;
178
+ return this.stats;
138
179
  }
139
180
 
140
181
  close() {
@@ -142,4 +183,4 @@ class QueryModule {
142
183
  }
143
184
  }
144
185
 
145
- module.exports = QueryModule;
186
+ module.exports = QueryModule;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memory-lucia",
3
- "version": "2.5.2",
3
+ "version": "2.5.4",
4
4
  "description": "Advanced memory system for OpenClaw agents with priority analysis, learning tracking, decision recording, and skill evolution",
5
5
  "main": "api/index.js",
6
6
  "scripts": {
@@ -1,213 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Memory V1 to V2 Migration Script
4
- * Migrates data from old memory format to V2 schema
5
- */
6
-
7
- const sqlite3 = require('sqlite3').verbose();
8
- const fs = require('fs');
9
- const path = require('path');
10
-
11
- class MemoryMigration {
12
- constructor(sourceDbPath, targetDbPath = './memory-v2.db') {
13
- this.sourceDbPath = sourceDbPath;
14
- this.targetDbPath = targetDbPath;
15
- this.sourceDb = null;
16
- this.targetDb = null;
17
- }
18
-
19
- async init() {
20
- // Check if source exists
21
- if (!fs.existsSync(this.sourceDbPath)) {
22
- throw new Error(`Source database not found: ${this.sourceDbPath}`);
23
- }
24
-
25
- // Open source database
26
- this.sourceDb = new sqlite3.Database(this.sourceDbPath, sqlite3.OPEN_READONLY);
27
-
28
- // Open/create target database
29
- this.targetDb = new sqlite3.Database(this.targetDbPath);
30
-
31
- console.log('āœ… Connected to source and target databases');
32
- }
33
-
34
- async runSchema() {
35
- const schemaPath = path.join(__dirname, '..', 'database', 'schema.sql');
36
-
37
- if (!fs.existsSync(schemaPath)) {
38
- throw new Error(`Schema file not found: ${schemaPath}`);
39
- }
40
-
41
- const schema = fs.readFileSync(schemaPath, 'utf8');
42
- const statements = schema
43
- .split(';')
44
- .map(s => s.trim())
45
- .filter(s => s.length > 0);
46
-
47
- for (const statement of statements) {
48
- await this.runTarget(statement);
49
- }
50
-
51
- console.log('āœ… Target database schema initialized');
52
- }
53
-
54
- async migratePriorities() {
55
- console.log('šŸ”„ Migrating priorities...');
56
-
57
- try {
58
- const rows = await this.allSource(
59
- "SELECT * FROM memory_priorities WHERE created_at >= datetime('now', '-90 days')"
60
- );
61
-
62
- for (const row of rows) {
63
- await this.runTarget(
64
- `INSERT INTO memory_priorities
65
- (msg_id, conv_id, priority_level, reasoning, category, created_at)
66
- VALUES (?, ?, ?, ?, ?, ?)`,
67
- [row.msg_id, row.conv_id, row.priority_level, row.reasoning, row.category, row.created_at]
68
- );
69
- }
70
-
71
- console.log(`āœ… Migrated ${rows.length} priority records`);
72
- } catch (err) {
73
- console.log('ā„¹ļø No priorities to migrate or table does not exist');
74
- }
75
- }
76
-
77
- async migrateLearning() {
78
- console.log('šŸ”„ Migrating learning records...');
79
-
80
- try {
81
- const rows = await this.allSource(
82
- "SELECT * FROM memory_learning WHERE status != 'abandoned'"
83
- );
84
-
85
- for (const row of rows) {
86
- await this.runTarget(
87
- `INSERT INTO memory_learning
88
- (msg_id, conv_id, topic, description, status, progress, started_at, updated_at, completed_at)
89
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
90
- [row.msg_id, row.conv_id, row.topic, row.description, row.status,
91
- row.progress, row.started_at, row.updated_at, row.completed_at]
92
- );
93
- }
94
-
95
- console.log(`āœ… Migrated ${rows.length} learning records`);
96
- } catch (err) {
97
- console.log('ā„¹ļø No learning records to migrate or table does not exist');
98
- }
99
- }
100
-
101
- async migrateDecisions() {
102
- console.log('šŸ”„ Migrating decisions...');
103
-
104
- try {
105
- const rows = await this.allSource(
106
- "SELECT * FROM memory_decisions WHERE status IN ('pending', 'implemented')"
107
- );
108
-
109
- for (const row of rows) {
110
- await this.runTarget(
111
- `INSERT INTO memory_decisions
112
- (msg_id, conv_id, summary, context, expected_outcome, actual_outcome,
113
- status, review_scheduled_at, reviewed_at, created_at)
114
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
115
- [row.msg_id, row.conv_id, row.summary, row.context, row.expected_outcome,
116
- row.actual_outcome, row.status, row.review_scheduled_at, row.reviewed_at, row.created_at]
117
- );
118
- }
119
-
120
- console.log(`āœ… Migrated ${rows.length} decision records`);
121
- } catch (err) {
122
- console.log('ā„¹ļø No decisions to migrate or table does not exist');
123
- }
124
- }
125
-
126
- async migrateEvolution() {
127
- console.log('šŸ”„ Migrating skill evolution...');
128
-
129
- try {
130
- const rows = await this.allSource(
131
- "SELECT * FROM memory_evolution WHERE usage_count > 0"
132
- );
133
-
134
- for (const row of rows) {
135
- await this.runTarget(
136
- `INSERT INTO memory_evolution
137
- (skill_name, category, usage_count, success_count, last_used_at, first_used_at)
138
- VALUES (?, ?, ?, ?, ?, ?)`,
139
- [row.skill_name, row.category, row.usage_count, row.success_count,
140
- row.last_used_at, row.first_used_at]
141
- );
142
- }
143
-
144
- console.log(`āœ… Migrated ${rows.length} skill evolution records`);
145
- } catch (err) {
146
- console.log('ā„¹ļø No evolution records to migrate or table does not exist');
147
- }
148
- }
149
-
150
- async runTarget(sql, params = []) {
151
- return new Promise((resolve, reject) => {
152
- this.targetDb.run(sql, params, function(err) {
153
- if (err) reject(err);
154
- else resolve({ id: this.lastID, changes: this.changes });
155
- });
156
- });
157
- }
158
-
159
- async allSource(sql, params = []) {
160
- return new Promise((resolve, reject) => {
161
- this.sourceDb.all(sql, params, (err, rows) => {
162
- if (err) reject(err);
163
- else resolve(rows);
164
- });
165
- });
166
- }
167
-
168
- async close() {
169
- if (this.sourceDb) {
170
- await new Promise((resolve) => this.sourceDb.close(resolve));
171
- }
172
- if (this.targetDb) {
173
- await new Promise((resolve) => this.targetDb.close(resolve));
174
- }
175
- console.log('āœ… Database connections closed');
176
- }
177
-
178
- async migrate() {
179
- try {
180
- await this.init();
181
- await this.runSchema();
182
- await this.migratePriorities();
183
- await this.migrateLearning();
184
- await this.migrateDecisions();
185
- await this.migrateEvolution();
186
-
187
- console.log('\nšŸŽ‰ Migration completed successfully!');
188
- console.log(`šŸ“ New database: ${path.resolve(this.targetDbPath)}`);
189
- } catch (err) {
190
- console.error('\nāŒ Migration failed:', err.message);
191
- process.exit(1);
192
- } finally {
193
- await this.close();
194
- }
195
- }
196
- }
197
-
198
- // CLI usage
199
- if (require.main === module) {
200
- const sourceDb = process.argv[2];
201
- const targetDb = process.argv[3] || './memory-v2.db';
202
-
203
- if (!sourceDb) {
204
- console.log('Usage: node migrations/v1-to-v2.js <source-v1.db> [target-v2.db]');
205
- console.log('Example: node migrations/v1-to-v2.js ./memory-v1.db ./memory-v2.db');
206
- process.exit(1);
207
- }
208
-
209
- const migration = new MemoryMigration(sourceDb, targetDb);
210
- migration.migrate();
211
- }
212
-
213
- module.exports = MemoryMigration;