@siduri-x/core 2.0.2 → 2.0.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/dist/chat-contract.d.ts +4 -0
- package/dist/chat-contract.js +17 -2
- package/dist/context.d.ts +2 -0
- package/dist/context.js +13 -0
- package/dist/index.d.ts +13 -5
- package/dist/intent-classifier.d.ts +4 -2
- package/dist/intent-classifier.js +32 -1
- package/dist/intent-classifier.test.js +52 -0
- package/dist/memory-settler.d.ts +2 -1
- package/dist/memory-settler.js +10 -2
- package/dist/mouth-types.d.ts +7 -2
- package/dist/perception-cycle.test.js +134 -0
- package/dist/perception-pipeline.d.ts +1 -0
- package/dist/perception-pipeline.js +10 -0
- package/dist/prompt-compiler.d.ts +3 -1
- package/dist/prompt-compiler.js +11 -1
- package/dist/proposals.d.ts +6 -2
- package/dist/response-envelope.d.ts +5 -1
- package/dist/response-envelope.js +14 -3
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +2 -1
- package/dist/siduri-db.d.ts +34 -11
- package/dist/siduri-db.js +208 -134
- package/dist/siduri-db.test.js +18 -18
- package/package.json +1 -1
package/dist/siduri-db.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SiduriDatabase = void 0;
|
|
4
|
+
exports.normalizeStatus = normalizeStatus;
|
|
4
5
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
5
6
|
const crypto = require('crypto');
|
|
6
7
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
7
8
|
const { DatabaseSync } = require('node:sqlite');
|
|
9
|
+
function normalizeStatus(status, defaultStatus = 'pending') {
|
|
10
|
+
if (!status)
|
|
11
|
+
return defaultStatus;
|
|
12
|
+
return status.toLowerCase().replace(/_/g, '-');
|
|
13
|
+
}
|
|
8
14
|
class SiduriDatabase {
|
|
9
15
|
db;
|
|
10
16
|
constructor(options = {}) {
|
|
@@ -20,6 +26,8 @@ class SiduriDatabase {
|
|
|
20
26
|
companion_id TEXT PRIMARY KEY,
|
|
21
27
|
name TEXT NOT NULL,
|
|
22
28
|
archetype TEXT,
|
|
29
|
+
origin TEXT,
|
|
30
|
+
ethos TEXT,
|
|
23
31
|
version TEXT NOT NULL,
|
|
24
32
|
updated_at TEXT DEFAULT (datetime('now'))
|
|
25
33
|
);
|
|
@@ -39,8 +47,9 @@ class SiduriDatabase {
|
|
|
39
47
|
companion_id TEXT NOT NULL,
|
|
40
48
|
priority INTEGER DEFAULT 50,
|
|
41
49
|
directive TEXT NOT NULL,
|
|
42
|
-
status TEXT DEFAULT '
|
|
50
|
+
status TEXT DEFAULT 'active',
|
|
43
51
|
category TEXT DEFAULT 'behavioral',
|
|
52
|
+
scope_actor TEXT,
|
|
44
53
|
supersedes_id TEXT,
|
|
45
54
|
created_at TEXT DEFAULT (datetime('now'))
|
|
46
55
|
);
|
|
@@ -48,13 +57,24 @@ class SiduriDatabase {
|
|
|
48
57
|
CREATE TABLE IF NOT EXISTS self_relationships (
|
|
49
58
|
companion_id TEXT NOT NULL,
|
|
50
59
|
entity_id TEXT NOT NULL,
|
|
51
|
-
entity_type TEXT NOT NULL,
|
|
60
|
+
entity_type TEXT NOT NULL DEFAULT 'human',
|
|
61
|
+
role TEXT DEFAULT 'user',
|
|
62
|
+
stance TEXT DEFAULT 'neutral',
|
|
52
63
|
trust_score REAL DEFAULT 0.5,
|
|
53
64
|
familiarity REAL DEFAULT 0.5,
|
|
54
65
|
interaction_conventions TEXT,
|
|
66
|
+
updated_at TEXT DEFAULT (datetime('now')),
|
|
55
67
|
PRIMARY KEY(companion_id, entity_id)
|
|
56
68
|
);
|
|
57
69
|
|
|
70
|
+
CREATE TABLE IF NOT EXISTS self_exemplars (
|
|
71
|
+
id TEXT PRIMARY KEY,
|
|
72
|
+
companion_id TEXT NOT NULL,
|
|
73
|
+
user_prompt TEXT NOT NULL,
|
|
74
|
+
companion_response TEXT NOT NULL,
|
|
75
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
76
|
+
);
|
|
77
|
+
|
|
58
78
|
-- Knowledge Tables
|
|
59
79
|
CREATE TABLE IF NOT EXISTS life_inventory (
|
|
60
80
|
id TEXT PRIMARY KEY,
|
|
@@ -109,7 +129,7 @@ class SiduriDatabase {
|
|
|
109
129
|
subject TEXT NOT NULL,
|
|
110
130
|
predicate TEXT NOT NULL,
|
|
111
131
|
value TEXT NOT NULL,
|
|
112
|
-
status TEXT DEFAULT '
|
|
132
|
+
status TEXT DEFAULT 'pending',
|
|
113
133
|
confidence REAL DEFAULT 1.0,
|
|
114
134
|
valid_from TEXT,
|
|
115
135
|
valid_until TEXT,
|
|
@@ -159,6 +179,42 @@ class SiduriDatabase {
|
|
|
159
179
|
catch {
|
|
160
180
|
// Column already exists
|
|
161
181
|
}
|
|
182
|
+
try {
|
|
183
|
+
this.db.exec("ALTER TABLE self_identity ADD COLUMN origin TEXT");
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
// Column already exists
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
this.db.exec("ALTER TABLE self_identity ADD COLUMN ethos TEXT");
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// Column already exists
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
this.db.exec("ALTER TABLE self_directives ADD COLUMN scope_actor TEXT");
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// Column already exists
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
this.db.exec("ALTER TABLE self_relationships ADD COLUMN role TEXT DEFAULT 'user'");
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// Column already exists
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
this.db.exec("ALTER TABLE self_relationships ADD COLUMN stance TEXT DEFAULT 'neutral'");
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// Column already exists
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
this.db.exec("ALTER TABLE self_relationships ADD COLUMN updated_at TEXT");
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
// Column already exists
|
|
217
|
+
}
|
|
162
218
|
}
|
|
163
219
|
close() {
|
|
164
220
|
this.db.close();
|
|
@@ -175,21 +231,25 @@ class SiduriDatabase {
|
|
|
175
231
|
companionId: row.companion_id,
|
|
176
232
|
name: row.name,
|
|
177
233
|
archetype: row.archetype || undefined,
|
|
234
|
+
origin: row.origin || undefined,
|
|
235
|
+
ethos: row.ethos || undefined,
|
|
178
236
|
version: row.version,
|
|
179
237
|
updatedAt: row.updated_at
|
|
180
238
|
};
|
|
181
239
|
}
|
|
182
240
|
setIdentity(identity) {
|
|
183
241
|
const stmt = this.db.prepare(`
|
|
184
|
-
INSERT INTO self_identity (companion_id, name, archetype, version, updated_at)
|
|
185
|
-
VALUES (?, ?, ?, ?, datetime('now'))
|
|
242
|
+
INSERT INTO self_identity (companion_id, name, archetype, origin, ethos, version, updated_at)
|
|
243
|
+
VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
|
|
186
244
|
ON CONFLICT(companion_id) DO UPDATE SET
|
|
187
245
|
name = excluded.name,
|
|
188
246
|
archetype = excluded.archetype,
|
|
247
|
+
origin = excluded.origin,
|
|
248
|
+
ethos = excluded.ethos,
|
|
189
249
|
version = excluded.version,
|
|
190
250
|
updated_at = datetime('now')
|
|
191
251
|
`);
|
|
192
|
-
stmt.run(identity.companionId, identity.name, identity.archetype || null, identity.version);
|
|
252
|
+
stmt.run(identity.companionId, identity.name, identity.archetype || null, identity.origin || null, identity.ethos || null, identity.version);
|
|
193
253
|
}
|
|
194
254
|
getPersonality(companionId) {
|
|
195
255
|
const stmt = this.db.prepare('SELECT * FROM self_personality WHERE companion_id = ?');
|
|
@@ -218,29 +278,41 @@ class SiduriDatabase {
|
|
|
218
278
|
`);
|
|
219
279
|
stmt.run(companionId, traits.warmth, traits.formality, traits.sarcasm, traits.verbosity, traits.curiosity);
|
|
220
280
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
SELECT * FROM self_directives
|
|
224
|
-
WHERE companion_id = ? AND status = 'ACTIVE'
|
|
225
|
-
ORDER BY priority DESC, created_at ASC
|
|
226
|
-
`);
|
|
227
|
-
return stmt.all(companionId).map((row) => ({
|
|
281
|
+
rowToSelfDirective(row) {
|
|
282
|
+
return {
|
|
228
283
|
id: row.id,
|
|
229
284
|
companionId: row.companion_id,
|
|
230
285
|
priority: row.priority,
|
|
231
286
|
directive: row.directive,
|
|
232
|
-
status: row.status,
|
|
287
|
+
status: normalizeStatus(row.status, 'active'),
|
|
233
288
|
category: row.category,
|
|
289
|
+
scopeActor: row.scope_actor || undefined,
|
|
234
290
|
supersedesId: row.supersedes_id || undefined,
|
|
235
|
-
createdAt: row.created_at
|
|
236
|
-
}
|
|
291
|
+
createdAt: row.created_at,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
getActiveDirectives(companionId) {
|
|
295
|
+
const stmt = this.db.prepare(`
|
|
296
|
+
SELECT * FROM self_directives
|
|
297
|
+
WHERE companion_id = ? AND LOWER(status) = 'active'
|
|
298
|
+
ORDER BY priority DESC, created_at ASC
|
|
299
|
+
`);
|
|
300
|
+
return stmt.all(companionId).map((row) => this.rowToSelfDirective(row));
|
|
301
|
+
}
|
|
302
|
+
getAllDirectives(companionId) {
|
|
303
|
+
const stmt = this.db.prepare(`
|
|
304
|
+
SELECT * FROM self_directives
|
|
305
|
+
WHERE companion_id = ?
|
|
306
|
+
ORDER BY priority DESC, created_at ASC
|
|
307
|
+
`);
|
|
308
|
+
return stmt.all(companionId).map((row) => this.rowToSelfDirective(row));
|
|
237
309
|
}
|
|
238
310
|
commitDirective(directive) {
|
|
239
311
|
const stmt = this.db.prepare(`
|
|
240
|
-
INSERT INTO self_directives (id, companion_id, priority, directive, status, category, supersedes_id, created_at)
|
|
241
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
312
|
+
INSERT INTO self_directives (id, companion_id, priority, directive, status, category, scope_actor, supersedes_id, created_at)
|
|
313
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
242
314
|
`);
|
|
243
|
-
stmt.run(directive.id, directive.companionId, directive.priority, directive.directive, directive.status, directive.category, directive.supersedesId || null, directive.createdAt || new Date().toISOString());
|
|
315
|
+
stmt.run(directive.id, directive.companionId, directive.priority !== undefined ? directive.priority : 50, directive.directive, normalizeStatus(directive.status, 'active'), directive.category || 'behavioral', directive.scopeActor || null, directive.supersedesId || null, directive.createdAt || new Date().toISOString());
|
|
244
316
|
}
|
|
245
317
|
getDirective(id, companionId) {
|
|
246
318
|
const stmt = companionId
|
|
@@ -249,16 +321,7 @@ class SiduriDatabase {
|
|
|
249
321
|
const row = (companionId ? stmt.get(id, companionId) : stmt.get(id));
|
|
250
322
|
if (!row)
|
|
251
323
|
return undefined;
|
|
252
|
-
return
|
|
253
|
-
id: row.id,
|
|
254
|
-
companionId: row.companion_id,
|
|
255
|
-
priority: row.priority,
|
|
256
|
-
directive: row.directive,
|
|
257
|
-
status: row.status,
|
|
258
|
-
category: row.category,
|
|
259
|
-
supersedesId: row.supersedes_id || undefined,
|
|
260
|
-
createdAt: row.created_at,
|
|
261
|
-
};
|
|
324
|
+
return this.rowToSelfDirective(row);
|
|
262
325
|
}
|
|
263
326
|
approveDirective(id, companionId) {
|
|
264
327
|
const findStmt = companionId
|
|
@@ -268,28 +331,28 @@ class SiduriDatabase {
|
|
|
268
331
|
if (!row) {
|
|
269
332
|
return;
|
|
270
333
|
}
|
|
271
|
-
if (row.status !== '
|
|
272
|
-
throw new Error(`Cannot approve directive '${id}': invalid transition from status '${row.status}' to '
|
|
334
|
+
if (normalizeStatus(row.status) !== 'pending') {
|
|
335
|
+
throw new Error(`Cannot approve directive '${id}': invalid transition from status '${row.status}' to 'active' (only pending directives can be approved)`);
|
|
273
336
|
}
|
|
274
|
-
// If this directive supersedes an earlier directive, transition that prior directive to
|
|
337
|
+
// If this directive supersedes an earlier directive, transition that prior directive to superseded
|
|
275
338
|
if (row.supersedes_id) {
|
|
276
339
|
const supersededId = row.supersedes_id;
|
|
277
340
|
const effectiveCompanionId = companionId || row.companion_id;
|
|
278
341
|
if (effectiveCompanionId) {
|
|
279
|
-
const supersedeStmt = this.db.prepare("UPDATE self_directives SET status = '
|
|
342
|
+
const supersedeStmt = this.db.prepare("UPDATE self_directives SET status = 'superseded' WHERE id = ? AND companion_id = ?");
|
|
280
343
|
supersedeStmt.run(supersededId, effectiveCompanionId);
|
|
281
344
|
}
|
|
282
345
|
else {
|
|
283
|
-
const supersedeStmt = this.db.prepare("UPDATE self_directives SET status = '
|
|
346
|
+
const supersedeStmt = this.db.prepare("UPDATE self_directives SET status = 'superseded' WHERE id = ?");
|
|
284
347
|
supersedeStmt.run(supersededId);
|
|
285
348
|
}
|
|
286
349
|
}
|
|
287
350
|
if (companionId) {
|
|
288
|
-
const stmt = this.db.prepare("UPDATE self_directives SET status = '
|
|
351
|
+
const stmt = this.db.prepare("UPDATE self_directives SET status = 'active' WHERE id = ? AND companion_id = ? AND LOWER(status) = 'pending'");
|
|
289
352
|
stmt.run(id, companionId);
|
|
290
353
|
}
|
|
291
354
|
else {
|
|
292
|
-
const stmt = this.db.prepare("UPDATE self_directives SET status = '
|
|
355
|
+
const stmt = this.db.prepare("UPDATE self_directives SET status = 'active' WHERE id = ? AND LOWER(status) = 'pending'");
|
|
293
356
|
stmt.run(id);
|
|
294
357
|
}
|
|
295
358
|
}
|
|
@@ -301,45 +364,45 @@ class SiduriDatabase {
|
|
|
301
364
|
if (!row) {
|
|
302
365
|
return;
|
|
303
366
|
}
|
|
304
|
-
if (row.status !== '
|
|
305
|
-
throw new Error(`Cannot reject directive '${id}': invalid transition from status '${row.status}' to '
|
|
367
|
+
if (normalizeStatus(row.status) !== 'pending') {
|
|
368
|
+
throw new Error(`Cannot reject directive '${id}': invalid transition from status '${row.status}' to 'rejected' (only pending directives can be rejected)`);
|
|
306
369
|
}
|
|
307
370
|
if (companionId) {
|
|
308
|
-
const stmt = this.db.prepare("UPDATE self_directives SET status = '
|
|
371
|
+
const stmt = this.db.prepare("UPDATE self_directives SET status = 'rejected' WHERE id = ? AND companion_id = ? AND LOWER(status) = 'pending'");
|
|
309
372
|
stmt.run(id, companionId);
|
|
310
373
|
}
|
|
311
374
|
else {
|
|
312
|
-
const stmt = this.db.prepare("UPDATE self_directives SET status = '
|
|
375
|
+
const stmt = this.db.prepare("UPDATE self_directives SET status = 'rejected' WHERE id = ? AND LOWER(status) = 'pending'");
|
|
313
376
|
stmt.run(id);
|
|
314
377
|
}
|
|
315
378
|
}
|
|
316
379
|
revokeDirective(id, companionId) {
|
|
317
380
|
if (companionId) {
|
|
318
|
-
const stmt = this.db.prepare("UPDATE self_directives SET status = '
|
|
381
|
+
const stmt = this.db.prepare("UPDATE self_directives SET status = 'revoked' WHERE id = ? AND companion_id = ?");
|
|
319
382
|
stmt.run(id, companionId);
|
|
320
383
|
}
|
|
321
384
|
else {
|
|
322
|
-
const stmt = this.db.prepare("UPDATE self_directives SET status = '
|
|
385
|
+
const stmt = this.db.prepare("UPDATE self_directives SET status = 'revoked' WHERE id = ?");
|
|
323
386
|
stmt.run(id);
|
|
324
387
|
}
|
|
325
388
|
}
|
|
326
389
|
expireDirective(id, companionId) {
|
|
327
390
|
if (companionId) {
|
|
328
|
-
const stmt = this.db.prepare("UPDATE self_directives SET status = '
|
|
391
|
+
const stmt = this.db.prepare("UPDATE self_directives SET status = 'expired' WHERE id = ? AND companion_id = ?");
|
|
329
392
|
stmt.run(id, companionId);
|
|
330
393
|
}
|
|
331
394
|
else {
|
|
332
|
-
const stmt = this.db.prepare("UPDATE self_directives SET status = '
|
|
395
|
+
const stmt = this.db.prepare("UPDATE self_directives SET status = 'expired' WHERE id = ?");
|
|
333
396
|
stmt.run(id);
|
|
334
397
|
}
|
|
335
398
|
}
|
|
336
399
|
disableDirective(id, companionId) {
|
|
337
400
|
if (companionId) {
|
|
338
|
-
const stmt = this.db.prepare("UPDATE self_directives SET status = '
|
|
401
|
+
const stmt = this.db.prepare("UPDATE self_directives SET status = 'disabled' WHERE id = ? AND companion_id = ?");
|
|
339
402
|
stmt.run(id, companionId);
|
|
340
403
|
}
|
|
341
404
|
else {
|
|
342
|
-
const stmt = this.db.prepare("UPDATE self_directives SET status = '
|
|
405
|
+
const stmt = this.db.prepare("UPDATE self_directives SET status = 'disabled' WHERE id = ?");
|
|
343
406
|
stmt.run(id);
|
|
344
407
|
}
|
|
345
408
|
}
|
|
@@ -351,23 +414,66 @@ class SiduriDatabase {
|
|
|
351
414
|
return {
|
|
352
415
|
companionId: row.companion_id,
|
|
353
416
|
entityId: row.entity_id,
|
|
354
|
-
entityType: row.entity_type,
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
417
|
+
entityType: row.entity_type || 'human',
|
|
418
|
+
role: row.role || 'user',
|
|
419
|
+
stance: row.stance || 'neutral',
|
|
420
|
+
trustScore: row.trust_score !== undefined && row.trust_score !== null ? row.trust_score : 0.5,
|
|
421
|
+
familiarity: row.familiarity !== undefined && row.familiarity !== null ? row.familiarity : 0.5,
|
|
422
|
+
interactionConventions: row.interaction_conventions ? JSON.parse(row.interaction_conventions) : [],
|
|
423
|
+
updatedAt: row.updated_at || undefined,
|
|
358
424
|
};
|
|
359
425
|
}
|
|
426
|
+
getRelationships(companionId) {
|
|
427
|
+
const stmt = this.db.prepare('SELECT * FROM self_relationships WHERE companion_id = ? ORDER BY entity_id ASC');
|
|
428
|
+
return stmt.all(companionId).map((row) => ({
|
|
429
|
+
companionId: row.companion_id,
|
|
430
|
+
entityId: row.entity_id,
|
|
431
|
+
entityType: row.entity_type || 'human',
|
|
432
|
+
role: row.role || 'user',
|
|
433
|
+
stance: row.stance || 'neutral',
|
|
434
|
+
trustScore: row.trust_score !== undefined && row.trust_score !== null ? row.trust_score : 0.5,
|
|
435
|
+
familiarity: row.familiarity !== undefined && row.familiarity !== null ? row.familiarity : 0.5,
|
|
436
|
+
interactionConventions: row.interaction_conventions ? JSON.parse(row.interaction_conventions) : [],
|
|
437
|
+
updatedAt: row.updated_at || undefined,
|
|
438
|
+
}));
|
|
439
|
+
}
|
|
360
440
|
upsertRelationship(rel) {
|
|
361
441
|
const stmt = this.db.prepare(`
|
|
362
|
-
INSERT INTO self_relationships (companion_id, entity_id, entity_type, trust_score, familiarity, interaction_conventions)
|
|
363
|
-
VALUES (?, ?, ?, ?, ?,
|
|
442
|
+
INSERT INTO self_relationships (companion_id, entity_id, entity_type, role, stance, trust_score, familiarity, interaction_conventions, updated_at)
|
|
443
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
|
|
364
444
|
ON CONFLICT(companion_id, entity_id) DO UPDATE SET
|
|
365
445
|
entity_type = excluded.entity_type,
|
|
446
|
+
role = excluded.role,
|
|
447
|
+
stance = excluded.stance,
|
|
366
448
|
trust_score = excluded.trust_score,
|
|
367
449
|
familiarity = excluded.familiarity,
|
|
368
|
-
interaction_conventions = excluded.interaction_conventions
|
|
450
|
+
interaction_conventions = excluded.interaction_conventions,
|
|
451
|
+
updated_at = datetime('now')
|
|
369
452
|
`);
|
|
370
|
-
stmt.run(rel.companionId, rel.entityId, rel.entityType, rel.trustScore, rel.familiarity, JSON.stringify(rel.interactionConventions || []));
|
|
453
|
+
stmt.run(rel.companionId, rel.entityId, rel.entityType || 'human', rel.role || 'user', rel.stance || 'neutral', rel.trustScore !== undefined && rel.trustScore !== null ? rel.trustScore : 0.5, rel.familiarity !== undefined && rel.familiarity !== null ? rel.familiarity : 0.5, JSON.stringify(rel.interactionConventions || []));
|
|
454
|
+
}
|
|
455
|
+
getExemplars(companionId) {
|
|
456
|
+
const stmt = this.db.prepare('SELECT * FROM self_exemplars WHERE companion_id = ? ORDER BY created_at ASC');
|
|
457
|
+
return stmt.all(companionId).map((row) => ({
|
|
458
|
+
id: row.id,
|
|
459
|
+
companionId: row.companion_id,
|
|
460
|
+
user: row.user_prompt,
|
|
461
|
+
assistant: row.companion_response,
|
|
462
|
+
createdAt: row.created_at,
|
|
463
|
+
}));
|
|
464
|
+
}
|
|
465
|
+
setExemplars(companionId, exemplars) {
|
|
466
|
+
const delStmt = this.db.prepare('DELETE FROM self_exemplars WHERE companion_id = ?');
|
|
467
|
+
delStmt.run(companionId);
|
|
468
|
+
const insertStmt = this.db.prepare(`
|
|
469
|
+
INSERT INTO self_exemplars (id, companion_id, user_prompt, companion_response, created_at)
|
|
470
|
+
VALUES (?, ?, ?, ?, datetime('now'))
|
|
471
|
+
`);
|
|
472
|
+
for (let i = 0; i < exemplars.length; i++) {
|
|
473
|
+
const ex = exemplars[i];
|
|
474
|
+
const id = ex.id || `ex-${i + 1}-${Date.now()}`;
|
|
475
|
+
insertStmt.run(id, companionId, ex.user, ex.assistant);
|
|
476
|
+
}
|
|
371
477
|
}
|
|
372
478
|
// ==========================================
|
|
373
479
|
// Knowledge / Life DB Methods
|
|
@@ -504,9 +610,26 @@ class SiduriDatabase {
|
|
|
504
610
|
payload: JSON.parse(row.payload)
|
|
505
611
|
};
|
|
506
612
|
}
|
|
613
|
+
rowToMemoryClaim(row) {
|
|
614
|
+
return {
|
|
615
|
+
id: row.id,
|
|
616
|
+
companionId: row.companion_id,
|
|
617
|
+
subject: row.subject,
|
|
618
|
+
predicate: row.predicate,
|
|
619
|
+
value: row.value,
|
|
620
|
+
status: normalizeStatus(row.status, 'pending'),
|
|
621
|
+
confidence: row.confidence,
|
|
622
|
+
validFrom: row.valid_from || undefined,
|
|
623
|
+
validUntil: row.valid_until || undefined,
|
|
624
|
+
evidence: row.evidence ? (typeof row.evidence === 'string' ? JSON.parse(row.evidence) : row.evidence) : undefined,
|
|
625
|
+
assertedAt: row.asserted_at,
|
|
626
|
+
supersedes: row.supersedes || undefined,
|
|
627
|
+
sourceEventId: row.source_event_id || undefined,
|
|
628
|
+
};
|
|
629
|
+
}
|
|
507
630
|
proposeClaim(claim) {
|
|
508
631
|
const id = claim.id || crypto.randomUUID();
|
|
509
|
-
const status = '
|
|
632
|
+
const status = normalizeStatus(claim.status, 'pending');
|
|
510
633
|
const confidence = claim.confidence ?? 1.0;
|
|
511
634
|
const assertedAt = claim.assertedAt || new Date().toISOString();
|
|
512
635
|
const stmt = this.db.prepare(`
|
|
@@ -517,7 +640,7 @@ class SiduriDatabase {
|
|
|
517
640
|
return {
|
|
518
641
|
...claim,
|
|
519
642
|
id,
|
|
520
|
-
status,
|
|
643
|
+
status: status,
|
|
521
644
|
confidence,
|
|
522
645
|
assertedAt,
|
|
523
646
|
supersedes: claim.supersedes,
|
|
@@ -532,67 +655,67 @@ class SiduriDatabase {
|
|
|
532
655
|
if (!row) {
|
|
533
656
|
return;
|
|
534
657
|
}
|
|
535
|
-
if (row.status !== '
|
|
536
|
-
throw new Error(`Cannot approve claim '${id}': invalid transition from status '${row.status}' to '
|
|
658
|
+
if (normalizeStatus(row.status) !== 'pending') {
|
|
659
|
+
throw new Error(`Cannot approve claim '${id}': invalid transition from status '${row.status}' to 'approved' (only pending claims can be approved)`);
|
|
537
660
|
}
|
|
538
|
-
// If this claim supersedes an earlier claim, transition that prior claim to
|
|
661
|
+
// If this claim supersedes an earlier claim, transition that prior claim to superseded
|
|
539
662
|
if (row.supersedes) {
|
|
540
663
|
const supersededId = row.supersedes;
|
|
541
664
|
if (companionId) {
|
|
542
|
-
const supersedeStmt = this.db.prepare("UPDATE memory_claims SET status = '
|
|
665
|
+
const supersedeStmt = this.db.prepare("UPDATE memory_claims SET status = 'superseded' WHERE id = ? AND companion_id = ?");
|
|
543
666
|
supersedeStmt.run(supersededId, companionId);
|
|
544
667
|
}
|
|
545
668
|
else {
|
|
546
|
-
const supersedeStmt = this.db.prepare("UPDATE memory_claims SET status = '
|
|
669
|
+
const supersedeStmt = this.db.prepare("UPDATE memory_claims SET status = 'superseded' WHERE id = ?");
|
|
547
670
|
supersedeStmt.run(supersededId);
|
|
548
671
|
}
|
|
549
672
|
}
|
|
550
673
|
if (companionId) {
|
|
551
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = '
|
|
674
|
+
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'approved' WHERE id = ? AND companion_id = ? AND LOWER(status) = 'pending'");
|
|
552
675
|
stmt.run(id, companionId);
|
|
553
676
|
}
|
|
554
677
|
else {
|
|
555
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = '
|
|
678
|
+
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'approved' WHERE id = ? AND LOWER(status) = 'pending'");
|
|
556
679
|
stmt.run(id);
|
|
557
680
|
}
|
|
558
681
|
}
|
|
559
682
|
rejectClaim(id, companionId) {
|
|
560
683
|
if (companionId) {
|
|
561
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = '
|
|
684
|
+
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'rejected' WHERE id = ? AND companion_id = ?");
|
|
562
685
|
stmt.run(id, companionId);
|
|
563
686
|
}
|
|
564
687
|
else {
|
|
565
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = '
|
|
688
|
+
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'rejected' WHERE id = ?");
|
|
566
689
|
stmt.run(id);
|
|
567
690
|
}
|
|
568
691
|
}
|
|
569
692
|
revokeClaim(id, companionId) {
|
|
570
693
|
if (companionId) {
|
|
571
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = '
|
|
694
|
+
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'revoked' WHERE id = ? AND companion_id = ?");
|
|
572
695
|
stmt.run(id, companionId);
|
|
573
696
|
}
|
|
574
697
|
else {
|
|
575
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = '
|
|
698
|
+
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'revoked' WHERE id = ?");
|
|
576
699
|
stmt.run(id);
|
|
577
700
|
}
|
|
578
701
|
}
|
|
579
702
|
expireClaim(id, companionId) {
|
|
580
703
|
if (companionId) {
|
|
581
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = '
|
|
704
|
+
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'expired' WHERE id = ? AND companion_id = ?");
|
|
582
705
|
stmt.run(id, companionId);
|
|
583
706
|
}
|
|
584
707
|
else {
|
|
585
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = '
|
|
708
|
+
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'expired' WHERE id = ?");
|
|
586
709
|
stmt.run(id);
|
|
587
710
|
}
|
|
588
711
|
}
|
|
589
712
|
markClaimSessionOnly(id, companionId) {
|
|
590
713
|
if (companionId) {
|
|
591
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = '
|
|
714
|
+
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'session-only' WHERE id = ? AND companion_id = ?");
|
|
592
715
|
stmt.run(id, companionId);
|
|
593
716
|
}
|
|
594
717
|
else {
|
|
595
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = '
|
|
718
|
+
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'session-only' WHERE id = ?");
|
|
596
719
|
stmt.run(id);
|
|
597
720
|
}
|
|
598
721
|
}
|
|
@@ -600,82 +723,33 @@ class SiduriDatabase {
|
|
|
600
723
|
const stmt = this.db.prepare(`
|
|
601
724
|
SELECT c.* FROM memory_claims c
|
|
602
725
|
JOIN memory_search s ON c.rowid = s.rowid
|
|
603
|
-
WHERE c.companion_id = ? AND c.status = '
|
|
726
|
+
WHERE c.companion_id = ? AND LOWER(c.status) = 'approved' AND memory_search MATCH ?
|
|
604
727
|
ORDER BY rank
|
|
605
728
|
LIMIT ?
|
|
606
729
|
`);
|
|
607
|
-
return stmt.all(companionId, query, limit).map((row) => (
|
|
608
|
-
id: row.id,
|
|
609
|
-
companionId: row.companion_id,
|
|
610
|
-
subject: row.subject,
|
|
611
|
-
predicate: row.predicate,
|
|
612
|
-
value: row.value,
|
|
613
|
-
status: row.status,
|
|
614
|
-
confidence: row.confidence,
|
|
615
|
-
validFrom: row.valid_from || undefined,
|
|
616
|
-
validUntil: row.valid_until || undefined,
|
|
617
|
-
evidence: row.evidence ? JSON.parse(row.evidence) : undefined,
|
|
618
|
-
assertedAt: row.asserted_at,
|
|
619
|
-
supersedes: row.supersedes || undefined,
|
|
620
|
-
sourceEventId: row.source_event_id || undefined,
|
|
621
|
-
}));
|
|
730
|
+
return stmt.all(companionId, query, limit).map((row) => this.rowToMemoryClaim(row));
|
|
622
731
|
}
|
|
623
732
|
getPendingClaims(companionId, limit = 50) {
|
|
624
|
-
const stmt = this.db.prepare("SELECT * FROM memory_claims WHERE companion_id = ? AND status = '
|
|
625
|
-
return stmt.all(companionId, limit).map((row) => (
|
|
626
|
-
id: row.id,
|
|
627
|
-
companionId: row.companion_id,
|
|
628
|
-
subject: row.subject,
|
|
629
|
-
predicate: row.predicate,
|
|
630
|
-
value: row.value,
|
|
631
|
-
status: row.status,
|
|
632
|
-
confidence: row.confidence,
|
|
633
|
-
validFrom: row.valid_from || undefined,
|
|
634
|
-
validUntil: row.valid_until || undefined,
|
|
635
|
-
evidence: row.evidence ? JSON.parse(row.evidence) : undefined,
|
|
636
|
-
assertedAt: row.asserted_at,
|
|
637
|
-
supersedes: row.supersedes || undefined,
|
|
638
|
-
sourceEventId: row.source_event_id || undefined,
|
|
639
|
-
}));
|
|
733
|
+
const stmt = this.db.prepare("SELECT * FROM memory_claims WHERE companion_id = ? AND LOWER(status) = 'pending' ORDER BY asserted_at DESC LIMIT ?");
|
|
734
|
+
return stmt.all(companionId, limit).map((row) => this.rowToMemoryClaim(row));
|
|
640
735
|
}
|
|
641
736
|
getApprovedClaims(companionId, limit = 50) {
|
|
642
|
-
const stmt = this.db.prepare("SELECT * FROM memory_claims WHERE companion_id = ? AND status = '
|
|
643
|
-
return stmt.all(companionId, limit).map((row) => (
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
validFrom: row.valid_from || undefined,
|
|
652
|
-
validUntil: row.valid_until || undefined,
|
|
653
|
-
evidence: row.evidence ? JSON.parse(row.evidence) : undefined,
|
|
654
|
-
assertedAt: row.asserted_at,
|
|
655
|
-
supersedes: row.supersedes || undefined,
|
|
656
|
-
sourceEventId: row.source_event_id || undefined,
|
|
657
|
-
}));
|
|
737
|
+
const stmt = this.db.prepare("SELECT * FROM memory_claims WHERE companion_id = ? AND LOWER(status) = 'approved' ORDER BY asserted_at DESC LIMIT ?");
|
|
738
|
+
return stmt.all(companionId, limit).map((row) => this.rowToMemoryClaim(row));
|
|
739
|
+
}
|
|
740
|
+
getAllClaims(companionId, limit = 100) {
|
|
741
|
+
const stmt = companionId
|
|
742
|
+
? this.db.prepare("SELECT * FROM memory_claims WHERE companion_id = ? ORDER BY asserted_at DESC LIMIT ?")
|
|
743
|
+
: this.db.prepare("SELECT * FROM memory_claims ORDER BY asserted_at DESC LIMIT ?");
|
|
744
|
+
const rows = companionId ? stmt.all(companionId, limit) : stmt.all(limit);
|
|
745
|
+
return rows.map((row) => this.rowToMemoryClaim(row));
|
|
658
746
|
}
|
|
659
747
|
getClaim(id) {
|
|
660
748
|
const stmt = this.db.prepare("SELECT * FROM memory_claims WHERE id = ?");
|
|
661
749
|
const row = stmt.get(id);
|
|
662
750
|
if (!row)
|
|
663
751
|
return undefined;
|
|
664
|
-
return
|
|
665
|
-
id: row.id,
|
|
666
|
-
companionId: row.companion_id,
|
|
667
|
-
subject: row.subject,
|
|
668
|
-
predicate: row.predicate,
|
|
669
|
-
value: row.value,
|
|
670
|
-
status: row.status,
|
|
671
|
-
confidence: row.confidence,
|
|
672
|
-
validFrom: row.valid_from || undefined,
|
|
673
|
-
validUntil: row.valid_until || undefined,
|
|
674
|
-
evidence: row.evidence ? JSON.parse(row.evidence) : undefined,
|
|
675
|
-
assertedAt: row.asserted_at,
|
|
676
|
-
supersedes: row.supersedes || undefined,
|
|
677
|
-
sourceEventId: row.source_event_id || undefined,
|
|
678
|
-
};
|
|
752
|
+
return this.rowToMemoryClaim(row);
|
|
679
753
|
}
|
|
680
754
|
resetMemory(companionId) {
|
|
681
755
|
const deleteClaims = this.db.prepare("DELETE FROM memory_claims WHERE companion_id = ?");
|