@siduri-x/core 2.0.1 → 2.0.3

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.
Files changed (41) hide show
  1. package/dist/action-policy.d.ts +8 -1
  2. package/dist/action-policy.js +84 -24
  3. package/dist/action-policy.test.js +67 -1
  4. package/dist/action.d.ts +1 -1
  5. package/dist/capability.d.ts +6 -2
  6. package/dist/capability.js +5 -1
  7. package/dist/capability.test.js +1 -0
  8. package/dist/chat-contract.d.ts +3 -1
  9. package/dist/chat-contract.js +5 -2
  10. package/dist/container.d.ts +74 -0
  11. package/dist/container.js +81 -0
  12. package/dist/context.d.ts +3 -1
  13. package/dist/context.js +13 -0
  14. package/dist/index.d.ts +11 -3
  15. package/dist/index.js +2 -0
  16. package/dist/input-normalizer.js +3 -1
  17. package/dist/intent-classifier.d.ts +4 -2
  18. package/dist/intent-classifier.js +32 -1
  19. package/dist/intent-classifier.test.js +52 -0
  20. package/dist/memory-settler.d.ts +2 -1
  21. package/dist/memory-settler.js +9 -1
  22. package/dist/perception-cycle.test.js +134 -0
  23. package/dist/perception-pipeline.d.ts +76 -0
  24. package/dist/perception-pipeline.js +258 -0
  25. package/dist/perception-pipeline.test.d.ts +1 -0
  26. package/dist/perception-pipeline.test.js +65 -0
  27. package/dist/prompt-compiler.d.ts +2 -1
  28. package/dist/prompt-compiler.js +7 -1
  29. package/dist/proposals.d.ts +4 -1
  30. package/dist/response-envelope.d.ts +2 -1
  31. package/dist/response-envelope.js +2 -1
  32. package/dist/runtime-facades.test.js +27 -27
  33. package/dist/runtime.d.ts +36 -113
  34. package/dist/runtime.js +58 -403
  35. package/dist/siduri-db.d.ts +25 -9
  36. package/dist/siduri-db.js +114 -15
  37. package/dist/siduri-db.test.js +4 -3
  38. package/dist/sqlite-action-store.d.ts +1 -1
  39. package/dist/sqlite-action-store.js +37 -6
  40. package/dist/sqlite-action-store.test.js +9 -0
  41. package/package.json +1 -1
package/dist/siduri-db.js CHANGED
@@ -20,6 +20,8 @@ class SiduriDatabase {
20
20
  companion_id TEXT PRIMARY KEY,
21
21
  name TEXT NOT NULL,
22
22
  archetype TEXT,
23
+ origin TEXT,
24
+ ethos TEXT,
23
25
  version TEXT NOT NULL,
24
26
  updated_at TEXT DEFAULT (datetime('now'))
25
27
  );
@@ -41,6 +43,7 @@ class SiduriDatabase {
41
43
  directive TEXT NOT NULL,
42
44
  status TEXT DEFAULT 'ACTIVE',
43
45
  category TEXT DEFAULT 'behavioral',
46
+ scope_actor TEXT,
44
47
  supersedes_id TEXT,
45
48
  created_at TEXT DEFAULT (datetime('now'))
46
49
  );
