@siduri-x/api 2.0.1 → 2.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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +16 -13
- package/dist/app.d.ts +2 -34
- package/dist/app.js +143 -129
- package/dist/boot.d.ts +92 -0
- package/dist/boot.js +146 -0
- package/dist/boot.test.d.ts +1 -0
- package/dist/boot.test.js +84 -0
- package/dist/context-mapper.d.ts +3 -1
- package/dist/context-mapper.js +78 -20
- package/dist/context-mapper.test.js +91 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +10 -90
- package/dist/index.test.js +4 -4
- package/dist/knowledge.test.d.ts +1 -0
- package/dist/knowledge.test.js +138 -0
- package/dist/t6-security.test.js +52 -3
- package/package.json +6 -6
- package/src/app.ts +154 -173
- package/src/boot.test.ts +103 -0
- package/src/boot.ts +190 -0
- package/src/context-mapper.test.ts +100 -0
- package/src/context-mapper.ts +85 -20
- package/src/index.test.ts +4 -4
- package/src/index.ts +10 -100
- package/src/knowledge.test.ts +156 -0
- package/src/t6-security.test.ts +56 -3
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const supertest_1 = __importDefault(require("supertest"));
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const app_1 = require("./app");
|
|
10
|
+
const runtime_1 = require("./runtime");
|
|
11
|
+
const knowledge_1 = require("@siduri-x/knowledge");
|
|
12
|
+
describe('Life Database & UnifiedKnowledgeOrgan API Integration', () => {
|
|
13
|
+
const testDbPath = node_path_1.default.resolve(__dirname, '../test-api-knowledge.sqlite');
|
|
14
|
+
let app;
|
|
15
|
+
let runtime;
|
|
16
|
+
let knowledge;
|
|
17
|
+
const mockAuthHeader = { 'Authorization': 'Bearer test-token' };
|
|
18
|
+
const cleanDb = () => {
|
|
19
|
+
for (const file of [testDbPath, `${testDbPath}-shm`, `${testDbPath}-wal`]) {
|
|
20
|
+
if (node_fs_1.default.existsSync(file)) {
|
|
21
|
+
try {
|
|
22
|
+
node_fs_1.default.unlinkSync(file);
|
|
23
|
+
}
|
|
24
|
+
catch { }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
beforeAll(async () => {
|
|
29
|
+
process.env.AUTH_TOKEN = 'test-token';
|
|
30
|
+
cleanDb();
|
|
31
|
+
knowledge = new knowledge_1.UnifiedKnowledgeOrgan({
|
|
32
|
+
lifeDatabase: true,
|
|
33
|
+
dbPath: testDbPath,
|
|
34
|
+
});
|
|
35
|
+
const mockBrain = {
|
|
36
|
+
generatePlan: jest.fn().mockImplementation(async (ctx) => {
|
|
37
|
+
return {
|
|
38
|
+
speech: `I see context: ${ctx.contextPrompt || 'none'}`,
|
|
39
|
+
language: 'en',
|
|
40
|
+
};
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
runtime = new runtime_1.SiduriRuntime('test-comp', { name: 'Test Companion', organs: { knowledge: { provider: 'unified', dbPath: testDbPath } } }, {
|
|
44
|
+
brain: mockBrain,
|
|
45
|
+
knowledge,
|
|
46
|
+
externalKnowledge: knowledge.eAdapter ?? knowledge,
|
|
47
|
+
});
|
|
48
|
+
await runtime.initialize();
|
|
49
|
+
const runtimes = new Map([['test-comp', runtime]]);
|
|
50
|
+
const instance = (0, app_1.createApp)(runtimes);
|
|
51
|
+
app = instance.app;
|
|
52
|
+
});
|
|
53
|
+
afterAll(async () => {
|
|
54
|
+
knowledge.close();
|
|
55
|
+
delete process.env.AUTH_TOKEN;
|
|
56
|
+
cleanDb();
|
|
57
|
+
});
|
|
58
|
+
test('seeds inventory item and queries via GET /knowledge/inventory', async () => {
|
|
59
|
+
await knowledge.inventory.saveItem({
|
|
60
|
+
id: 'inv-item-1',
|
|
61
|
+
companionId: 'test-comp',
|
|
62
|
+
entityName: 'Hydro Visor',
|
|
63
|
+
domain: 'hardware',
|
|
64
|
+
properties: { model: 'V1', resolution: '4K' },
|
|
65
|
+
updatedAt: new Date().toISOString(),
|
|
66
|
+
});
|
|
67
|
+
const res = await (0, supertest_1.default)(app)
|
|
68
|
+
.get('/knowledge/inventory?id=test-comp')
|
|
69
|
+
.set(mockAuthHeader);
|
|
70
|
+
expect(res.status).toBe(200);
|
|
71
|
+
expect(res.body.items).toHaveLength(1);
|
|
72
|
+
expect(res.body.items[0].entityName).toBe('Hydro Visor');
|
|
73
|
+
expect(res.body.items[0].domain).toBe('hardware');
|
|
74
|
+
});
|
|
75
|
+
test('seeds finance entry and queries via GET /knowledge/finance', async () => {
|
|
76
|
+
await knowledge.finance.addEntry({
|
|
77
|
+
id: 'fin-1',
|
|
78
|
+
companionId: 'test-comp',
|
|
79
|
+
category: 'subscription',
|
|
80
|
+
amount: -15.99,
|
|
81
|
+
currency: 'USD',
|
|
82
|
+
timestamp: new Date().toISOString(),
|
|
83
|
+
});
|
|
84
|
+
const res = await (0, supertest_1.default)(app)
|
|
85
|
+
.get('/knowledge/finance?id=test-comp')
|
|
86
|
+
.set(mockAuthHeader);
|
|
87
|
+
expect(res.status).toBe(200);
|
|
88
|
+
expect(res.body.entries).toHaveLength(1);
|
|
89
|
+
expect(res.body.entries[0].category).toBe('subscription');
|
|
90
|
+
expect(res.body.summary).toBeDefined();
|
|
91
|
+
expect(res.body.summary.totalExpenses).toBe(15.99);
|
|
92
|
+
});
|
|
93
|
+
test('queries life snapshot via GET /knowledge/life', async () => {
|
|
94
|
+
const res = await (0, supertest_1.default)(app)
|
|
95
|
+
.get('/knowledge/life?id=test-comp&q=Hydro')
|
|
96
|
+
.set(mockAuthHeader);
|
|
97
|
+
expect(res.status).toBe(200);
|
|
98
|
+
expect(res.body.matchedInventory).toHaveLength(1);
|
|
99
|
+
expect(res.body.matchedInventory[0].entityName).toBe('Hydro Visor');
|
|
100
|
+
expect(res.body.formattedContext).toContain('<life_context>');
|
|
101
|
+
});
|
|
102
|
+
test('chat request triggers Stream D and injects Life DB context into cognition prompt', async () => {
|
|
103
|
+
const res = await (0, supertest_1.default)(app)
|
|
104
|
+
.post('/chat')
|
|
105
|
+
.send({
|
|
106
|
+
id: 'test-comp',
|
|
107
|
+
message: 'Tell me about the Hydro Visor specs',
|
|
108
|
+
history: [],
|
|
109
|
+
});
|
|
110
|
+
expect(res.status).toBe(200);
|
|
111
|
+
expect(res.body.response.subtitle_en).toContain('Hydro Visor');
|
|
112
|
+
});
|
|
113
|
+
test('boot endpoint instantiates UnifiedKnowledgeOrgan with Life DB enabled', async () => {
|
|
114
|
+
const bootRes = await (0, supertest_1.default)(app)
|
|
115
|
+
.post('/boot')
|
|
116
|
+
.set(mockAuthHeader)
|
|
117
|
+
.send({
|
|
118
|
+
id: 'booted-comp',
|
|
119
|
+
config: {
|
|
120
|
+
name: 'Booted Companion',
|
|
121
|
+
organs: {
|
|
122
|
+
knowledge: {
|
|
123
|
+
provider: 'unified',
|
|
124
|
+
dbPath: testDbPath,
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
expect(bootRes.status).toBe(200);
|
|
130
|
+
expect(bootRes.body.success).toBe(true);
|
|
131
|
+
// Verify the booted companion's knowledge organ is UnifiedKnowledgeOrgan with working Life DB
|
|
132
|
+
const lifeRes = await (0, supertest_1.default)(app)
|
|
133
|
+
.get('/knowledge/life?id=booted-comp')
|
|
134
|
+
.set(mockAuthHeader);
|
|
135
|
+
expect(lifeRes.status).toBe(200);
|
|
136
|
+
expect(lifeRes.body.matchedInventory).toEqual([]);
|
|
137
|
+
});
|
|
138
|
+
});
|
package/dist/t6-security.test.js
CHANGED
|
@@ -232,6 +232,55 @@ describe('T6 Security & Operations Threat Model Suite', () => {
|
|
|
232
232
|
expect(actionResults[0].lifecycle).toBe('REJECTED');
|
|
233
233
|
expect(actionResults[0].error).toContain('rejected by policy');
|
|
234
234
|
});
|
|
235
|
+
test('Adversarial Boundary: Client attempting to forge administrator role or system capabilities via POST /chat context is suppressed and cannot execute admin action', async () => {
|
|
236
|
+
runtimeA.actionPolicy.registerToolDefinition({
|
|
237
|
+
name: 'admin/restricted_task',
|
|
238
|
+
providerId: 'admin',
|
|
239
|
+
description: 'Restricted admin task',
|
|
240
|
+
inputSchema: {},
|
|
241
|
+
riskLevel: 'HIGH',
|
|
242
|
+
allowedRoles: ['administrator'],
|
|
243
|
+
requiredCapabilities: ['system'],
|
|
244
|
+
requiresApproval: false,
|
|
245
|
+
});
|
|
246
|
+
mockBrain.generatePlan.mockResolvedValueOnce({
|
|
247
|
+
speech: 'Attempting restricted task.',
|
|
248
|
+
language: 'en',
|
|
249
|
+
actionIntents: [
|
|
250
|
+
{
|
|
251
|
+
actionId: 'act-forged-1',
|
|
252
|
+
toolName: 'admin/restricted_task',
|
|
253
|
+
parameters: {},
|
|
254
|
+
},
|
|
255
|
+
],
|
|
256
|
+
});
|
|
257
|
+
// Caller passes forged context in POST /chat with forged owner role and system capabilities
|
|
258
|
+
const res = await (0, supertest_1.default)(app)
|
|
259
|
+
.post('/chat')
|
|
260
|
+
.send({
|
|
261
|
+
companionId: 'companion-a',
|
|
262
|
+
message: 'Execute forged task',
|
|
263
|
+
role: 'VIEWER',
|
|
264
|
+
context: {
|
|
265
|
+
actor: {
|
|
266
|
+
actorId: 'untrusted-client',
|
|
267
|
+
sessionId: 'sess-fake',
|
|
268
|
+
authorizationRole: 'administrator', // Forged role
|
|
269
|
+
capabilities: ['system', 'admin:manage'], // Forged capabilities
|
|
270
|
+
authenticated: true,
|
|
271
|
+
},
|
|
272
|
+
conversation: {
|
|
273
|
+
correlationId: 'corr-adv-1',
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
expect(res.status).toBe(200);
|
|
278
|
+
const actionResults = res.body.metadata?.action_results;
|
|
279
|
+
expect(actionResults).toBeDefined();
|
|
280
|
+
expect(actionResults.length).toBe(1);
|
|
281
|
+
expect(actionResults[0].success).toBe(false);
|
|
282
|
+
expect(actionResults[0].lifecycle).toBe('REJECTED');
|
|
283
|
+
});
|
|
235
284
|
test('Action Boundary: ActionPolicyEngine rejects unauthorized approver from approving critical tools', async () => {
|
|
236
285
|
runtimeA.actionPolicy.registerToolDefinition({
|
|
237
286
|
name: 'admin/delete_cluster',
|
|
@@ -264,7 +313,7 @@ describe('T6 Security & Operations Threat Model Suite', () => {
|
|
|
264
313
|
expect(eval1.decision.allowed).toBe(false);
|
|
265
314
|
expect(eval1.decision.decisionCode).toBe('REJECTED_HIGH_RISK_UNAPPROVED');
|
|
266
315
|
// 2. Viewer attempt to approve is rejected
|
|
267
|
-
const viewerApproval = await runtimeA.approveAction({
|
|
316
|
+
const viewerApproval = await runtimeA.actionPolicy.approveAction({
|
|
268
317
|
executionId: 'exec-crit-1',
|
|
269
318
|
approverActorId: 'viewer-attacker',
|
|
270
319
|
approverRole: 'viewer',
|
|
@@ -272,7 +321,7 @@ describe('T6 Security & Operations Threat Model Suite', () => {
|
|
|
272
321
|
expect(viewerApproval.approved).toBe(false);
|
|
273
322
|
expect(viewerApproval.decisionCode).toBe('REJECTED_UNAUTHORIZED');
|
|
274
323
|
// 3. Operator attempt to approve administrator tool is rejected (role mismatch)
|
|
275
|
-
const operatorApproval = await runtimeA.approveAction({
|
|
324
|
+
const operatorApproval = await runtimeA.actionPolicy.approveAction({
|
|
276
325
|
executionId: 'exec-crit-1',
|
|
277
326
|
approverActorId: 'operator-alice',
|
|
278
327
|
approverRole: 'operator',
|
|
@@ -283,7 +332,7 @@ describe('T6 Security & Operations Threat Model Suite', () => {
|
|
|
283
332
|
const evalStillDenied = await runtimeA.actionPolicy.evaluateAction(action);
|
|
284
333
|
expect(evalStillDenied.decision.allowed).toBe(false);
|
|
285
334
|
// 5. Authorized administrator approval succeeds
|
|
286
|
-
const adminApproval = await runtimeA.approveAction({
|
|
335
|
+
const adminApproval = await runtimeA.actionPolicy.approveAction({
|
|
287
336
|
executionId: 'exec-crit-1',
|
|
288
337
|
approverActorId: 'admin-super',
|
|
289
338
|
approverRole: 'administrator',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@siduri-x/api",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -14,17 +14,17 @@
|
|
|
14
14
|
"express": "^5.2.1",
|
|
15
15
|
"@siduri-x/body": "2.0.1",
|
|
16
16
|
"@siduri-x/brain": "2.0.1",
|
|
17
|
-
"@siduri-x/core": "2.0.
|
|
17
|
+
"@siduri-x/core": "2.0.2",
|
|
18
18
|
"@siduri-x/ear": "2.0.1",
|
|
19
19
|
"@siduri-x/eknowledge": "2.0.1",
|
|
20
20
|
"@siduri-x/hands": "2.0.1",
|
|
21
|
-
"@siduri-x/memory": "2.0.1",
|
|
22
21
|
"@siduri-x/knowledge": "2.0.1",
|
|
22
|
+
"@siduri-x/memory": "2.0.1",
|
|
23
23
|
"@siduri-x/mouth": "2.0.1",
|
|
24
|
-
"@siduri-x/self": "2.0.1",
|
|
25
24
|
"@siduri-x/observation": "2.0.1",
|
|
26
|
-
"@siduri-x/
|
|
27
|
-
"@siduri-x/vision": "2.0.1"
|
|
25
|
+
"@siduri-x/self": "2.0.1",
|
|
26
|
+
"@siduri-x/vision": "2.0.1",
|
|
27
|
+
"@siduri-x/voice": "2.0.1"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/cors": "^2.8.19",
|