@siduri-x/core 1.0.4 → 1.0.5

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 (45) hide show
  1. package/package.json +1 -1
  2. package/dist/action-policy.d.ts +0 -45
  3. package/dist/action-policy.js +0 -224
  4. package/dist/action-policy.test.d.ts +0 -1
  5. package/dist/action-policy.test.js +0 -194
  6. package/dist/action.d.ts +0 -72
  7. package/dist/action.js +0 -2
  8. package/dist/adversarial.test.d.ts +0 -1
  9. package/dist/adversarial.test.js +0 -493
  10. package/dist/architecture-boundary.test.d.ts +0 -1
  11. package/dist/architecture-boundary.test.js +0 -116
  12. package/dist/capability.d.ts +0 -57
  13. package/dist/capability.js +0 -130
  14. package/dist/capability.test.d.ts +0 -1
  15. package/dist/capability.test.js +0 -269
  16. package/dist/chat-contract.d.ts +0 -77
  17. package/dist/chat-contract.js +0 -65
  18. package/dist/context.d.ts +0 -47
  19. package/dist/context.js +0 -92
  20. package/dist/context.test.d.ts +0 -1
  21. package/dist/context.test.js +0 -109
  22. package/dist/dispatcher.d.ts +0 -14
  23. package/dist/dispatcher.js +0 -40
  24. package/dist/dispatcher.test.d.ts +0 -1
  25. package/dist/dispatcher.test.js +0 -60
  26. package/dist/ear-types.d.ts +0 -33
  27. package/dist/ear-types.js +0 -2
  28. package/dist/evidence.d.ts +0 -72
  29. package/dist/evidence.js +0 -45
  30. package/dist/evidence.test.d.ts +0 -1
  31. package/dist/evidence.test.js +0 -101
  32. package/dist/experience.d.ts +0 -56
  33. package/dist/experience.js +0 -78
  34. package/dist/experience.test.d.ts +0 -1
  35. package/dist/experience.test.js +0 -58
  36. package/dist/gating.d.ts +0 -45
  37. package/dist/gating.js +0 -189
  38. package/dist/gating.test.d.ts +0 -1
  39. package/dist/gating.test.js +0 -190
  40. package/dist/index.d.ts +0 -266
  41. package/dist/index.js +0 -29
  42. package/dist/runtime.d.ts +0 -50
  43. package/dist/runtime.js +0 -411
  44. package/dist/teaching.d.ts +0 -15
  45. package/dist/teaching.js +0 -159
