@sidurijs/core 1.0.0 → 1.0.2
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/LICENSE +190 -0
- package/dist/adversarial.test.js +27 -19
- package/dist/chat-contract.d.ts +9 -0
- package/dist/cognition-planner.d.ts +2 -2
- package/dist/container.d.ts +6 -4
- package/dist/container.js +1 -4
- package/dist/context-retriever.d.ts +5 -4
- package/dist/context-retriever.js +43 -25
- package/dist/conversational-teach.test.js +61 -53
- package/dist/evidence.d.ts +3 -3
- package/dist/gating.d.ts +2 -2
- package/dist/gating.js +1 -1
- package/dist/index.d.ts +43 -51
- package/dist/index.js +1 -1
- package/dist/intent-classifier.d.ts +1 -1
- package/dist/intent-classifier.js +4 -4
- package/dist/intent-classifier.test.js +1 -1
- package/dist/{memory-settler.d.ts → interaction-settler.d.ts} +12 -11
- package/dist/interaction-settler.js +173 -0
- package/dist/perception-cycle.test.js +18 -34
- package/dist/perception-pipeline.d.ts +6 -5
- package/dist/perception-pipeline.js +17 -15
- package/dist/prompt-compiler.d.ts +1 -1
- package/dist/prompt-compiler.js +11 -11
- package/dist/prompt-compiler.test.js +5 -5
- package/dist/proposals.d.ts +1 -2
- package/dist/response-envelope.d.ts +3 -3
- package/dist/response-envelope.js +5 -4
- package/dist/runtime-facades.test.js +12 -35
- package/dist/runtime.d.ts +16 -10
- package/dist/runtime.js +85 -107
- package/dist/schema-validator.test.js +3 -3
- package/dist/session-history.d.ts +3 -3
- package/dist/session-history.js +1 -1
- package/dist/siduri-db.d.ts +11 -29
- package/dist/siduri-db.js +159 -445
- package/dist/siduri-db.test.js +113 -281
- package/dist/teaching.d.ts +3 -3
- package/dist/teaching.js +1 -1
- package/package.json +7 -7
- package/dist/memory-settler.js +0 -161
package/dist/siduri-db.js
CHANGED
|
@@ -163,8 +163,8 @@ class SiduriDatabase {
|
|
|
163
163
|
CREATE INDEX IF NOT EXISTS idx_life_events_comp ON life_events(companion_id, stream, timestamp);
|
|
164
164
|
CREATE INDEX IF NOT EXISTS idx_life_tasks_comp ON life_tasks(companion_id, status);
|
|
165
165
|
|
|
166
|
-
--
|
|
167
|
-
CREATE TABLE IF NOT EXISTS
|
|
166
|
+
-- Archive Tables (RFC VX-26-13: Audited interaction ledger and cold search)
|
|
167
|
+
CREATE TABLE IF NOT EXISTS archive_events (
|
|
168
168
|
id TEXT PRIMARY KEY,
|
|
169
169
|
companion_id TEXT NOT NULL,
|
|
170
170
|
source_type TEXT NOT NULL,
|
|
@@ -172,47 +172,28 @@ class SiduriDatabase {
|
|
|
172
172
|
payload TEXT NOT NULL
|
|
173
173
|
);
|
|
174
174
|
|
|
175
|
-
CREATE TABLE IF NOT EXISTS
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
predicate TEXT NOT NULL,
|
|
180
|
-
value TEXT NOT NULL,
|
|
181
|
-
status TEXT DEFAULT 'pending',
|
|
182
|
-
confidence REAL DEFAULT 1.0,
|
|
183
|
-
valid_from TEXT,
|
|
184
|
-
valid_until TEXT,
|
|
185
|
-
evidence TEXT,
|
|
186
|
-
asserted_at TEXT DEFAULT (datetime('now')),
|
|
187
|
-
supersedes TEXT,
|
|
188
|
-
source_event_id TEXT
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
-- FTS5 Virtual Table for Memory Claims
|
|
192
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS memory_search USING fts5(
|
|
193
|
-
subject,
|
|
194
|
-
predicate,
|
|
195
|
-
value,
|
|
196
|
-
content='memory_claims',
|
|
175
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS archive_search USING fts5(
|
|
176
|
+
source_type,
|
|
177
|
+
payload,
|
|
178
|
+
content='archive_events',
|
|
197
179
|
content_rowid='rowid'
|
|
198
180
|
);
|
|
199
181
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
VALUES (new.rowid, new.subject, new.predicate, new.value);
|
|
182
|
+
CREATE TRIGGER IF NOT EXISTS archive_events_ai AFTER INSERT ON archive_events BEGIN
|
|
183
|
+
INSERT INTO archive_search(rowid, source_type, payload)
|
|
184
|
+
VALUES (new.rowid, new.source_type, new.payload);
|
|
204
185
|
END;
|
|
205
186
|
|
|
206
|
-
CREATE TRIGGER IF NOT EXISTS
|
|
207
|
-
INSERT INTO
|
|
208
|
-
VALUES('delete', old.rowid, old.
|
|
187
|
+
CREATE TRIGGER IF NOT EXISTS archive_events_ad AFTER DELETE ON archive_events BEGIN
|
|
188
|
+
INSERT INTO archive_search(archive_search, rowid, source_type, payload)
|
|
189
|
+
VALUES('delete', old.rowid, old.source_type, old.payload);
|
|
209
190
|
END;
|
|
210
191
|
|
|
211
|
-
CREATE TRIGGER IF NOT EXISTS
|
|
212
|
-
INSERT INTO
|
|
213
|
-
VALUES('delete', old.rowid, old.
|
|
214
|
-
INSERT INTO
|
|
215
|
-
VALUES (new.rowid, new.
|
|
192
|
+
CREATE TRIGGER IF NOT EXISTS archive_events_au AFTER UPDATE ON archive_events BEGIN
|
|
193
|
+
INSERT INTO archive_search(archive_search, rowid, source_type, payload)
|
|
194
|
+
VALUES('delete', old.rowid, old.source_type, old.payload);
|
|
195
|
+
INSERT INTO archive_search(rowid, source_type, payload)
|
|
196
|
+
VALUES (new.rowid, new.source_type, new.payload);
|
|
216
197
|
END;
|
|
217
198
|
|
|
218
199
|
-- System Logs Table
|
|
@@ -229,18 +210,6 @@ class SiduriDatabase {
|
|
|
229
210
|
CREATE INDEX IF NOT EXISTS idx_system_logs_comp ON system_logs(companion_id, level, created_at DESC);
|
|
230
211
|
`;
|
|
231
212
|
this.db.exec(schema);
|
|
232
|
-
try {
|
|
233
|
-
this.db.exec("ALTER TABLE memory_claims ADD COLUMN supersedes TEXT");
|
|
234
|
-
}
|
|
235
|
-
catch {
|
|
236
|
-
// Column already exists
|
|
237
|
-
}
|
|
238
|
-
try {
|
|
239
|
-
this.db.exec("ALTER TABLE memory_claims ADD COLUMN source_event_id TEXT");
|
|
240
|
-
}
|
|
241
|
-
catch {
|
|
242
|
-
// Column already exists
|
|
243
|
-
}
|
|
244
213
|
try {
|
|
245
214
|
this.db.exec("ALTER TABLE self_identity ADD COLUMN origin TEXT");
|
|
246
215
|
}
|
|
@@ -295,34 +264,6 @@ class SiduriDatabase {
|
|
|
295
264
|
catch {
|
|
296
265
|
// Column already exists
|
|
297
266
|
}
|
|
298
|
-
try {
|
|
299
|
-
// Reconcile creator claims that may have been recorded before origin dual-promotion
|
|
300
|
-
const creatorClaims = this.db.prepare(`
|
|
301
|
-
SELECT * FROM memory_claims
|
|
302
|
-
WHERE predicate = 'stated_relationship'
|
|
303
|
-
AND LOWER(value) LIKE '%creator%'
|
|
304
|
-
AND LOWER(status) = 'approved'
|
|
305
|
-
`).all();
|
|
306
|
-
for (const claim of creatorClaims) {
|
|
307
|
-
const identity = this.db.prepare(`SELECT * FROM self_identity WHERE companion_id = ?`).get(claim.companion_id);
|
|
308
|
-
if (identity && !identity.origin) {
|
|
309
|
-
const nameClaim = this.db.prepare(`
|
|
310
|
-
SELECT value FROM memory_claims
|
|
311
|
-
WHERE companion_id = ? AND subject = ? AND predicate = 'name' AND LOWER(status) = 'approved'
|
|
312
|
-
ORDER BY asserted_at DESC LIMIT 1
|
|
313
|
-
`).get(claim.companion_id, claim.subject);
|
|
314
|
-
const originName = nameClaim?.value || (claim.subject.startsWith('actor:') ? claim.subject.slice(6) : claim.subject);
|
|
315
|
-
this.db.prepare(`UPDATE self_identity SET origin = ? WHERE companion_id = ?`).run(originName, claim.companion_id);
|
|
316
|
-
}
|
|
317
|
-
const rel = this.db.prepare(`SELECT * FROM self_relationships WHERE companion_id = ? AND entity_id = ?`).get(claim.companion_id, claim.subject);
|
|
318
|
-
if (rel && (rel.role !== 'creator' || rel.stance !== 'familiar_loyal')) {
|
|
319
|
-
this.db.prepare(`UPDATE self_relationships SET role = 'creator', stance = 'familiar_loyal', trust_score = 1.0 WHERE companion_id = ? AND entity_id = ?`).run(claim.companion_id, claim.subject);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
catch {
|
|
324
|
-
// Best-effort auto-reconciliation
|
|
325
|
-
}
|
|
326
267
|
}
|
|
327
268
|
close() {
|
|
328
269
|
this.db.close();
|
|
@@ -499,7 +440,7 @@ class SiduriDatabase {
|
|
|
499
440
|
});
|
|
500
441
|
}
|
|
501
442
|
}
|
|
502
|
-
// Single-value directive canonical supersession
|
|
443
|
+
// Single-value directive canonical supersession and direct Self mutation (RFC VX-26-13)
|
|
503
444
|
const roleMatch = (row.directive || '').match(/^acknowledge role as\s+([^"”'.]+)/i);
|
|
504
445
|
if (roleMatch) {
|
|
505
446
|
const existingDirectives = this.getAllDirectives(companionId || 'default');
|
|
@@ -511,6 +452,20 @@ class SiduriDatabase {
|
|
|
511
452
|
this.supersedeDirective(d.id, companionId);
|
|
512
453
|
}
|
|
513
454
|
}
|
|
455
|
+
const newRole = roleMatch[1].trim();
|
|
456
|
+
if (newRole) {
|
|
457
|
+
const targetId = companionId || row.companion_id || 'default';
|
|
458
|
+
const currentIdentity = this.getIdentity(targetId) || {
|
|
459
|
+
companionId: targetId,
|
|
460
|
+
name: '',
|
|
461
|
+
version: '1.0.0',
|
|
462
|
+
updatedAt: new Date().toISOString(),
|
|
463
|
+
};
|
|
464
|
+
currentIdentity.role = newRole;
|
|
465
|
+
currentIdentity.archetype = newRole;
|
|
466
|
+
currentIdentity.updatedAt = new Date().toISOString();
|
|
467
|
+
this.setIdentity(currentIdentity);
|
|
468
|
+
}
|
|
514
469
|
}
|
|
515
470
|
// Companion name directive detection: "Address companion as X", "Your name is X", "Call yourself X", "Acknowledge name as X"
|
|
516
471
|
const compNameMatch = (row.directive || '').match(/^(?:address\s+companion\s+as|your\s+name\s+is|call\s+yourself|acknowledge\s+name\s+as|companion\s+name\s+is)\s+["“']?([^"”'.]+)["”']?/i);
|
|
@@ -529,6 +484,82 @@ class SiduriDatabase {
|
|
|
529
484
|
this.setIdentity(currentIdentity);
|
|
530
485
|
}
|
|
531
486
|
}
|
|
487
|
+
// Relationship directive direct domain routing:
|
|
488
|
+
// "Recognize <actor> stated relationship as <role>", "Recognize <actor> relationship as <role>", "Recognize <actor> as <role>"
|
|
489
|
+
const relMatch = (row.directive || '').match(/^(?:recognize\s+(\S+)\s+(?:stated\s+)?relationship\s+as|recognize\s+(\S+)\s+as)\s+([^"”'.]+)/i);
|
|
490
|
+
if (relMatch) {
|
|
491
|
+
const rawActor = (relMatch[1] || relMatch[2] || '').trim();
|
|
492
|
+
const roleOrStance = (relMatch[3] || '').trim();
|
|
493
|
+
if (rawActor && roleOrStance) {
|
|
494
|
+
const targetId = companionId || row.companion_id || 'default';
|
|
495
|
+
const isCreator = roleOrStance.toLowerCase().includes('creator');
|
|
496
|
+
const existingRel = this.getRelationship(targetId, rawActor);
|
|
497
|
+
const stance = isCreator ? 'familiar_loyal' : (existingRel?.stance || 'neutral');
|
|
498
|
+
const trustScore = isCreator ? 1.0 : (existingRel?.trustScore ?? 0.8);
|
|
499
|
+
const familiarity = isCreator ? 0.9 : (existingRel?.familiarity ?? 0.5);
|
|
500
|
+
const interactionConventions = isCreator
|
|
501
|
+
? Array.from(new Set([...(existingRel?.interactionConventions || []), 'Direct communication', 'Highest administrative trust']))
|
|
502
|
+
: (existingRel?.interactionConventions || []);
|
|
503
|
+
this.upsertRelationship({
|
|
504
|
+
companionId: targetId,
|
|
505
|
+
entityId: rawActor,
|
|
506
|
+
entityType: 'human',
|
|
507
|
+
name: existingRel?.name,
|
|
508
|
+
affiliation: existingRel?.affiliation,
|
|
509
|
+
role: roleOrStance,
|
|
510
|
+
stance,
|
|
511
|
+
trustScore,
|
|
512
|
+
familiarity,
|
|
513
|
+
interactionConventions,
|
|
514
|
+
});
|
|
515
|
+
if (isCreator) {
|
|
516
|
+
const currentIdentity = this.getIdentity(targetId) || {
|
|
517
|
+
companionId: targetId,
|
|
518
|
+
name: '',
|
|
519
|
+
version: '1.0.0',
|
|
520
|
+
updatedAt: new Date().toISOString(),
|
|
521
|
+
};
|
|
522
|
+
const creatorName = existingRel?.name || (rawActor.startsWith('actor:') ? rawActor.slice(6) : rawActor);
|
|
523
|
+
currentIdentity.origin = creatorName !== 'user' && creatorName !== 'primary' ? creatorName : roleOrStance;
|
|
524
|
+
currentIdentity.updatedAt = new Date().toISOString();
|
|
525
|
+
this.setIdentity(currentIdentity);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
// User name address directive direct domain routing: "Address <actor> as <name>"
|
|
530
|
+
const userAddrMatch = (row.directive || '').match(/^address\s+(actor:\S+|\S+)\s+as\s+([^"”'.]+)/i);
|
|
531
|
+
if (userAddrMatch) {
|
|
532
|
+
const actorId = userAddrMatch[1].trim();
|
|
533
|
+
const userName = userAddrMatch[2].trim();
|
|
534
|
+
if (actorId && userName) {
|
|
535
|
+
const targetId = companionId || row.companion_id || 'default';
|
|
536
|
+
const existingRel = this.getRelationship(targetId, actorId);
|
|
537
|
+
const isCreator = existingRel?.role === 'creator' || (existingRel?.stance === 'familiar_loyal' && existingRel?.trustScore === 1.0);
|
|
538
|
+
this.upsertRelationship({
|
|
539
|
+
companionId: targetId,
|
|
540
|
+
entityId: actorId,
|
|
541
|
+
entityType: 'human',
|
|
542
|
+
name: userName,
|
|
543
|
+
affiliation: existingRel?.affiliation,
|
|
544
|
+
role: existingRel?.role || (isCreator ? 'creator' : 'user'),
|
|
545
|
+
stance: existingRel?.stance || (isCreator ? 'familiar_loyal' : 'neutral'),
|
|
546
|
+
trustScore: existingRel?.trustScore ?? (isCreator ? 1.0 : 0.8),
|
|
547
|
+
familiarity: existingRel?.familiarity ?? (isCreator ? 0.9 : 0.5),
|
|
548
|
+
interactionConventions: existingRel?.interactionConventions || [],
|
|
549
|
+
});
|
|
550
|
+
if (isCreator) {
|
|
551
|
+
const currentIdentity = this.getIdentity(targetId) || {
|
|
552
|
+
companionId: targetId,
|
|
553
|
+
name: '',
|
|
554
|
+
version: '1.0.0',
|
|
555
|
+
updatedAt: new Date().toISOString(),
|
|
556
|
+
};
|
|
557
|
+
currentIdentity.origin = userName;
|
|
558
|
+
currentIdentity.updatedAt = new Date().toISOString();
|
|
559
|
+
this.setIdentity(currentIdentity);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
532
563
|
if (companionId) {
|
|
533
564
|
const stmt = this.db.prepare("UPDATE self_directives SET status = 'active', priority = MAX(priority, ?) WHERE id = ? AND companion_id = ? AND LOWER(status) IN ('pending', 'superseded')");
|
|
534
565
|
stmt.run(targetPriority, id, companionId);
|
|
@@ -914,27 +945,27 @@ class SiduriDatabase {
|
|
|
914
945
|
return res.changes > 0;
|
|
915
946
|
}
|
|
916
947
|
// ==========================================
|
|
917
|
-
//
|
|
948
|
+
// Archive Domain Methods (RFC VX-26-13: Audited interaction ledger and cold search)
|
|
918
949
|
// ==========================================
|
|
919
|
-
|
|
950
|
+
recordArchiveEvent(event) {
|
|
920
951
|
const stmt = this.db.prepare(`
|
|
921
|
-
INSERT INTO
|
|
952
|
+
INSERT INTO archive_events (id, companion_id, source_type, occurred_at, payload)
|
|
922
953
|
VALUES (?, ?, ?, coalesce(?, datetime('now')), ?)
|
|
923
954
|
`);
|
|
924
|
-
stmt.run(event.id, event.companionId, event.sourceType, event.occurredAt || null, JSON.stringify(event.payload));
|
|
955
|
+
stmt.run(event.id, event.companionId, event.sourceType, event.occurredAt || null, typeof event.payload === 'string' ? event.payload : JSON.stringify(event.payload));
|
|
925
956
|
}
|
|
926
|
-
|
|
927
|
-
const stmt = this.db.prepare('SELECT * FROM
|
|
957
|
+
getRecentArchiveEvents(companionId, limit = 50) {
|
|
958
|
+
const stmt = this.db.prepare('SELECT * FROM archive_events WHERE companion_id = ? ORDER BY occurred_at DESC LIMIT ?');
|
|
928
959
|
return stmt.all(companionId, limit).map((row) => ({
|
|
929
960
|
id: row.id,
|
|
930
961
|
companionId: row.companion_id,
|
|
931
962
|
sourceType: row.source_type,
|
|
932
963
|
occurredAt: row.occurred_at,
|
|
933
|
-
payload:
|
|
964
|
+
payload: safeJsonParse(row.payload, {})
|
|
934
965
|
}));
|
|
935
966
|
}
|
|
936
|
-
|
|
937
|
-
const stmt = this.db.prepare('SELECT * FROM
|
|
967
|
+
getArchiveEvent(id) {
|
|
968
|
+
const stmt = this.db.prepare('SELECT * FROM archive_events WHERE id = ?');
|
|
938
969
|
const row = stmt.get(id);
|
|
939
970
|
if (!row)
|
|
940
971
|
return undefined;
|
|
@@ -943,332 +974,13 @@ class SiduriDatabase {
|
|
|
943
974
|
companionId: row.companion_id,
|
|
944
975
|
sourceType: row.source_type,
|
|
945
976
|
occurredAt: row.occurred_at,
|
|
946
|
-
payload:
|
|
977
|
+
payload: safeJsonParse(row.payload, {})
|
|
947
978
|
};
|
|
948
979
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
companionId: row.companion_id,
|
|
953
|
-
subject: row.subject,
|
|
954
|
-
predicate: row.predicate,
|
|
955
|
-
value: row.value,
|
|
956
|
-
status: normalizeStatus(row.status, 'pending'),
|
|
957
|
-
confidence: row.confidence,
|
|
958
|
-
validFrom: row.valid_from || undefined,
|
|
959
|
-
validUntil: row.valid_until || undefined,
|
|
960
|
-
evidence: row.evidence ? (typeof row.evidence === 'string' ? JSON.parse(row.evidence) : row.evidence) : undefined,
|
|
961
|
-
assertedAt: row.asserted_at,
|
|
962
|
-
supersedes: row.supersedes || undefined,
|
|
963
|
-
sourceEventId: row.source_event_id || undefined,
|
|
964
|
-
};
|
|
965
|
-
}
|
|
966
|
-
proposeClaim(claim) {
|
|
967
|
-
const id = claim.id || crypto.randomUUID();
|
|
968
|
-
const status = normalizeStatus(claim.status, 'pending');
|
|
969
|
-
const confidence = claim.confidence ?? 1.0;
|
|
970
|
-
const assertedAt = claim.assertedAt || new Date().toISOString();
|
|
971
|
-
const stmt = this.db.prepare(`
|
|
972
|
-
INSERT INTO memory_claims (id, companion_id, subject, predicate, value, status, confidence, valid_from, valid_until, evidence, asserted_at, supersedes, source_event_id)
|
|
973
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, coalesce(?, datetime('now')), ?, ?)
|
|
974
|
-
`);
|
|
975
|
-
stmt.run(id, claim.companionId || 'default', claim.subject, claim.predicate, claim.value, status, confidence, claim.validFrom || null, claim.validUntil || null, claim.evidence ? JSON.stringify(claim.evidence) : null, assertedAt || null, claim.supersedes || null, claim.sourceEventId || null);
|
|
976
|
-
return {
|
|
977
|
-
...claim,
|
|
978
|
-
id,
|
|
979
|
-
status: status,
|
|
980
|
-
confidence,
|
|
981
|
-
assertedAt,
|
|
982
|
-
supersedes: claim.supersedes,
|
|
983
|
-
sourceEventId: claim.sourceEventId,
|
|
984
|
-
};
|
|
985
|
-
}
|
|
986
|
-
approveClaim(id, companionId) {
|
|
987
|
-
const findClaim = companionId
|
|
988
|
-
? this.db.prepare("SELECT id, subject, predicate, status, supersedes FROM memory_claims WHERE id = ? AND companion_id = ?")
|
|
989
|
-
: this.db.prepare("SELECT id, subject, predicate, status, supersedes FROM memory_claims WHERE id = ?");
|
|
990
|
-
const row = (companionId ? findClaim.get(id, companionId) : findClaim.get(id));
|
|
991
|
-
if (!row) {
|
|
992
|
-
return;
|
|
993
|
-
}
|
|
994
|
-
if (normalizeStatus(row.status) !== 'pending') {
|
|
995
|
-
if (normalizeStatus(row.status) === 'approved') {
|
|
996
|
-
return;
|
|
997
|
-
}
|
|
998
|
-
throw new Error(`Cannot approve claim '${id}': invalid transition from status '${row.status}' to 'approved' (only pending claims can be approved)`);
|
|
999
|
-
}
|
|
1000
|
-
// Explicit supersession transition: if claim explicitly targets an earlier claim
|
|
1001
|
-
if (row.supersedes) {
|
|
1002
|
-
const supersededId = row.supersedes;
|
|
1003
|
-
if (companionId) {
|
|
1004
|
-
const supersedeStmt = this.db.prepare("UPDATE memory_claims SET status = 'superseded' WHERE id = ? AND companion_id = ?");
|
|
1005
|
-
supersedeStmt.run(supersededId, companionId);
|
|
1006
|
-
}
|
|
1007
|
-
else {
|
|
1008
|
-
const supersedeStmt = this.db.prepare("UPDATE memory_claims SET status = 'superseded' WHERE id = ?");
|
|
1009
|
-
supersedeStmt.run(supersededId);
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
// Automatic supersession for single-value predicates:
|
|
1013
|
-
// When a single-value predicate (e.g. name, role, creator, preferred_address) is approved,
|
|
1014
|
-
// transition previous active/approved claims on the same subject & predicate to 'superseded'
|
|
1015
|
-
const SINGLE_VALUE_PREDICATES = new Set([
|
|
1016
|
-
'name',
|
|
1017
|
-
'preferred_name',
|
|
1018
|
-
'preferred_address',
|
|
1019
|
-
'preferred_form_of_address',
|
|
1020
|
-
'form_of_address',
|
|
1021
|
-
'title',
|
|
1022
|
-
'honorific',
|
|
1023
|
-
'role',
|
|
1024
|
-
'archetype',
|
|
1025
|
-
'origin',
|
|
1026
|
-
'created_by',
|
|
1027
|
-
'stated_relationship',
|
|
1028
|
-
'relationship',
|
|
1029
|
-
'relationship_to_siduri',
|
|
1030
|
-
'ethos',
|
|
1031
|
-
'server',
|
|
1032
|
-
'uid',
|
|
1033
|
-
'preferred_language',
|
|
1034
|
-
]);
|
|
1035
|
-
const lowerPred = (row.predicate || '').toLowerCase();
|
|
1036
|
-
if (SINGLE_VALUE_PREDICATES.has(lowerPred)) {
|
|
1037
|
-
const effCompanionId = companionId || 'default';
|
|
1038
|
-
const autoSupersedeStmt = this.db.prepare(`
|
|
1039
|
-
UPDATE memory_claims
|
|
1040
|
-
SET status = 'superseded'
|
|
1041
|
-
WHERE companion_id = ?
|
|
1042
|
-
AND LOWER(subject) = LOWER(?)
|
|
1043
|
-
AND LOWER(predicate) = LOWER(?)
|
|
1044
|
-
AND id != ?
|
|
1045
|
-
AND LOWER(status) IN ('approved', 'pending')
|
|
1046
|
-
`);
|
|
1047
|
-
autoSupersedeStmt.run(effCompanionId, row.subject, row.predicate, id);
|
|
1048
|
-
}
|
|
1049
|
-
if (companionId) {
|
|
1050
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'approved' WHERE id = ? AND companion_id = ? AND LOWER(status) = 'pending'");
|
|
1051
|
-
stmt.run(id, companionId);
|
|
1052
|
-
}
|
|
1053
|
-
else {
|
|
1054
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'approved' WHERE id = ? AND LOWER(status) = 'pending'");
|
|
1055
|
-
stmt.run(id);
|
|
1056
|
-
}
|
|
1057
|
-
// Canonically promote approved claim to Self domain state (identity, role, relationships)
|
|
1058
|
-
const approvedClaim = this.getClaim(id);
|
|
1059
|
-
if (approvedClaim) {
|
|
1060
|
-
this.promoteClaimToSelf(approvedClaim);
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
/**
|
|
1064
|
-
* Canonically promotes a Claim into Self domain tables.
|
|
1065
|
-
*/
|
|
1066
|
-
promoteClaimToSelf(claim) {
|
|
1067
|
-
const companionId = claim.companionId || 'default';
|
|
1068
|
-
const subject = (claim.subject || '').toLowerCase();
|
|
1069
|
-
const predicate = (claim.predicate || '').toLowerCase();
|
|
1070
|
-
const value = claim.value || '';
|
|
1071
|
-
if (!value)
|
|
1072
|
-
return;
|
|
1073
|
-
// 1. Identity mutations: companion identity/role/origin/name/ethos
|
|
1074
|
-
const isCompanionTarget = subject.startsWith('companion') ||
|
|
1075
|
-
subject === 'siduri' ||
|
|
1076
|
-
subject === 'self' ||
|
|
1077
|
-
subject === 'assistant' ||
|
|
1078
|
-
subject === 'persona' ||
|
|
1079
|
-
subject === 'ai' ||
|
|
1080
|
-
subject === 'me' ||
|
|
1081
|
-
((predicate === 'name' || predicate === 'role' || predicate === 'archetype' || predicate === 'ethos' || predicate === 'origin') &&
|
|
1082
|
-
!subject.startsWith('actor:') &&
|
|
1083
|
-
subject !== 'user' &&
|
|
1084
|
-
subject !== 'primary_user' &&
|
|
1085
|
-
subject !== 'owner' &&
|
|
1086
|
-
subject !== 'creator');
|
|
1087
|
-
if (isCompanionTarget) {
|
|
1088
|
-
const existing = this.getIdentity(companionId) || {
|
|
1089
|
-
companionId,
|
|
1090
|
-
name: '',
|
|
1091
|
-
version: '1.0.0',
|
|
1092
|
-
updatedAt: new Date().toISOString(),
|
|
1093
|
-
};
|
|
1094
|
-
if (predicate === 'role' || predicate === 'archetype') {
|
|
1095
|
-
existing.archetype = value;
|
|
1096
|
-
existing.role = value;
|
|
1097
|
-
this.setIdentity(existing);
|
|
1098
|
-
this.commitDirective({
|
|
1099
|
-
id: `dir-role-${claim.id || Date.now()}`,
|
|
1100
|
-
companionId,
|
|
1101
|
-
priority: 70,
|
|
1102
|
-
directive: `Acknowledge role as ${value}`,
|
|
1103
|
-
status: 'active',
|
|
1104
|
-
category: 'relational',
|
|
1105
|
-
createdAt: new Date().toISOString(),
|
|
1106
|
-
});
|
|
1107
|
-
}
|
|
1108
|
-
else if (predicate === 'origin' || predicate === 'created_by') {
|
|
1109
|
-
existing.origin = value;
|
|
1110
|
-
this.setIdentity(existing);
|
|
1111
|
-
}
|
|
1112
|
-
else if (predicate === 'name') {
|
|
1113
|
-
existing.name = value;
|
|
1114
|
-
this.setIdentity(existing);
|
|
1115
|
-
}
|
|
1116
|
-
else if (predicate === 'ethos') {
|
|
1117
|
-
existing.ethos = value;
|
|
1118
|
-
this.setIdentity(existing);
|
|
1119
|
-
}
|
|
1120
|
-
return;
|
|
1121
|
-
}
|
|
1122
|
-
// 2. Relationship mutations: creator or user stated relationship, name, preferred address, or affiliation
|
|
1123
|
-
const isPreferredAddress = [
|
|
1124
|
-
'preferred_address',
|
|
1125
|
-
'preferred_form_of_address',
|
|
1126
|
-
'preferred_name',
|
|
1127
|
-
'form_of_address',
|
|
1128
|
-
'title',
|
|
1129
|
-
'honorific',
|
|
1130
|
-
'call_me',
|
|
1131
|
-
'addressed_as',
|
|
1132
|
-
].includes(predicate);
|
|
1133
|
-
const isName = (predicate === 'name' && (subject.startsWith('actor:') || subject === 'user' || subject === 'primary_user')) || isPreferredAddress;
|
|
1134
|
-
if (claim.claimType === 'relationship' ||
|
|
1135
|
-
predicate === 'stated_relationship' ||
|
|
1136
|
-
predicate === 'relationship' ||
|
|
1137
|
-
predicate === 'relationship_to_siduri' ||
|
|
1138
|
-
predicate === 'affiliation' ||
|
|
1139
|
-
isName) {
|
|
1140
|
-
const rawSubject = (claim.subject || 'actor:user').replace(/^actor:actor:/, 'actor:');
|
|
1141
|
-
const isCreator = value.toLowerCase().includes('creator');
|
|
1142
|
-
const isAffil = predicate === 'affiliation';
|
|
1143
|
-
const existingRel = this.getRelationship(companionId, rawSubject);
|
|
1144
|
-
const isPriorCreator = existingRel?.role === 'creator' || (existingRel?.stance === 'familiar_loyal' && existingRel.trustScore === 1.0);
|
|
1145
|
-
const role = isCreator
|
|
1146
|
-
? 'creator'
|
|
1147
|
-
: (isPriorCreator ? 'creator' : (existingRel?.role && existingRel.role !== 'user' ? existingRel.role : (isName || isAffil ? existingRel?.role || 'user' : value)));
|
|
1148
|
-
const name = isName ? (isPreferredAddress && existingRel?.name ? existingRel.name : value) : existingRel?.name;
|
|
1149
|
-
const affiliation = isAffil ? value : existingRel?.affiliation;
|
|
1150
|
-
const stance = isCreator || isPriorCreator ? 'familiar_loyal' : (existingRel?.stance || 'neutral');
|
|
1151
|
-
const trustScore = isCreator || isPriorCreator ? 1.0 : (existingRel?.trustScore ?? 0.8);
|
|
1152
|
-
const familiarity = isCreator || isPriorCreator ? 0.9 : (existingRel?.familiarity ?? 0.5);
|
|
1153
|
-
const conventions = isCreator || isPriorCreator
|
|
1154
|
-
? Array.from(new Set([...(existingRel?.interactionConventions || []), 'Direct communication', 'Highest administrative trust']))
|
|
1155
|
-
: [...(existingRel?.interactionConventions || [])];
|
|
1156
|
-
if (isPreferredAddress) {
|
|
1157
|
-
const filteredConvs = conventions.filter((c) => !c.toLowerCase().startsWith('address as'));
|
|
1158
|
-
filteredConvs.push(`Address as ${value}`);
|
|
1159
|
-
conventions.length = 0;
|
|
1160
|
-
conventions.push(...filteredConvs);
|
|
1161
|
-
}
|
|
1162
|
-
this.upsertRelationship({
|
|
1163
|
-
companionId,
|
|
1164
|
-
entityId: rawSubject,
|
|
1165
|
-
entityType: 'human',
|
|
1166
|
-
name,
|
|
1167
|
-
affiliation,
|
|
1168
|
-
role,
|
|
1169
|
-
stance,
|
|
1170
|
-
trustScore,
|
|
1171
|
-
familiarity,
|
|
1172
|
-
interactionConventions: conventions,
|
|
1173
|
-
});
|
|
1174
|
-
// Dual promotion: If this actor is established as creator, also populate companion's origin in self_identity
|
|
1175
|
-
if (isCreator || (isName && isPriorCreator)) {
|
|
1176
|
-
const existingIdentity = this.getIdentity(companionId) || {
|
|
1177
|
-
companionId,
|
|
1178
|
-
name: 'Siduri',
|
|
1179
|
-
version: '1.0.0',
|
|
1180
|
-
updatedAt: new Date().toISOString(),
|
|
1181
|
-
};
|
|
1182
|
-
const creatorName = name || existingRel?.name || (rawSubject.startsWith('actor:') && rawSubject !== 'actor:user' && rawSubject !== 'actor:primary' ? rawSubject.slice(6) : value);
|
|
1183
|
-
existingIdentity.origin = creatorName !== 'user' && creatorName !== 'primary' ? creatorName : value;
|
|
1184
|
-
existingIdentity.updatedAt = new Date().toISOString();
|
|
1185
|
-
this.setIdentity(existingIdentity);
|
|
1186
|
-
}
|
|
1187
|
-
if (isName) {
|
|
1188
|
-
const priority = isPreferredAddress ? 85 : 75;
|
|
1189
|
-
const directiveId = isPreferredAddress ? `dir-prefaddr-${claim.id || Date.now()}` : `dir-name-${claim.id || Date.now()}`;
|
|
1190
|
-
// If preferred address, supersede/revoke older address directives for this subject
|
|
1191
|
-
if (isPreferredAddress) {
|
|
1192
|
-
const existingDirectives = this.getAllDirectives(companionId);
|
|
1193
|
-
for (const d of existingDirectives) {
|
|
1194
|
-
const lowerDir = (d.directive || '').toLowerCase();
|
|
1195
|
-
const lowerSubj = rawSubject.toLowerCase();
|
|
1196
|
-
if ((d.status === 'active' || d.status === 'pending') &&
|
|
1197
|
-
d.id !== directiveId &&
|
|
1198
|
-
(lowerDir.startsWith(`address ${lowerSubj} as`) ||
|
|
1199
|
-
lowerDir.startsWith('address the user as') ||
|
|
1200
|
-
lowerDir.startsWith('address actor:user as') ||
|
|
1201
|
-
lowerDir.startsWith('address user as'))) {
|
|
1202
|
-
this.supersedeDirective(d.id, companionId);
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
this.commitDirective({
|
|
1207
|
-
id: directiveId,
|
|
1208
|
-
companionId,
|
|
1209
|
-
priority,
|
|
1210
|
-
directive: `Address ${rawSubject} as ${value}`,
|
|
1211
|
-
status: 'active',
|
|
1212
|
-
category: 'relational',
|
|
1213
|
-
createdAt: new Date().toISOString(),
|
|
1214
|
-
});
|
|
1215
|
-
}
|
|
1216
|
-
return;
|
|
1217
|
-
}
|
|
1218
|
-
// 3. Behavioral rule claim
|
|
1219
|
-
if (predicate === 'behavioral_rule' || predicate === 'rule') {
|
|
1220
|
-
this.commitDirective({
|
|
1221
|
-
id: `dir-rule-${claim.id || Date.now()}`,
|
|
1222
|
-
companionId,
|
|
1223
|
-
priority: 60,
|
|
1224
|
-
directive: value,
|
|
1225
|
-
status: 'active',
|
|
1226
|
-
category: 'behavioral',
|
|
1227
|
-
createdAt: new Date().toISOString(),
|
|
1228
|
-
});
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
rejectClaim(id, companionId) {
|
|
1232
|
-
if (companionId) {
|
|
1233
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'rejected' WHERE id = ? AND companion_id = ?");
|
|
1234
|
-
stmt.run(id, companionId);
|
|
1235
|
-
}
|
|
1236
|
-
else {
|
|
1237
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'rejected' WHERE id = ?");
|
|
1238
|
-
stmt.run(id);
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
revokeClaim(id, companionId) {
|
|
1242
|
-
if (companionId) {
|
|
1243
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'revoked' WHERE id = ? AND companion_id = ?");
|
|
1244
|
-
stmt.run(id, companionId);
|
|
980
|
+
searchArchiveEvents(companionId, query, limit = 50) {
|
|
981
|
+
if (!query || !query.trim()) {
|
|
982
|
+
return this.getRecentArchiveEvents(companionId, limit);
|
|
1245
983
|
}
|
|
1246
|
-
else {
|
|
1247
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'revoked' WHERE id = ?");
|
|
1248
|
-
stmt.run(id);
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
expireClaim(id, companionId) {
|
|
1252
|
-
if (companionId) {
|
|
1253
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'expired' WHERE id = ? AND companion_id = ?");
|
|
1254
|
-
stmt.run(id, companionId);
|
|
1255
|
-
}
|
|
1256
|
-
else {
|
|
1257
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'expired' WHERE id = ?");
|
|
1258
|
-
stmt.run(id);
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
markClaimSessionOnly(id, companionId) {
|
|
1262
|
-
if (companionId) {
|
|
1263
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'session-only' WHERE id = ? AND companion_id = ?");
|
|
1264
|
-
stmt.run(id, companionId);
|
|
1265
|
-
}
|
|
1266
|
-
else {
|
|
1267
|
-
const stmt = this.db.prepare("UPDATE memory_claims SET status = 'session-only' WHERE id = ?");
|
|
1268
|
-
stmt.run(id);
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
searchClaims(companionId, query, limit = 20) {
|
|
1272
984
|
const cleanTokens = query
|
|
1273
985
|
.replace(/[^\p{L}\p{N}\s_]/gu, ' ')
|
|
1274
986
|
.trim()
|
|
@@ -1276,44 +988,46 @@ class SiduriDatabase {
|
|
|
1276
988
|
.filter((t) => t.length > 0)
|
|
1277
989
|
.map((t) => `"${t.replace(/"/g, '""')}"`);
|
|
1278
990
|
if (cleanTokens.length === 0)
|
|
1279
|
-
return
|
|
991
|
+
return this.getRecentArchiveEvents(companionId, limit);
|
|
1280
992
|
const ftsQuery = cleanTokens.join(' OR ');
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
993
|
+
try {
|
|
994
|
+
const stmt = this.db.prepare(`
|
|
995
|
+
SELECT e.* FROM archive_events e
|
|
996
|
+
JOIN archive_search s ON e.rowid = s.rowid
|
|
997
|
+
WHERE e.companion_id = ? AND archive_search MATCH ?
|
|
998
|
+
ORDER BY rank
|
|
999
|
+
LIMIT ?
|
|
1000
|
+
`);
|
|
1001
|
+
return stmt.all(companionId, ftsQuery, limit).map((row) => ({
|
|
1002
|
+
id: row.id,
|
|
1003
|
+
companionId: row.companion_id,
|
|
1004
|
+
sourceType: row.source_type,
|
|
1005
|
+
occurredAt: row.occurred_at,
|
|
1006
|
+
payload: safeJsonParse(row.payload, {})
|
|
1007
|
+
}));
|
|
1008
|
+
}
|
|
1009
|
+
catch {
|
|
1010
|
+
const stmt = this.db.prepare(`
|
|
1011
|
+
SELECT * FROM archive_events
|
|
1012
|
+
WHERE companion_id = ? AND (source_type LIKE ? OR payload LIKE ?)
|
|
1013
|
+
ORDER BY occurred_at DESC
|
|
1014
|
+
LIMIT ?
|
|
1015
|
+
`);
|
|
1016
|
+
const pattern = `%${query.trim()}%`;
|
|
1017
|
+
return stmt.all(companionId, pattern, pattern, limit).map((row) => ({
|
|
1018
|
+
id: row.id,
|
|
1019
|
+
companionId: row.companion_id,
|
|
1020
|
+
sourceType: row.source_type,
|
|
1021
|
+
occurredAt: row.occurred_at,
|
|
1022
|
+
payload: safeJsonParse(row.payload, {})
|
|
1023
|
+
}));
|
|
1024
|
+
}
|
|
1311
1025
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1026
|
+
resetArchive(companionId) {
|
|
1027
|
+
try {
|
|
1028
|
+
this.db.prepare("DELETE FROM archive_events WHERE companion_id = ?").run(companionId);
|
|
1029
|
+
}
|
|
1030
|
+
catch { }
|
|
1317
1031
|
}
|
|
1318
1032
|
// --- System Logs ---
|
|
1319
1033
|
insertLog(entry) {
|