@@ -48,13 +51,24 @@ class SiduriDatabase {
48
51
  CREATE TABLE IF NOT EXISTS self_relationships (
49
52
  companion_id TEXT NOT NULL,
50
53
  entity_id TEXT NOT NULL,
51
- entity_type TEXT NOT NULL,
54
+ entity_type TEXT NOT NULL DEFAULT 'human',
55
+ role TEXT DEFAULT 'user',
56
+ stance TEXT DEFAULT 'neutral',
52
57
  trust_score REAL DEFAULT 0.5,
53
58
  familiarity REAL DEFAULT 0.5,
54
59
  interaction_conventions TEXT,
60
+ updated_at TEXT DEFAULT (datetime('now')),
55
61
  PRIMARY KEY(companion_id, entity_id)
56
62
  );
57
63
 
64
+ CREATE TABLE IF NOT EXISTS self_exemplars (
65
+ id TEXT PRIMARY KEY,
66
+ companion_id TEXT NOT NULL,
67
+ user_prompt TEXT NOT NULL,
68
+ companion_response TEXT NOT NULL,
69
+ created_at TEXT DEFAULT (datetime('now'))
70
+ );
71
+
58
72
  -- Knowledge Tables
59
73
  CREATE TABLE IF NOT EXISTS life_inventory (
60
74
  id TEXT PRIMARY KEY,
@@ -159,6 +173,42 @@ class SiduriDatabase {
159
173
  catch {
160
174
  // Column already exists
161
175
  }
176
+ try {
177
+ this.db.exec("ALTER TABLE self_identity ADD COLUMN origin TEXT");
178
+ }
179
+ catch {
180
+ // Column already exists
181
+ }
182
+ try {
183
+ this.db.exec("ALTER TABLE self_identity ADD COLUMN ethos TEXT");
184
+ }
185
+ catch {
186
+ // Column already exists
187
+ }
188
+ try {
189
+ this.db.exec("ALTER TABLE self_directives ADD COLUMN scope_actor TEXT");
190
+ }
191
+ catch {
192
+ // Column already exists
193
+ }
194
+ try {
195
+ this.db.exec("ALTER TABLE self_relationships ADD COLUMN role TEXT DEFAULT 'user'");
196
+ }
197
+ catch {
198
+ // Column already exists
199
+ }
200
+ try {
201
+ this.db.exec("ALTER TABLE self_relationships ADD COLUMN stance TEXT DEFAULT 'neutral'");
202
+ }
203
+ catch {
204
+ // Column already exists
205
+ }
206
+ try {
207
+ this.db.exec("ALTER TABLE self_relationships ADD COLUMN updated_at TEXT");
208
+ }
209
+ catch {
210
+ // Column already exists
211
+ }
162
212
  }
163
213
  close() {
164
214
  this.db.close();
@@ -175,21 +225,25 @@ class SiduriDatabase {
175
225
  companionId: row.companion_id,
176
226
  name: row.name,
177
227
  archetype: row.archetype || undefined,
228
+ origin: row.origin || undefined,
229
+ ethos: row.ethos || undefined,
178
230
  version: row.version,
179
231
  updatedAt: row.updated_at
180
232
  };
181
233
  }
182
234
  setIdentity(identity) {
183
235
  const stmt = this.db.prepare(`
184
- INSERT INTO self_identity (companion_id, name, archetype, version, updated_at)
185
- VALUES (?, ?, ?, ?, datetime('now'))
236
+ INSERT INTO self_identity (companion_id, name, archetype, origin, ethos, version, updated_at)
237
+ VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
186
238
  ON CONFLICT(companion_id) DO UPDATE SET
187
239
  name = excluded.name,
188
240
  archetype = excluded.archetype,
241
+ origin = excluded.origin,
242
+ ethos = excluded.ethos,
189
243
  version = excluded.version,
190
244
  updated_at = datetime('now')
191
245
  `);
192
- stmt.run(identity.companionId, identity.name, identity.archetype || null, identity.version);
246
+ stmt.run(identity.companionId, identity.name, identity.archetype || null, identity.origin || null, identity.ethos || null, identity.version);
193
247
  }
194
248
  getPersonality(companionId) {
195
249
  const stmt = this.db.prepare('SELECT * FROM self_personality WHERE companion_id = ?');
@@ -231,16 +285,17 @@ class SiduriDatabase {
231
285
  directive: row.directive,
232
286
  status: row.status,
233
287
  category: row.category,
288
+ scopeActor: row.scope_actor || undefined,
234
289
  supersedesId: row.supersedes_id || undefined,
235
290
  createdAt: row.created_at
236
291
  }));
237
292
  }
238
293
  commitDirective(directive) {
239
294
  const stmt = this.db.prepare(`
240
- INSERT INTO self_directives (id, companion_id, priority, directive, status, category, supersedes_id, created_at)
241
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
295
+ INSERT INTO self_directives (id, companion_id, priority, directive, status, category, scope_actor, supersedes_id, created_at)
296
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
242
297
  `);
243
- stmt.run(directive.id, directive.companionId, directive.priority, directive.directive, directive.status, directive.category, directive.supersedesId || null, directive.createdAt || new Date().toISOString());
298
+ stmt.run(directive.id, directive.companionId, directive.priority !== undefined ? directive.priority : 50, directive.directive, directive.status, directive.category || 'behavioral', directive.scopeActor || null, directive.supersedesId || null, directive.createdAt || new Date().toISOString());
244
299
  }
