@siduri-x/core 1.0.9 → 2.0.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.
@@ -0,0 +1,444 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SiduriDatabase = void 0;
4
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
5
+ const crypto = require('crypto');
6
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
7
+ const { DatabaseSync } = require('node:sqlite');
8
+ class SiduriDatabase {
9
+ db;
10
+ constructor(options = {}) {
11
+ const dbPath = options.dbPath || ':memory:';
12
+ this.db = new DatabaseSync(dbPath);
13
+ this.db.exec('PRAGMA journal_mode = WAL');
14
+ this.initSchema();
15
+ }
16
+ initSchema() {
17
+ const schema = `
18
+ -- Self Tables
19
+ CREATE TABLE IF NOT EXISTS self_identity (
20
+ companion_id TEXT PRIMARY KEY,
21
+ name TEXT NOT NULL,
22
+ archetype TEXT,
23
+ version TEXT NOT NULL,
24
+ updated_at TEXT DEFAULT (datetime('now'))
25
+ );
26
+
27
+ CREATE TABLE IF NOT EXISTS self_personality (
28
+ companion_id TEXT PRIMARY KEY,
29
+ warmth REAL DEFAULT 0.5,
30
+ formality REAL DEFAULT 0.5,
31
+ sarcasm REAL DEFAULT 0.5,
32
+ verbosity REAL DEFAULT 0.5,
33
+ curiosity REAL DEFAULT 0.5,
34
+ updated_at TEXT DEFAULT (datetime('now'))
35
+ );
36
+
37
+ CREATE TABLE IF NOT EXISTS self_directives (
38
+ id TEXT PRIMARY KEY,
39
+ companion_id TEXT NOT NULL,
40
+ priority INTEGER DEFAULT 50,
41
+ directive TEXT NOT NULL,
42
+ status TEXT DEFAULT 'ACTIVE',
43
+ category TEXT DEFAULT 'behavioral',
44
+ supersedes_id TEXT,
45
+ created_at TEXT DEFAULT (datetime('now'))
46
+ );
47
+
48
+ CREATE TABLE IF NOT EXISTS self_relationships (
49
+ companion_id TEXT NOT NULL,
50
+ entity_id TEXT NOT NULL,
51
+ entity_type TEXT NOT NULL,
52
+ trust_score REAL DEFAULT 0.5,
53
+ familiarity REAL DEFAULT 0.5,
54
+ interaction_conventions TEXT,
55
+ PRIMARY KEY(companion_id, entity_id)
56
+ );
57
+
58
+ -- Knowledge Tables
59
+ CREATE TABLE IF NOT EXISTS life_inventory (
60
+ id TEXT PRIMARY KEY,
61
+ companion_id TEXT NOT NULL,
62
+ domain TEXT NOT NULL,
63
+ entity_name TEXT NOT NULL,
64
+ properties TEXT NOT NULL,
65
+ updated_at TEXT DEFAULT (datetime('now'))
66
+ );
67
+
68
+ CREATE TABLE IF NOT EXISTS life_finance (
69
+ id TEXT PRIMARY KEY,
70
+ companion_id TEXT NOT NULL,
71
+ category TEXT NOT NULL,
72
+ amount REAL NOT NULL,
73
+ currency TEXT DEFAULT 'USD',
74
+ timestamp TEXT DEFAULT (datetime('now')),
75
+ metadata TEXT
76
+ );
77
+
78
+ CREATE TABLE IF NOT EXISTS life_schedule (
79
+ id TEXT PRIMARY KEY,
80
+ companion_id TEXT NOT NULL,
81
+ title TEXT NOT NULL,
82
+ start_time TEXT NOT NULL,
83
+ end_time TEXT,
84
+ is_recurring INTEGER DEFAULT 0,
85
+ status TEXT DEFAULT 'active'
86
+ );
87
+
88
+ CREATE TABLE IF NOT EXISTS life_preferences (
89
+ id TEXT PRIMARY KEY,
90
+ companion_id TEXT NOT NULL,
91
+ preference_key TEXT NOT NULL,
92
+ preference_value TEXT NOT NULL,
93
+ category TEXT NOT NULL,
94
+ updated_at TEXT DEFAULT (datetime('now'))
95
+ );
96
+
97
+ -- Memory Tables
98
+ CREATE TABLE IF NOT EXISTS memory_events (
99
+ id TEXT PRIMARY KEY,
100
+ companion_id TEXT NOT NULL,
101
+ source_type TEXT NOT NULL,
102
+ occurred_at TEXT DEFAULT (datetime('now')),
103
+ payload TEXT NOT NULL
104
+ );
105
+
106
+ CREATE TABLE IF NOT EXISTS memory_claims (
107
+ id TEXT PRIMARY KEY,
108
+ companion_id TEXT NOT NULL,
109
+ subject TEXT NOT NULL,
110
+ predicate TEXT NOT NULL,
111
+ value TEXT NOT NULL,
112
+ status TEXT DEFAULT 'PENDING',
113
+ confidence REAL DEFAULT 1.0,
114
+ valid_from TEXT,
115
+ valid_until TEXT,
116
+ evidence TEXT,
117
+ asserted_at TEXT DEFAULT (datetime('now'))
118
+ );
119
+
120
+ -- FTS5 Virtual Table for Memory Claims
121
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_search USING fts5(
122
+ subject,
123
+ predicate,
124
+ value,
125
+ content='memory_claims',
126
+ content_rowid='rowid'
127
+ );
128
+
129
+ -- FTS5 Triggers
130
+ CREATE TRIGGER IF NOT EXISTS memory_claims_ai AFTER INSERT ON memory_claims BEGIN
131
+ INSERT INTO memory_search(rowid, subject, predicate, value)
132
+ VALUES (new.rowid, new.subject, new.predicate, new.value);
133
+ END;
134
+
135
+ CREATE TRIGGER IF NOT EXISTS memory_claims_ad AFTER DELETE ON memory_claims BEGIN
136
+ INSERT INTO memory_search(memory_search, rowid, subject, predicate, value)
137
+ VALUES('delete', old.rowid, old.subject, old.predicate, old.value);
138
+ END;
139
+
140
+ CREATE TRIGGER IF NOT EXISTS memory_claims_au AFTER UPDATE ON memory_claims BEGIN
141
+ INSERT INTO memory_search(memory_search, rowid, subject, predicate, value)
142
+ VALUES('delete', old.rowid, old.subject, old.predicate, old.value);
143
+ INSERT INTO memory_search(rowid, subject, predicate, value)
144
+ VALUES (new.rowid, new.subject, new.predicate, new.value);
145
+ END;
146
+ `;
147
+ this.db.exec(schema);
148
+ }
149
+ close() {
150
+ this.db.close();
151
+ }
152
+ // ==========================================
153
+ // Self Domain Methods
154
+ // ==========================================
155
+ getIdentity(companionId) {
156
+ const stmt = this.db.prepare('SELECT * FROM self_identity WHERE companion_id = ?');
157
+ const row = stmt.get(companionId);
158
+ if (!row)
159
+ return undefined;
160
+ return {
161
+ companionId: row.companion_id,
162
+ name: row.name,
163
+ archetype: row.archetype || undefined,
164
+ version: row.version,
165
+ updatedAt: row.updated_at
166
+ };
167
+ }
168
+ setIdentity(identity) {
169
+ const stmt = this.db.prepare(`
170
+ INSERT INTO self_identity (companion_id, name, archetype, version, updated_at)
171
+ VALUES (?, ?, ?, ?, datetime('now'))
172
+ ON CONFLICT(companion_id) DO UPDATE SET
173
+ name = excluded.name,
174
+ archetype = excluded.archetype,
175
+ version = excluded.version,
176
+ updated_at = datetime('now')
177
+ `);
178
+ stmt.run(identity.companionId, identity.name, identity.archetype || null, identity.version);
179
+ }
180
+ getPersonality(companionId) {
181
+ const stmt = this.db.prepare('SELECT * FROM self_personality WHERE companion_id = ?');
182
+ const row = stmt.get(companionId);
183
+ if (!row)
184
+ return undefined;
185
+ return {
186
+ warmth: row.warmth,
187
+ formality: row.formality,
188
+ sarcasm: row.sarcasm,
189
+ verbosity: row.verbosity,
190
+ curiosity: row.curiosity
191
+ };
192
+ }
193
+ setPersonality(companionId, traits) {
194
+ const stmt = this.db.prepare(`
195
+ INSERT INTO self_personality (companion_id, warmth, formality, sarcasm, verbosity, curiosity, updated_at)
196
+ VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
197
+ ON CONFLICT(companion_id) DO UPDATE SET
198
+ warmth = excluded.warmth,
199
+ formality = excluded.formality,
200
+ sarcasm = excluded.sarcasm,
201
+ verbosity = excluded.verbosity,
202
+ curiosity = excluded.curiosity,
203
+ updated_at = datetime('now')
204
+ `);
205
+ stmt.run(companionId, traits.warmth, traits.formality, traits.sarcasm, traits.verbosity, traits.curiosity);
206
+ }
207
+ getActiveDirectives(companionId) {
208
+ const stmt = this.db.prepare(`
209
+ SELECT * FROM self_directives
210
+ WHERE companion_id = ? AND status = 'ACTIVE'
211
+ ORDER BY priority DESC, created_at ASC
212
+ `);
213
+ return stmt.all(companionId).map((row) => ({
214
+ id: row.id,
215
+ companionId: row.companion_id,
216
+ priority: row.priority,
217
+ directive: row.directive,
218
+ status: row.status,
219
+ category: row.category,
220
+ supersedesId: row.supersedes_id || undefined,
221
+ createdAt: row.created_at
222
+ }));
223
+ }
224
+ commitDirective(directive) {
225
+ const stmt = this.db.prepare(`
226
+ INSERT INTO self_directives (id, companion_id, priority, directive, status, category, supersedes_id, created_at)
227
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
228
+ `);
229
+ stmt.run(directive.id, directive.companionId, directive.priority, directive.directive, directive.status, directive.category, directive.supersedesId || null, directive.createdAt || new Date().toISOString());
230
+ }
231
+ disableDirective(id) {
232
+ const stmt = this.db.prepare("UPDATE self_directives SET status = 'DISABLED' WHERE id = ?");
233
+ stmt.run(id);
234
+ }
235
+ getRelationship(companionId, entityId) {
236
+ const stmt = this.db.prepare('SELECT * FROM self_relationships WHERE companion_id = ? AND entity_id = ?');
237
+ const row = stmt.get(companionId, entityId);
238
+ if (!row)
239
+ return undefined;
240
+ return {
241
+ companionId: row.companion_id,
242
+ entityId: row.entity_id,
243
+ entityType: row.entity_type,
244
+ trustScore: row.trust_score,
245
+ familiarity: row.familiarity,
246
+ interactionConventions: row.interaction_conventions ? JSON.parse(row.interaction_conventions) : []
247
+ };
248
+ }
249
+ upsertRelationship(rel) {
250
+ const stmt = this.db.prepare(`
251
+ INSERT INTO self_relationships (companion_id, entity_id, entity_type, trust_score, familiarity, interaction_conventions)
252
+ VALUES (?, ?, ?, ?, ?, ?)
253
+ ON CONFLICT(companion_id, entity_id) DO UPDATE SET
254
+ entity_type = excluded.entity_type,
255
+ trust_score = excluded.trust_score,
256
+ familiarity = excluded.familiarity,
257
+ interaction_conventions = excluded.interaction_conventions
258
+ `);
259
+ stmt.run(rel.companionId, rel.entityId, rel.entityType, rel.trustScore, rel.familiarity, JSON.stringify(rel.interactionConventions || []));
260
+ }
261
+ // ==========================================
262
+ // Knowledge / Life DB Methods
263
+ // ==========================================
264
+ getInventory(companionId, domain) {
265
+ let stmt;
266
+ let rows;
267
+ if (domain) {
268
+ stmt = this.db.prepare('SELECT * FROM life_inventory WHERE companion_id = ? AND domain = ?');
269
+ rows = stmt.all(companionId, domain);
270
+ }
271
+ else {
272
+ stmt = this.db.prepare('SELECT * FROM life_inventory WHERE companion_id = ?');
273
+ rows = stmt.all(companionId);
274
+ }
275
+ return rows.map((row) => ({
276
+ id: row.id,
277
+ companionId: row.companion_id,
278
+ domain: row.domain,
279
+ entityName: row.entity_name,
280
+ properties: JSON.parse(row.properties),
281
+ updatedAt: row.updated_at
282
+ }));
283
+ }
284
+ upsertInventoryItem(item) {
285
+ const stmt = this.db.prepare(`
286
+ INSERT INTO life_inventory (id, companion_id, domain, entity_name, properties, updated_at)
287
+ VALUES (?, ?, ?, ?, ?, datetime('now'))
288
+ ON CONFLICT(id) DO UPDATE SET
289
+ domain = excluded.domain,
290
+ entity_name = excluded.entity_name,
291
+ properties = excluded.properties,
292
+ updated_at = datetime('now')
293
+ `);
294
+ stmt.run(item.id, item.companionId, item.domain, item.entityName, JSON.stringify(item.properties));
295
+ }
296
+ getFinanceEntries(companionId, limit = 50) {
297
+ const stmt = this.db.prepare('SELECT * FROM life_finance WHERE companion_id = ? ORDER BY timestamp DESC LIMIT ?');
298
+ return stmt.all(companionId, limit).map((row) => ({
299
+ id: row.id,
300
+ companionId: row.companion_id,
301
+ category: row.category,
302
+ amount: row.amount,
303
+ currency: row.currency,
304
+ timestamp: row.timestamp,
305
+ metadata: row.metadata ? JSON.parse(row.metadata) : undefined
306
+ }));
307
+ }
308
+ addFinanceEntry(entry) {
309
+ const stmt = this.db.prepare(`
310
+ INSERT INTO life_finance (id, companion_id, category, amount, currency, timestamp, metadata)
311
+ VALUES (?, ?, ?, ?, ?, coalesce(?, datetime('now')), ?)
312
+ `);
313
+ stmt.run(entry.id, entry.companionId, entry.category, entry.amount, entry.currency || 'USD', entry.timestamp || null, entry.metadata ? JSON.stringify(entry.metadata) : null);
314
+ }
315
+ getSchedule(companionId) {
316
+ const stmt = this.db.prepare('SELECT * FROM life_schedule WHERE companion_id = ? AND status = ? ORDER BY start_time ASC');
317
+ return stmt.all(companionId, 'active').map((row) => ({
318
+ id: row.id,
319
+ companionId: row.companion_id,
320
+ title: row.title,
321
+ startTime: row.start_time,
322
+ endTime: row.end_time || undefined,
323
+ isRecurring: row.is_recurring === 1,
324
+ status: row.status
325
+ }));
326
+ }
327
+ upsertScheduleItem(item) {
328
+ const stmt = this.db.prepare(`
329
+ INSERT INTO life_schedule (id, companion_id, title, start_time, end_time, is_recurring, status)
330
+ VALUES (?, ?, ?, ?, ?, ?, ?)
331
+ ON CONFLICT(id) DO UPDATE SET
332
+ title = excluded.title,
333
+ start_time = excluded.start_time,
334
+ end_time = excluded.end_time,
335
+ is_recurring = excluded.is_recurring,
336
+ status = excluded.status
337
+ `);
338
+ stmt.run(item.id, item.companionId, item.title, item.startTime, item.endTime || null, item.isRecurring ? 1 : 0, item.status || 'active');
339
+ }
340
+ getPreferences(companionId) {
341
+ const stmt = this.db.prepare('SELECT * FROM life_preferences WHERE companion_id = ?');
342
+ return stmt.all(companionId).map((row) => ({
343
+ id: row.id,
344
+ companionId: row.companion_id,
345
+ preferenceKey: row.preference_key,
346
+ preferenceValue: row.preference_value,
347
+ category: row.category,
348
+ updatedAt: row.updated_at
349
+ }));
350
+ }
351
+ upsertPreference(pref) {
352
+ const stmt = this.db.prepare(`
353
+ INSERT INTO life_preferences (id, companion_id, preference_key, preference_value, category, updated_at)
354
+ VALUES (?, ?, ?, ?, ?, datetime('now'))
355
+ ON CONFLICT(id) DO UPDATE SET
356
+ preference_key = excluded.preference_key,
357
+ preference_value = excluded.preference_value,
358
+ category = excluded.category,
359
+ updated_at = datetime('now')
360
+ `);
361
+ stmt.run(pref.id, pref.companionId, pref.preferenceKey, pref.preferenceValue, pref.category);
362
+ }
363
+ // ==========================================
364
+ // Memory Domain Methods
365
+ // ==========================================
366
+ recordEvent(event) {
367
+ const stmt = this.db.prepare(`
368
+ INSERT INTO memory_events (id, companion_id, source_type, occurred_at, payload)
369
+ VALUES (?, ?, ?, coalesce(?, datetime('now')), ?)
370
+ `);
371
+ stmt.run(event.id, event.companionId, event.sourceType, event.occurredAt || null, JSON.stringify(event.payload));
372
+ }
373
+ getRecentEvents(companionId, limit = 50) {
374
+ const stmt = this.db.prepare('SELECT * FROM memory_events WHERE companion_id = ? ORDER BY occurred_at DESC LIMIT ?');
375
+ return stmt.all(companionId, limit).map((row) => ({
376
+ id: row.id,
377
+ companionId: row.companion_id,
378
+ sourceType: row.source_type,
379
+ occurredAt: row.occurred_at,
380
+ payload: JSON.parse(row.payload)
381
+ }));
382
+ }
383
+ proposeClaim(claim) {
384
+ const id = claim.id || crypto.randomUUID();
385
+ const status = 'PENDING';
386
+ const stmt = this.db.prepare(`
387
+ INSERT INTO memory_claims (id, companion_id, subject, predicate, value, status, confidence, valid_from, valid_until, evidence, asserted_at)
388
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, coalesce(?, datetime('now')))
389
+ `);
390
+ stmt.run(id, claim.companionId, claim.subject, claim.predicate, claim.value, status, claim.confidence ?? 1.0, claim.validFrom || null, claim.validUntil || null, claim.evidence ? JSON.stringify(claim.evidence) : null, claim.assertedAt || null);
391
+ return {
392
+ ...claim,
393
+ id,
394
+ status
395
+ };
396
+ }
397
+ approveClaim(id) {
398
+ const stmt = this.db.prepare("UPDATE memory_claims SET status = 'APPROVED' WHERE id = ?");
399
+ stmt.run(id);
400
+ }
401
+ rejectClaim(id) {
402
+ const stmt = this.db.prepare("UPDATE memory_claims SET status = 'REJECTED' WHERE id = ?");
403
+ stmt.run(id);
404
+ }
405
+ searchClaims(companionId, query, limit = 20) {
406
+ const stmt = this.db.prepare(`
407
+ SELECT c.* FROM memory_claims c
408
+ JOIN memory_search s ON c.rowid = s.rowid
409
+ WHERE c.companion_id = ? AND memory_search MATCH ?
410
+ ORDER BY rank
411
+ LIMIT ?
412
+ `);
413
+ return stmt.all(companionId, query, limit).map((row) => ({
414
+ id: row.id,
415
+ companionId: row.companion_id,
416
+ subject: row.subject,
417
+ predicate: row.predicate,
418
+ value: row.value,
419
+ status: row.status,
420
+ confidence: row.confidence,
421
+ validFrom: row.valid_from || undefined,
422
+ validUntil: row.valid_until || undefined,
423
+ evidence: row.evidence ? JSON.parse(row.evidence) : undefined,
424
+ assertedAt: row.asserted_at
425
+ }));
426
+ }
427
+ getApprovedClaims(companionId, limit = 50) {
428
+ const stmt = this.db.prepare("SELECT * FROM memory_claims WHERE companion_id = ? AND status = 'APPROVED' ORDER BY asserted_at DESC LIMIT ?");
429
+ return stmt.all(companionId, limit).map((row) => ({
430
+ id: row.id,
431
+ companionId: row.companion_id,
432
+ subject: row.subject,
433
+ predicate: row.predicate,
434
+ value: row.value,
435
+ status: row.status,
436
+ confidence: row.confidence,
437
+ validFrom: row.valid_from || undefined,
438
+ validUntil: row.valid_until || undefined,
439
+ evidence: row.evidence ? JSON.parse(row.evidence) : undefined,
440
+ assertedAt: row.asserted_at
441
+ }));
442
+ }
443
+ }
444
+ exports.SiduriDatabase = SiduriDatabase;
@@ -0,0 +1 @@
1
+ export {};