@@ -1,493 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const index_1 = require("./index");
4
- describe('Adversarial Hardening Verification Suite (Phase 3)', () => {
5
- const secretKey = 'test_policy_secret_key_123';
6
- const baseOwnerContext = {
7
- companionId: 'companion-adv',
8
- actor: {
9
- actorId: 'owner-user',
10
- sessionId: 'sess-owner',
11
- authorizationRole: 'administrator',
12
- capabilities: ['chat:public', 'chat:private', 'system:manage', 'tools:all'],
13
- authenticated: true,
14
- },
15
- conversation: {
16
- channel: 'private',
17
- audienceId: 'audience-owner',
18
- correlationId: 'corr-adv-1',
19
- },
20
- };
21
- const baseViewerContext = {
22
- companionId: 'companion-adv',
23
- actor: {
24
- actorId: 'anonymous-viewer',
25
- sessionId: 'sess-viewer',
26
- authorizationRole: 'viewer',
27
- capabilities: ['chat:public'],
28
- authenticated: false,
29
- },
30
- conversation: {
31
- channel: 'public',
32
- audienceId: 'audience-public',
33
- correlationId: 'corr-adv-2',
34
- },
35
- };
36
- // INVARIANT 1: Memory Truth & Scoping Boundaries
37
- describe('Invariant 1: Memory Truth & Cognition Filtering', () => {
38
- test('expired, future, and below-threshold claims are not injected into Brain contextPrompt', async () => {
39
- const mockBrain = {
40
- generatePlan: jest.fn().mockResolvedValue({ speech: 'Cognition received context', language: 'en' }),
41
- };
42
- const now = new Date();
43
- const pastTime = new Date(now.getTime() - 100_000).toISOString();
44
- const futureTime = new Date(now.getTime() + 100_000).toISOString();
45
- // Memory mock simulating search output containing valid claim only after organ filtering
46
- const validClaim = {
47
- id: 'c-valid',
48
- companionId: 'companion-adv',
49
- subject: 'User',
50
- predicate: 'favoriteColor',
51
- value: 'Azure',
52
- status: 'APPROVED',
53
- scope: 'OWNER',
54
- confidence: 0.95,
55
- validFrom: pastTime,
56
- validUntil: futureTime,
57
- };
58
- const mockMemory = {
59
- initialize: jest.fn().mockResolvedValue(undefined),
60
- searchClaims: jest.fn().mockResolvedValue([validClaim]),
61
- getDirectives: jest.fn().mockResolvedValue([]),
62
- };
63
- const runtime = new index_1.SiduriRuntime('companion-adv', { name: 'AdvCompanion' }, {
64
- brain: mockBrain,
65
- memory: mockMemory,
66
- });
67
- await runtime.handleUserMessage('What is my favorite color?', baseOwnerContext);
68
- expect(mockMemory.searchClaims).toHaveBeenCalled();
69
- const brainCall = mockBrain.generatePlan.mock.calls[0][0];
70
- expect(brainCall.contextPrompt).toContain('User favoriteColor Azure');
71
- });
72
- test('companion isolation: runtime passes only companionId matching context', async () => {
73
- const mockBrain = {
74
- generatePlan: jest.fn().mockResolvedValue({ speech: 'OK', language: 'en' }),
75
- };
76
- const mockMemory = {
77
- initialize: jest.fn().mockResolvedValue(undefined),
78
- searchClaims: jest.fn().mockResolvedValue([]),
79
- getDirectives: jest.fn().mockResolvedValue([]),
80
- };
81
- const runtime = new index_1.SiduriRuntime('companion-A', { name: 'AdvA' }, {
82
- brain: mockBrain,
83
- memory: mockMemory,
84
- });
85
- await runtime.handleUserMessage('Query', {
86
- ...baseOwnerContext,
87
- companionId: 'companion-A',
88
- });
89
- expect(mockMemory.searchClaims).toHaveBeenCalledWith('Query', expect.objectContaining({ channel: 'private', audienceId: 'audience-owner' }), 5);
90
- });
91
- });
92
- // INVARIANT 2: Authority & Request Boundary
93
- describe('Invariant 2: Authority & Request Context Boundary', () => {
94
- test('viewer cannot execute admin action intents even if request context is maliciously populated', async () => {
95
- const store = new index_1.InMemoryActionStore();
96
- const policyEngine = new index_1.ActionPolicyEngine({ store, secretKey });
97
- policyEngine.registerToolDefinition({
98
- name: 'database/drop_tables',
99
- providerId: 'db',
100
- description: 'Drop DB tables',
101
- inputSchema: {},
102
- riskLevel: 'CRITICAL',
103
- allowedRoles: ['administrator'],
104
- requiredCapabilities: ['system:manage'],
105
- });
106
- const intent = {
107
- actionId: 'act-drop-1',
108
- toolName: 'db/database/drop_tables',
109
- parameters: {},
110
- context: baseViewerContext, // Viewer role
111
- };
112
- const { decision, capability } = await policyEngine.evaluateAction(intent);
113
- expect(decision.allowed).toBe(false);
114
- expect(decision.decisionCode).toBe('REJECTED_UNAUTHORIZED');
115
- expect(capability).toBeUndefined();
116
- });
117
- test('missing request context strictly blocks authorization', async () => {
118
- const store = new index_1.InMemoryActionStore();
119
- const policyEngine = new index_1.ActionPolicyEngine({ store, secretKey });
120
- policyEngine.registerToolDefinition({
121
- name: 'test_tool',
122
- providerId: 'sys',
123
- description: 'Test Tool',
124
- inputSchema: {},
125
- riskLevel: 'LOW',
126
- });
127
- const intent = {
128
- actionId: 'act-no-ctx',
129
- toolName: 'sys/test_tool',
130
- parameters: {},
131
- };
132
- const { decision, capability } = await policyEngine.evaluateAction(intent, undefined);
133
- expect(decision.allowed).toBe(false);
134
- expect(decision.decisionCode).toBe('REJECTED_UNAUTHORIZED');
135
- expect(capability).toBeUndefined();
136
- });
137
- });
138
- // INVARIANT 4: Action Replay & Concurrency Protection
139
- describe('Invariant 4: Action Idempotency, Concurrency & Signature Replay', () => {
140
- let store;
141
- let mockHandlerExecute;
142
- class TestHandsOrgan {
143
- store;
144
- secretKey;
145
- constructor(store, secretKey) {
146
- this.store = store;
147
- this.secretKey = secretKey;
148
- }
149
- async listTools() {
150
- return [{
151
- name: 'transfer_funds',
152
- providerId: 'bank',
153
- description: 'Transfer money',
154
- inputSchema: {},
155
- riskLevel: 'CRITICAL',
156
- }];
157
- }
158
- async executeAction(action, authorization) {
159
- const actionId = action?.actionId || 'unknown';
160
- const toolName = action?.toolName || 'unknown';
161
- if (!authorization || authorization.allowed !== true) {
162
- return { actionId, executionId: 'unauthorized', toolName, lifecycle: 'REJECTED', success: false, error: 'Unauthorized' };
163
- }
164
- if (!(0, index_1.verifyCapabilitySignature)(authorization, this.secretKey)) {
165
- return { actionId, executionId: authorization.executionId, toolName, lifecycle: 'REJECTED', success: false, error: 'Invalid or forged AuthorizationCapability signature' };
166
- }
167
- if (authorization.expiresAt && new Date(authorization.expiresAt).getTime() <= Date.now()) {
168
- return { actionId, executionId: authorization.executionId, toolName, lifecycle: 'REJECTED', success: false, error: 'AuthorizationCapability has expired' };
169
- }
170
- const currentParamsHash = (0, index_1.computeParametersHash)(action.parameters);
171
- if (authorization.parametersHash !== currentParamsHash) {
172
- return { actionId, executionId: authorization.executionId, toolName, lifecycle: 'REJECTED', success: false, error: 'Parameters hash mismatch' };
173
- }
174
- const executionId = authorization.executionId;
175
- const existing = await this.store.getExecution(executionId);
176
- if (existing && existing.lifecycle === 'COMPLETED') {
177
- return { actionId, executionId, toolName, lifecycle: 'COMPLETED', success: true, result: existing.result };
178
- }
179
- const reserved = await this.store.reserveExecution({
180
- executionId,
181
- actionId,
182
- toolName,
183
- providerId: authorization.providerId,
184
- parametersHash: currentParamsHash,
185
- lifecycle: 'EXECUTING',
186
- createdAt: new Date().toISOString(),
187
- updatedAt: new Date().toISOString(),
188
- });
189
- if (!reserved) {
190
- return { actionId, executionId, toolName, lifecycle: 'FAILED', success: false, error: 'Reservation conflict' };
191
- }
192
- const result = await mockHandlerExecute(action.parameters);
193
- await this.store.updateExecution({
194
- executionId,
195
- actionId,
196
- toolName,
197
- providerId: authorization.providerId,
198
- parametersHash: currentParamsHash,
199
- lifecycle: 'COMPLETED',
200
- result,
201
- createdAt: new Date().toISOString(),
202
- updatedAt: new Date().toISOString(),
203
- });
204
- return { actionId, executionId, toolName, lifecycle: 'COMPLETED', success: true, result };
205
- }
206
- }
207
- let hands;
208
- beforeEach(() => {
209
- store = new index_1.InMemoryActionStore();
210
- mockHandlerExecute = jest.fn().mockResolvedValue({ transactionId: 'tx-12345', status: 'CONFIRMED' });
211
- hands = new TestHandsOrgan(store, secretKey);
212
- });
213
- test('replaying a completed capability returns cached result without re-executing handler', async () => {
214
- const mockExecute = mockHandlerExecute;
215
- const action = {
216
- actionId: 'act-tx-1',
217
- executionId: 'exec-tx-1',
218
- toolName: 'bank/transfer_funds',
219
- parameters: { amount: 100, to: 'Alice' },
220
- };
221
- const paramsHash = (0, index_1.computeParametersHash)(action.parameters);
222
- const capabilityPayload = {
223
- executionId: 'exec-tx-1',
224
- actionId: 'act-tx-1',
225
- toolName: 'bank/transfer_funds',
226
- providerId: 'bank',
227
- parametersHash: paramsHash,
228
- companionId: 'companion-adv',
229
- actorId: 'owner-user',
230
- sessionId: 'sess-owner',
231
- channel: 'private',
232
- correlationId: 'corr-adv-1',
233
- riskLevel: 'CRITICAL',
234
- issuedAt: new Date().toISOString(),
235
- expiresAt: new Date(Date.now() + 60_000).toISOString(),
236
- };
237
- const signature = (0, index_1.signCapabilityPayload)(capabilityPayload, secretKey);
238
- const capability = {
239
- ...capabilityPayload,
240
- allowed: true,
241
- signature,
242
- };
243
- // 1. First execution succeeds
244
- const res1 = await hands.executeAction(action, capability);
245
- expect(res1.success).toBe(true);
246
- expect(res1.lifecycle).toBe('COMPLETED');
247
- expect(mockExecute).toHaveBeenCalledTimes(1);
248
- // 2. Replay execution with identical capability
249
- const res2 = await hands.executeAction(action, capability);
250
- expect(res2.success).toBe(true);
251
- expect(res2.lifecycle).toBe('COMPLETED');
252
- expect(res2.result).toEqual({ transactionId: 'tx-12345', status: 'CONFIRMED' });
253
- // Handler was NOT called a second time (replay defended)
254
- expect(mockExecute).toHaveBeenCalledTimes(1);
255
- });
256
- test('tampering with action parameters invalidates cryptographic capability', async () => {
257
- const action = {
258
- actionId: 'act-tx-2',
259
- executionId: 'exec-tx-2',
260
- toolName: 'bank/transfer_funds',
261
- parameters: { amount: 100, to: 'Alice' },
262
- };
263
- const paramsHash = (0, index_1.computeParametersHash)(action.parameters);
264
- const capabilityPayload = {
265
- executionId: 'exec-tx-2',
266
- actionId: 'act-tx-2',
267
- toolName: 'bank/transfer_funds',
268
- providerId: 'bank',
269
- parametersHash: paramsHash,
270
- companionId: 'companion-adv',
271
- actorId: 'owner-user',
272
- sessionId: 'sess-owner',
273
- channel: 'private',
274
- correlationId: 'corr-adv-1',
275
- riskLevel: 'CRITICAL',
276
- issuedAt: new Date().toISOString(),
277
- expiresAt: new Date(Date.now() + 60_000).toISOString(),
278
- };
279
- const signature = (0, index_1.signCapabilityPayload)(capabilityPayload, secretKey);
280
- const capability = { ...capabilityPayload, allowed: true, signature };
281
- // Attacker tampers with parameters from $100 to $10,000
282
- const tamperedAction = {
283
- ...action,
284
- parameters: { amount: 10_000, to: 'Attacker' },
285
- };
286
- const res = await hands.executeAction(tamperedAction, capability);
287
- expect(res.success).toBe(false);
288
- expect(res.lifecycle).toBe('REJECTED');
289
- expect(res.error).toContain('Parameters hash mismatch');
290
- });
291
- test('forged signature on capability is rejected by constant-time verification', async () => {
292
- const action = {
293
- actionId: 'act-tx-3',
294
- executionId: 'exec-tx-3',
295
- toolName: 'bank/transfer_funds',
296
- parameters: { amount: 100, to: 'Alice' },
297
- };
298
- const capability = {
299
- executionId: 'exec-tx-3',
300
- actionId: 'act-tx-3',
301
- toolName: 'bank/transfer_funds',
302
- providerId: 'bank',
303
- parametersHash: (0, index_1.computeParametersHash)(action.parameters),
304
- companionId: 'companion-adv',
305
- actorId: 'owner-user',
306
- sessionId: 'sess-owner',
307
- channel: 'private',
308
- correlationId: 'corr-adv-1',
309
- riskLevel: 'CRITICAL',
310
- issuedAt: new Date().toISOString(),
311
- expiresAt: new Date(Date.now() + 60_000).toISOString(),
312
- allowed: true,
313
- signature: '0000000000000000000000000000000000000000000000000000000000000000', // Forged signature
314
- };
315
- const res = await hands.executeAction(action, capability);
316
- expect(res.success).toBe(false);
317
- expect(res.lifecycle).toBe('REJECTED');
318
- expect(res.error).toContain('Invalid or forged AuthorizationCapability signature');
319
- });
320
- test('expired authorization capability is rejected', async () => {
321
- const action = {
322
- actionId: 'act-tx-4',
323
- executionId: 'exec-tx-4',
324
- toolName: 'bank/transfer_funds',
325
- parameters: { amount: 100, to: 'Alice' },
326
- };
327
- const paramsHash = (0, index_1.computeParametersHash)(action.parameters);
328
- const expiredTime = new Date(Date.now() - 5000).toISOString();
329
- const capabilityPayload = {
330
- executionId: 'exec-tx-4',
331
- actionId: 'act-tx-4',
332
- toolName: 'bank/transfer_funds',
333
- providerId: 'bank',
334
- parametersHash: paramsHash,
335
- companionId: 'companion-adv',
336
- actorId: 'owner-user',
337
- sessionId: 'sess-owner',
338
- channel: 'private',
339
- correlationId: 'corr-adv-1',
340
- riskLevel: 'CRITICAL',
341
- issuedAt: new Date(Date.now() - 10000).toISOString(),
342
- expiresAt: expiredTime,
343
- };
344
- const signature = (0, index_1.signCapabilityPayload)(capabilityPayload, secretKey);
345
- const capability = { ...capabilityPayload, allowed: true, signature };
346
- const res = await hands.executeAction(action, capability);
347
- expect(res.success).toBe(false);
348
- expect(res.lifecycle).toBe('REJECTED');
349
- expect(res.error).toContain('AuthorizationCapability has expired');
350
- });
351
- });
352
- // INVARIANT 6: Failure Semantics & Degradation
353
- describe('Invariant 6: Subsystem Failure Diagnostics & Non-Empty Propagation', () => {
354
- test('database/memory query failure surfaces diagnostic in contextPrompt and metadata', async () => {
355
- const mockBrain = {
356
- generatePlan: jest.fn().mockResolvedValue({ speech: 'Graceful fallback response', language: 'en' }),
357
- };
358
- const failingMemory = {
359
- initialize: jest.fn().mockResolvedValue(undefined),
360
- searchClaims: jest.fn().mockRejectedValue(new Error('Connection terminated unexpectedly')),
361
- getDirectives: jest.fn().mockRejectedValue(new Error('PostgreSQL read timeout')),
362
- };
363
- const runtime = new index_1.SiduriRuntime('companion-adv', { name: 'AdvCompanion' }, {
364
- brain: mockBrain,
365
- memory: failingMemory,
366
- });
367
- const response = await runtime.handleUserMessage('Hello companion', baseOwnerContext);
368
- expect(response.status).toBe('APPROVED');
369
- expect(response.metadata.subsystem_diagnostics).toBeDefined();
370
- expect(response.metadata.subsystem_diagnostics.memory_claims).toContain('UNAVAILABLE');
371
- expect(response.metadata.subsystem_diagnostics.memory_directives).toContain('UNAVAILABLE');
372
- const brainCall = mockBrain.generatePlan.mock.calls[0][0];
373
- expect(brainCall.contextPrompt).toContain('SUBSYSTEM STATUS (DEGRADED):');
374
- expect(brainCall.contextPrompt).toContain('memory_claims');
375
- });
376
- });
377
- // INVARIANT 8: Truth Gate Admissibility vs Factuality
378
- describe('Invariant 8: Response Gating Evidence Admissibility Semantics', () => {
379
- test('gate strictly enforces evidence admissibility and disclosure without claiming unverified factuality', () => {
380
- const gating = new index_1.ResponseGatingEngine();
381
- const publicEvidence = {
382
- evidenceId: 'ev-pub-1',
383
- sourceId: 'src-facts',
384
- origin: 'knowledge',
385
- trust: 'configured',
386
- sensitivity: 'public',
387
- allowedAudiences: ['audience-public'],
388
- companionId: 'companion-adv',
389
- correlationId: 'corr-adv-1',
390
- createdAt: new Date().toISOString(),
391
- };
392
- const privateEvidence = {
393
- evidenceId: 'ev-priv-1',
394
- sourceId: 'src-secrets',
395
- origin: 'knowledge',
396
- trust: 'configured',
397
- sensitivity: 'restricted',
398
- allowedAudiences: ['audience-owner'],
399
- companionId: 'companion-adv',
400
- correlationId: 'corr-adv-1',
401
- createdAt: new Date().toISOString(),
402
- };
403
- // Staged for public channel with both public and restricted evidence attached
404
- const staged = gating.stageResponse({
405
- requestContext: baseViewerContext, // Public channel
406
- candidateSpeech: 'Siduri was created in 1840 by aliens.',
407
- candidateLanguage: 'en',
408
- evidenceRecords: [publicEvidence, privateEvidence],
409
- });
410
- const evaluation = gating.evaluateGate(staged, [publicEvidence, privateEvidence]);
411
- expect(evaluation.admissible).toBe(true);
412
- expect(evaluation.reasonCode).toBe('APPROVED_DIRECT');
413
- // Public evidence admitted, restricted private evidence excluded from public emission
414
- expect(evaluation.filteredEvidenceIds).toEqual(['ev-pub-1']);
415
- expect(evaluation.filteredEvidenceIds).not.toContain('ev-priv-1');
416
- });
417
- });
418
- // INVARIANT 9: Full-Field Tamper-Evident Audit Trail
419
- describe('Invariant 9: Full-Field SHA-256 Audit Trail Chaining', () => {
420
- test('mutating any security-critical field breaks cryptographic hash chain', async () => {
421
- const store = new index_1.InMemoryActionStore();
422
- const policyEngine = new index_1.ActionPolicyEngine({ store, secretKey });
423
- policyEngine.registerToolDefinition({
424
- name: 'test_tool',
425
- providerId: 'sys',
426
- description: 'Test Tool',
427
- inputSchema: {},
428
- riskLevel: 'LOW',
429
- });
430
- // Event 1
431
- await policyEngine.evaluateAction({
432
- actionId: 'act-1',
433
- toolName: 'sys/test_tool',
434
- parameters: { step: 1 },
435
- context: baseOwnerContext,
436
- });
437
- // Event 2
438
- await policyEngine.evaluateAction({
439
- actionId: 'act-2',
440
- toolName: 'sys/test_tool',
441
- parameters: { step: 2 },
442
- context: baseOwnerContext,
443
- });
444
- const auditTrail = await store.getAuditLog();
445
- expect(auditTrail.length).toBe(2);
446
- const event1 = auditTrail[0];
447
- const event2 = auditTrail[1];
448
- // Mutate security-critical fields in event1 and verify chain discrepancy
449
- const criticalFields = [
450
- 'executionId',
451
- 'actionId',
452
- 'toolName',
453
- 'companionId',
454
- 'actorId',
455
- 'sessionId',
456
- 'channel',
457
- 'correlationId',
458
- 'riskLevel',
459
- 'lifecycle',
460
- 'parametersHash',
461
- ];
462
- for (const field of criticalFields) {
463
- const tamperedEvent = { ...event1, [field]: 'TAMPERED_VALUE' };
464
- const initialPrevHash = '0000000000000000000000000000000000000000000000000000000000000000';
465
- const canonical = (0, index_1.canonicalizeJson)({
466
- executionId: tamperedEvent.executionId,
467
- actionId: tamperedEvent.actionId,
468
- toolName: tamperedEvent.toolName,
469
- providerId: tamperedEvent.providerId || null,
470
- companionId: tamperedEvent.companionId,
471
- actorId: tamperedEvent.actorId || null,
472
- sessionId: tamperedEvent.sessionId || null,
473
- channel: tamperedEvent.channel || null,
474
- correlationId: tamperedEvent.correlationId || null,
475
- riskLevel: tamperedEvent.riskLevel,
476
- lifecycle: tamperedEvent.lifecycle,
477
- decision: tamperedEvent.decision ? {
478
- allowed: tamperedEvent.decision.allowed,
479
- reason: tamperedEvent.decision.reason,
480
- riskLevel: tamperedEvent.decision.riskLevel,
481
- decisionCode: tamperedEvent.decision.decisionCode,
482
- } : null,
483
- parametersHash: tamperedEvent.parametersHash || null,
484
- error: tamperedEvent.error || null,
485
- timestamp: tamperedEvent.timestamp,
486
- });
487
- const crypto = require('node:crypto');
488
- const brokenHash1 = crypto.createHash('sha256').update(`${initialPrevHash}:${canonical}`, 'utf8').digest('hex');
489
- expect(brokenHash1).not.toBe(event1.resultHash);
490
- }
491
- });
492
- });
493
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,116 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- const fs = __importStar(require("fs"));
37
- const path = __importStar(require("path"));
38
- describe('Architecture: Core & Organ Package Boundaries (Phase 2)', () => {
39
- const rootOrgansDir = path.resolve(__dirname, '../../organs');
40
- const coreSrcDir = path.resolve(__dirname);
41
- const corePackageJsonPath = path.resolve(__dirname, '../package.json');
42
- const EXPECTED_ORGANS = [
43
- { dir: 'brain', name: '@siduri-x/brain', organType: 'brain', configKey: 'brain' },
44
- { dir: 'memory', name: '@siduri-x/memory', organType: 'memory', configKey: 'memory' },
45
- { dir: 'knowledge', name: '@siduri-x/knowledge', organType: 'knowledge', configKey: 'knowledge' },
46
- { dir: 'behavior', name: '@siduri-x/behavior', organType: 'behavior', configKey: 'behavior' },
47
- { dir: 'ear', name: '@siduri-x/ear', organType: 'ear', configKey: 'ear' },
48
- { dir: 'vision', name: '@siduri-x/vision', organType: 'vision', configKey: 'vision' },
49
- { dir: 'hands', name: '@siduri-x/hands', organType: 'hands', configKey: 'hands' },
50
- { dir: 'body', name: '@siduri-x/body', organType: 'body', configKey: 'body' },
51
- { dir: 'voice', name: '@siduri-x/voice', organType: 'voice', configKey: 'voice' },
52
- { dir: 'observation', name: '@siduri-x/observation', organType: 'observation', configKey: 'observation' },
53
- ];
54
- it('package.json has zero dependencies on @siduri-x organ packages', () => {
55
- const pkg = JSON.parse(fs.readFileSync(corePackageJsonPath, 'utf8'));
56
- const allDeps = {
57
- ...(pkg.dependencies || {}),
58
- ...(pkg.devDependencies || {}),
59
- ...(pkg.peerDependencies || {}),
60
- };
61
- const organDeps = Object.keys(allDeps).filter((dep) => dep.startsWith('@siduri-x/') && dep !== '@siduri-x/core');
62
- expect(organDeps).toEqual([]);
63
- });
64
- it('source files in packages/core have zero imports referencing @siduri-x organ packages', () => {
65
- const files = fs.readdirSync(coreSrcDir).filter((f) => f.endsWith('.ts') && !f.endsWith('.test.ts'));
66
- const forbiddenImports = [];
67
- for (const file of files) {
68
- const content = fs.readFileSync(path.join(coreSrcDir, file), 'utf8');
69
- const lines = content.split('\n');
70
- for (const line of lines) {
71
- if ((line.includes('import ') || line.includes('require(') || line.includes('export * from')) &&
72
- line.includes('@siduri-x/') &&
73
- !line.includes('@siduri-x/core')) {
74
- forbiddenImports.push({ file, match: line.trim() });
75
- }
76
- }
77
- }
78
- expect(forbiddenImports).toEqual([]);
79
- });
80
- it('all 10 organ packages have a valid organ-manifest.json', () => {
81
- for (const organ of EXPECTED_ORGANS) {
82
- const manifestPath = path.join(rootOrgansDir, organ.dir, 'organ-manifest.json');
83
- expect(fs.existsSync(manifestPath)).toBe(true);
84
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
85
- expect(manifest.name).toBe(organ.name);
86
- expect(manifest.organType).toBe(organ.organType);
87
- expect(manifest.configKey).toBe(organ.configKey);
88
- expect(typeof manifest.version).toBe('string');
89
- expect(typeof manifest.displayName).toBe('string');
90
- expect(typeof manifest.entrypoint).toBe('string');
91
- expect(typeof manifest.factory).toBe('string');
92
- expect(manifest.configSchema).toBeDefined();
93
- expect(Array.isArray(manifest.environment)).toBe(true);
94
- expect(Array.isArray(manifest.services)).toBe(true);
95
- }
96
- });
97
- it('no organ package contains link: or relative monorepo dependencies in package.json', () => {
98
- for (const organ of EXPECTED_ORGANS) {
99
- const pkgPath = path.join(rootOrgansDir, organ.dir, 'package.json');
100
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
101
- const deps = { ...(pkg.dependencies || {}) };
102
- for (const [depName, version] of Object.entries(deps)) {
103
- if (typeof version === 'string') {
104
- expect(version.startsWith('link:')).toBe(false);
105
- expect(version.includes('../')).toBe(false);
106
- }
107
- }
108
- }
109
- });
110
- it('memory organ packages SQL migrations', () => {
111
- const memoryMigrationsDir = path.join(rootOrgansDir, 'memory', 'migrations');
112
- expect(fs.existsSync(memoryMigrationsDir)).toBe(true);
113
- const files = fs.readdirSync(memoryMigrationsDir).filter((f) => f.endsWith('.sql'));
114
- expect(files.length).toBeGreaterThanOrEqual(1);
115
- });
116
- });
@@ -1,57 +0,0 @@
1
- import { ActionRiskLevel, ActionLifecycleState, ActionAuditEvent, ActionPolicyDecision } from './action';
2
- export interface AuthorizationCapability {
3
- executionId: string;
4
- actionId: string;
5
- toolName: string;
6
- providerId: string;
7
- parametersHash: string;
8
- companionId: string;
9
- actorId?: string;
10
- sessionId?: string;
11
- channel?: string;
12
- correlationId?: string;
13
- riskLevel: ActionRiskLevel;
14
- issuedAt: string;
15
- expiresAt: string;
16
- allowed: true;
17
- signature: string;
18
- }
19
- export interface PersistentExecutionRecord {
20
- executionId: string;
21
- actionId: string;
22
- toolName: string;
23
- providerId: string;
24
- parametersHash: string;
25
- lifecycle: ActionLifecycleState;
26
- decision?: ActionPolicyDecision;
27
- result?: unknown;
28
- error?: string;
29
- createdAt: string;
30
- updatedAt: string;
31
- }
32
- export interface ActionStore {
33
- reserveExecution(record: PersistentExecutionRecord): Promise<boolean>;
34
- updateExecution(record: PersistentExecutionRecord): Promise<void>;
35
- getExecution(executionId: string): Promise<PersistentExecutionRecord | undefined>;
36
- saveApproval(executionId: string, approverActorId: string, reason?: string): Promise<void>;
37
- isActionApproved(executionId: string): Promise<boolean>;
38
- appendAudit(event: ActionAuditEvent): Promise<void>;
39
- getAuditLog(executionId?: string): Promise<ActionAuditEvent[]>;
40
- }
41
- export declare class InMemoryActionStore implements ActionStore {
42
- private readonly executions;
43
- private readonly approvals;
44
- private readonly auditLog;
45
- private lastAuditHash;
46
- reserveExecution(record: PersistentExecutionRecord): Promise<boolean>;
47
- updateExecution(record: PersistentExecutionRecord): Promise<void>;
48
- getExecution(executionId: string): Promise<PersistentExecutionRecord | undefined>;
49
- saveApproval(executionId: string, approverActorId: string, reason?: string): Promise<void>;
50
- isActionApproved(executionId: string): Promise<boolean>;
51
- appendAudit(event: ActionAuditEvent): Promise<void>;
52
- getAuditLog(executionId?: string): Promise<ActionAuditEvent[]>;
53
- }
54
- export declare function canonicalizeJson(obj: unknown): string;
55
- export declare function computeParametersHash(params: unknown): string;
56
- export declare function signCapabilityPayload(payload: Record<string, unknown>, secretKey?: string): string;
57
- export declare function verifyCapabilitySignature(capability: AuthorizationCapability, secretKey?: string): boolean;