245
300
  getDirective(id, companionId) {
246
301
  const stmt = companionId
@@ -256,6 +311,7 @@ class SiduriDatabase {
256
311
  directive: row.directive,
257
312
  status: row.status,
258
313
  category: row.category,
314
+ scopeActor: row.scope_actor || undefined,
259
315
  supersedesId: row.supersedes_id || undefined,
260
316
  createdAt: row.created_at,
261
317
  };
@@ -351,23 +407,66 @@ class SiduriDatabase {
351
407
  return {
352
408
  companionId: row.companion_id,
353
409
  entityId: row.entity_id,
354
- entityType: row.entity_type,
355
- trustScore: row.trust_score,
356
- familiarity: row.familiarity,
357
- interactionConventions: row.interaction_conventions ? JSON.parse(row.interaction_conventions) : []
410
+ entityType: row.entity_type || 'human',
411
+ role: row.role || 'user',
412
+ stance: row.stance || 'neutral',
413
+ trustScore: row.trust_score !== undefined && row.trust_score !== null ? row.trust_score : 0.5,
414
+ familiarity: row.familiarity !== undefined && row.familiarity !== null ? row.familiarity : 0.5,
415
+ interactionConventions: row.interaction_conventions ? JSON.parse(row.interaction_conventions) : [],
416
+ updatedAt: row.updated_at || undefined,
358
417
  };
359
418
  }
419
+ getRelationships(companionId) {
420
+ const stmt = this.db.prepare('SELECT * FROM self_relationships WHERE companion_id = ? ORDER BY entity_id ASC');
421
+ return stmt.all(companionId).map((row) => ({
422
+ companionId: row.companion_id,
423
+ entityId: row.entity_id,
424
+ entityType: row.entity_type || 'human',
425
+ role: row.role || 'user',
426
+ stance: row.stance || 'neutral',
427
+ trustScore: row.trust_score !== undefined && row.trust_score !== null ? row.trust_score : 0.5,
428
+ familiarity: row.familiarity !== undefined && row.familiarity !== null ? row.familiarity : 0.5,
429
+ interactionConventions: row.interaction_conventions ? JSON.parse(row.interaction_conventions) : [],
430
+ updatedAt: row.updated_at || undefined,
431
+ }));
432
+ }
360
433
  upsertRelationship(rel) {
361
434
  const stmt = this.db.prepare(`
362
- INSERT INTO self_relationships (companion_id, entity_id, entity_type, trust_score, familiarity, interaction_conventions)
363
- VALUES (?, ?, ?, ?, ?, ?)
435
+ INSERT INTO self_relationships (companion_id, entity_id, entity_type, role, stance, trust_score, familiarity, interaction_conventions, updated_at)
436
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
364
437
  ON CONFLICT(companion_id, entity_id) DO UPDATE SET
365
438
  entity_type = excluded.entity_type,
439
+ role = excluded.role,
440
+ stance = excluded.stance,
366
441
  trust_score = excluded.trust_score,
367
442
  familiarity = excluded.familiarity,
368
- interaction_conventions = excluded.interaction_conventions
443
+ interaction_conventions = excluded.interaction_conventions,
444
+ updated_at = datetime('now')
369
445
  `);
370
- stmt.run(rel.companionId, rel.entityId, rel.entityType, rel.trustScore, rel.familiarity, JSON.stringify(rel.interactionConventions || []));
446
+ 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 || []));
447
+ }
448
+ getExemplars(companionId) {
449
+ const stmt = this.db.prepare('SELECT * FROM self_exemplars WHERE companion_id = ? ORDER BY created_at ASC');
450
+ return stmt.all(companionId).map((row) => ({
451
+ id: row.id,
452
+ companionId: row.companion_id,
453
+ user: row.user_prompt,
454
+ assistant: row.companion_response,
455
+ createdAt: row.created_at,
456
+ }));
457
+ }
458
+ setExemplars(companionId, exemplars) {
459
+ const delStmt = this.db.prepare('DELETE FROM self_exemplars WHERE companion_id = ?');
460
+ delStmt.run(companionId);
461
+ const insertStmt = this.db.prepare(`
462
+ INSERT INTO self_exemplars (id, companion_id, user_prompt, companion_response, created_at)
463
+ VALUES (?, ?, ?, ?, datetime('now'))
464
+ `);
465
+ for (let i = 0; i < exemplars.length; i++) {
466
+ const ex = exemplars[i];
467
+ const id = ex.id || `ex-${i + 1}-${Date.now()}`;
468
+ insertStmt.run(id, companionId, ex.user, ex.assistant);
469
+ }
371
470
  }
372
471
  // ==========================================
373
472
  // Knowledge / Life DB Methods
@@ -72,14 +72,15 @@ describe('SiduriDatabase', () => {
72
72
  memDb.close();
73
73
  }).not.toThrow();
74
74
  });
75
- it('initializes schema and WAL mode within the startup latency budget (<100ms in CI, typical <20ms locally)', () => {
75
+ it('initializes schema and WAL mode within the startup latency budget (<1000ms in CI, typical <20ms locally)', () => {
76
76
  const start = performance.now();
77
77
  const benchDb = new siduri_db_1.SiduriDatabase({ dbPath });
78
78
  const duration = performance.now() - start;
79
79
  benchDb.close();
80
80
  // In bare-metal local development, SQLite cold init is ~2-5ms.
81
- // Under virtualized CI runners with concurrent Turbo tasks, allow a safe 100ms budget.
82
- expect(duration).toBeLessThan(100);
81
+ // Under virtualized CI runners with concurrent Turbo tasks and shared I/O, allow up to 1000ms.
82
+ const budgetMs = process.env.CI ? 1000 : 250;
83
+ expect(duration).toBeLessThan(budgetMs);
83
84
  });
84
85
  it('stores and retrieves companion identity', () => {
85
86
  db = new siduri_db_1.SiduriDatabase({ dbPath });
@@ -12,7 +12,7 @@ export declare class SqliteActionStore implements ActionStore {
12
12
  reserveExecution(record: PersistentExecutionRecord): Promise<boolean>;
13
13
  updateExecution(record: PersistentExecutionRecord): Promise<void>;
14
14
  getExecution(executionId: string): Promise<PersistentExecutionRecord | undefined>;
15
- saveApproval(executionId: string, approverActorId: string, reason?: string, approverRole?: string): Promise<void>;
15
+ saveApproval(executionId: string, approverActorId: string, reason?: string, approverRole?: string, toolName?: string, parametersHash?: string, companionId?: string, actorId?: string): Promise<void>;
16
16
  isActionApproved(executionId: string): Promise<boolean>;
17
17
  getApproval(executionId: string): Promise<ActionApprovalRecord | undefined>;
18
18
  appendAudit(event: ActionAuditEvent): Promise<void>;
@@ -37,7 +37,11 @@ class SqliteActionStore {
37
37
  approver_actor_id TEXT NOT NULL,
38
38
  reason TEXT,
39
39
  approver_role TEXT,
40
- approved_at TEXT NOT NULL
40
+ approved_at TEXT NOT NULL,
41
+ tool_name TEXT,
42
+ parameters_hash TEXT,
43
+ companion_id TEXT,
44
+ actor_id TEXT
41
45
  );
42
46
 
43
47
  CREATE TABLE IF NOT EXISTS action_audit_log (
@@ -69,6 +73,22 @@ class SqliteActionStore {
69
73
  catch {
70
74
  // Column already exists or table freshly created
71
75
  }
76
+ try {
77
+ this.db.exec('ALTER TABLE action_approvals ADD COLUMN tool_name TEXT;');
78
+ }
79
+ catch { }
80
+ try {
81
+ this.db.exec('ALTER TABLE action_approvals ADD COLUMN parameters_hash TEXT;');
82
+ }
83
+ catch { }
84
+ try {
85
+ this.db.exec('ALTER TABLE action_approvals ADD COLUMN companion_id TEXT;');
86
+ }
87
+ catch { }
88
+ try {
89
+ this.db.exec('ALTER TABLE action_approvals ADD COLUMN actor_id TEXT;');
90
+ }
91
+ catch { }
72
92
  }
73
93
  initLastAuditHash() {
74
94
  const row = this.db.prepare('SELECT event_hash FROM action_audit_log ORDER BY id DESC LIMIT 1').get();
@@ -134,17 +154,24 @@ class SqliteActionStore {
134
154
  updatedAt: row.updated_at,
135
155
  };
136
156
  }
137
- async saveApproval(executionId, approverActorId, reason, approverRole) {
157
+ async saveApproval(executionId, approverActorId, reason, approverRole, toolName, parametersHash, companionId, actorId) {
138
158
  const stmt = this.db.prepare(`
139
- INSERT INTO action_approvals (execution_id, approver_actor_id, reason, approver_role, approved_at)
140
- VALUES (?, ?, ?, ?, ?)
159
+ INSERT INTO action_approvals (
160
+ execution_id, approver_actor_id, reason, approver_role, approved_at,
161
+ tool_name, parameters_hash, companion_id, actor_id
162
+ )
163
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
141
164
  ON CONFLICT(execution_id) DO UPDATE SET
142
165
  approver_actor_id = excluded.approver_actor_id,
143
166
  reason = excluded.reason,
144
167
  approver_role = excluded.approver_role,
145
- approved_at = excluded.approved_at
168
+ approved_at = excluded.approved_at,
169
+ tool_name = excluded.tool_name,
170
+ parameters_hash = excluded.parameters_hash,
171
+ companion_id = excluded.companion_id,
172
+ actor_id = excluded.actor_id
146
173
  `);
147
- stmt.run(executionId, approverActorId, reason ?? null, approverRole ?? null, new Date().toISOString());
174
+ stmt.run(executionId, approverActorId, reason ?? null, approverRole ?? null, new Date().toISOString(), toolName ?? null, parametersHash ?? null, companionId ?? null, actorId ?? null);
148
175
  }
149
176
  async isActionApproved(executionId) {
150
177
  const stmt = this.db.prepare('SELECT 1 FROM action_approvals WHERE execution_id = ?');
@@ -163,6 +190,10 @@ class SqliteActionStore {
163
190
  reason: row.reason ?? undefined,
164
191
  approverRole: row.approver_role ?? undefined,
165
192
  approvedAt: row.approved_at,
193
+ toolName: row.tool_name ?? undefined,
194
+ parametersHash: row.parameters_hash ?? undefined,
195
+ companionId: row.companion_id ?? undefined,
196
+ actorId: row.actor_id ?? undefined,
166
197
  };
167
198
  }
168
199
  async appendAudit(event) {
@@ -229,6 +229,7 @@ describe('SqliteActionStore Implementation & Durability', () => {
229
229
  await engine1.approveAction({
230
230
  executionId: 'exec-danger-1',
231
231
  approverActorId: 'local-owner',
232
+ approverRole: 'owner',
232
233
  reason: 'Owner confirmed cleanup',
233
234
  });
234
235
  store1.close();
@@ -247,6 +248,14 @@ describe('SqliteActionStore Implementation & Durability', () => {
247
248
  expect(eval2.decision.decisionCode).toBe('ALLOWED_POLICY');
248
249
  expect(eval2.capability).toBeDefined();
249
250
  expect((0, capability_1.verifyCapabilitySignature)(eval2.capability, secretKey)).toBe(true);
251
+ // 5. Tampered action after restart -> rejected due to approval parameter mismatch
252
+ const tamperedAction = {
253
+ ...action,
254
+ parameters: { force: true, dropDatabase: true },
255
+ };
256
+ const evalTampered = await engine2.evaluateAction(tamperedAction);
257
+ expect(evalTampered.decision.allowed).toBe(false);
258
+ expect(evalTampered.decision.decisionCode).toBe('REJECTED_APPROVAL_MISMATCH');
250
259
  store2.close();
251
260
  });
252
261
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siduri-x/core",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "Core runtime types, evidence protocol, action dispatcher, capability validation, and SiduriRuntime protocol",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {