@siduri-x/api 1.0.5 → 2.0.1
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 +4 -0
- package/.turbo/turbo-test.log +40 -0
- package/dist/app.d.ts +1 -1
- package/dist/app.js +82 -15
- package/dist/b0-b6.test.js +1 -1
- package/dist/context-mapper.js +25 -12
- package/dist/context-mapper.test.js +25 -0
- package/dist/index.js +20 -15
- package/dist/t4-gating.test.js +1 -1
- package/dist/t5-experience.test.js +1 -1
- package/dist/t6-security.test.js +66 -3
- package/dist/t7-release.test.js +1 -1
- package/dist/teach-mode.test.d.ts +1 -0
- package/dist/teach-mode.test.js +136 -0
- package/package.json +27 -23
- package/src/app.ts +86 -14
- package/src/b0-b6.test.ts +1 -1
- package/src/context-mapper.test.ts +27 -0
- package/src/context-mapper.ts +28 -12
- package/src/index.ts +21 -16
- package/src/t4-gating.test.ts +1 -1
- package/src/t5-experience.test.ts +1 -1
- package/src/t6-security.test.ts +73 -2
- package/src/t7-release.test.ts +1 -1
- package/src/teach-mode.test.ts +152 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
> @siduri-x/api@2.0.1 test /home/zagin/Projects/vxnus-studio/projects/siduri-x/apps/api
|
|
3
|
+
> jest --config jest.config.json
|
|
4
|
+
|
|
5
|
+
PASS src/context-mapper.test.ts (10.14 s)
|
|
6
|
+
PASS src/runtime.test.ts (10.545 s)
|
|
7
|
+
● Console
|
|
8
|
+
|
|
9
|
+
console.error
|
|
10
|
+
[SiduriRuntime] Knowledge search failed: E-Teyvat is down
|
|
11
|
+
|
|
12
|
+
21 | extKnowledge && shouldQueryKnowledge && typeof extKnowledge.search === 'function'
|
|
13
|
+
22 | ? extKnowledge.search(perceivedText).catch((e) => {
|
|
14
|
+
> 23 | console.error('[SiduriRuntime] Knowledge search failed:', e.message);
|
|
15
|
+
| ^
|
|
16
|
+
24 | subsystemDiagnostics['knowledge'] = `UNAVAILABLE: ${e.message}`;
|
|
17
|
+
25 | return [];
|
|
18
|
+
26 | })
|
|
19
|
+
|
|
20
|
+
at ../../packages/core/dist/context-retriever.js:23:25
|
|
21
|
+
at async Promise.all (index 0)
|
|
22
|
+
at retrieveRuntimeContext (../../packages/core/dist/context-retriever.js:19:78)
|
|
23
|
+
at SiduriRuntime.processPerception (../../packages/core/dist/runtime.js:253:34)
|
|
24
|
+
at Object.<anonymous> (src/runtime.test.ts:74:22)
|
|
25
|
+
|
|
26
|
+
PASS src/index.test.ts (10.888 s)
|
|
27
|
+
PASS src/auth.test.ts
|
|
28
|
+
PASS src/smoke.test.ts
|
|
29
|
+
PASS src/t4-gating.test.ts (11.522 s)
|
|
30
|
+
PASS src/b0-b6.test.ts
|
|
31
|
+
PASS src/t5-experience.test.ts (11.592 s)
|
|
32
|
+
PASS src/t7-release.test.ts (11.335 s)
|
|
33
|
+
PASS src/teach-mode.test.ts
|
|
34
|
+
PASS src/t6-security.test.ts (12.22 s)
|
|
35
|
+
[999D[K
|
|
36
|
+
Test Suites: 11 passed, 11 total
|
|
37
|
+
Tests: 72 passed, 72 total
|
|
38
|
+
Snapshots: 0 total
|
|
39
|
+
Time: 15.203 s, estimated 16 s
|
|
40
|
+
Ran all test suites.
|
package/dist/app.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Express } from 'express';
|
|
2
2
|
import { SiduriRuntime } from './runtime';
|
|
3
3
|
import { VoiceConfig } from '@siduri-x/voice';
|
|
4
|
-
import { EKnowledgeConfig } from '@siduri-x/
|
|
4
|
+
import { EKnowledgeConfig } from '@siduri-x/eknowledge';
|
|
5
5
|
import { OpenRouterVisionConfig } from '@siduri-x/vision';
|
|
6
6
|
import { Live2DAdapterConfig } from '@siduri-x/body';
|
|
7
7
|
import { FixtureObservationOrgan } from '@siduri-x/observation';
|
package/dist/app.js
CHANGED
|
@@ -11,9 +11,9 @@ const runtime_1 = require("./runtime");
|
|
|
11
11
|
const brain_1 = require("@siduri-x/brain");
|
|
12
12
|
const memory_1 = require("@siduri-x/memory");
|
|
13
13
|
const voice_1 = require("@siduri-x/voice");
|
|
14
|
-
const
|
|
14
|
+
const eknowledge_1 = require("@siduri-x/eknowledge");
|
|
15
15
|
const vision_1 = require("@siduri-x/vision");
|
|
16
|
-
const
|
|
16
|
+
const self_1 = require("@siduri-x/self");
|
|
17
17
|
const body_1 = require("@siduri-x/body");
|
|
18
18
|
const hands_1 = require("@siduri-x/hands");
|
|
19
19
|
const ear_1 = require("@siduri-x/ear");
|
|
@@ -52,7 +52,7 @@ function createApp(runtimes = new Map()) {
|
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
function createKnowledge(config) {
|
|
55
|
-
return isDisabled(config) ? undefined : new
|
|
55
|
+
return isDisabled(config) ? undefined : new eknowledge_1.EKnowledgeAdapter(config || {});
|
|
56
56
|
}
|
|
57
57
|
function createVision(config) {
|
|
58
58
|
return isDisabled(config)
|
|
@@ -64,7 +64,7 @@ function createApp(runtimes = new Map()) {
|
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
66
|
function createBehavior(config) {
|
|
67
|
-
return isDisabled(config) ? undefined : new
|
|
67
|
+
return isDisabled(config) ? undefined : new self_1.ActiveSelfCompiler();
|
|
68
68
|
}
|
|
69
69
|
function createBody(config) {
|
|
70
70
|
return isDisabled(config)
|
|
@@ -92,16 +92,18 @@ function createApp(runtimes = new Map()) {
|
|
|
92
92
|
if (runtimes.has(id)) {
|
|
93
93
|
return res.status(400).json({ error: "Already booted" });
|
|
94
94
|
}
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
const
|
|
95
|
+
const organs = config?.organs || {};
|
|
96
|
+
const brain = createBrain(organs.brain || config?.brain);
|
|
97
|
+
const memory = new memory_1.SqliteMemoryStore({ dbPath: process.env.STORAGE_PATH || process.env.SQLITE_DB_PATH || 'siduri.sqlite' });
|
|
98
|
+
const selfRepo = new self_1.SqliteSelfRepository({ dbPath: process.env.STORAGE_PATH || process.env.SQLITE_DB_PATH || 'siduri.sqlite' });
|
|
99
|
+
const voice = createVoice(organs.voice || config?.voice);
|
|
100
|
+
const knowledge = createKnowledge(organs.knowledge || config?.knowledge);
|
|
101
|
+
const vision = createVision(organs.vision || config?.vision);
|
|
102
|
+
const behavior = createBehavior(organs.behavior || config?.behavior);
|
|
103
|
+
const body = createBody(organs.body || config?.body);
|
|
104
|
+
const hands = createHands(organs.hands || config?.hands);
|
|
105
|
+
const ear = createEar(organs.ear || config?.ear);
|
|
106
|
+
const mouth = createMouth(organs.mouth || config?.mouth, voice);
|
|
105
107
|
const runtime = new runtime_1.SiduriRuntime(id, config, {
|
|
106
108
|
brain,
|
|
107
109
|
memory,
|
|
@@ -114,6 +116,8 @@ function createApp(runtimes = new Map()) {
|
|
|
114
116
|
ear,
|
|
115
117
|
mouth,
|
|
116
118
|
observation: observationOrgan,
|
|
119
|
+
self: selfRepo,
|
|
120
|
+
externalKnowledge: knowledge,
|
|
117
121
|
});
|
|
118
122
|
await runtime.initialize();
|
|
119
123
|
runtimes.set(id, runtime);
|
|
@@ -162,6 +166,67 @@ function createApp(runtimes = new Map()) {
|
|
|
162
166
|
});
|
|
163
167
|
});
|
|
164
168
|
app.put('/me', auth_1.requireAuth, (req, res) => res.json({ success: true }));
|
|
169
|
+
// TEACH MODE ENDPOINTS
|
|
170
|
+
app.post('/teach/upload-self', auth_1.requireAuth, async (req, res) => {
|
|
171
|
+
try {
|
|
172
|
+
const { content } = req.body;
|
|
173
|
+
if (!content)
|
|
174
|
+
return res.status(400).json({ error: "Missing content" });
|
|
175
|
+
const parsed = self_1.SelfPackageParser.parse(content);
|
|
176
|
+
res.json(parsed);
|
|
177
|
+
}
|
|
178
|
+
catch (e) {
|
|
179
|
+
res.status(500).json({ error: e.message });
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
app.post('/teach/install-self', auth_1.requireAuth, async (req, res) => {
|
|
183
|
+
try {
|
|
184
|
+
const { companionId, manifest, approvedDirectiveIds } = req.body;
|
|
185
|
+
if (!companionId || !manifest || !Array.isArray(approvedDirectiveIds)) {
|
|
186
|
+
return res.status(400).json({ error: "Missing required fields" });
|
|
187
|
+
}
|
|
188
|
+
if (!manifest.identity || !manifest.identity.name) {
|
|
189
|
+
return res.status(400).json({ error: "Invalid manifest: missing identity.name" });
|
|
190
|
+
}
|
|
191
|
+
const directivesToCommit = manifest.directives?.filter((d) => approvedDirectiveIds.includes(d.id)) || [];
|
|
192
|
+
for (const d of directivesToCommit) {
|
|
193
|
+
if (!d || typeof d.directive !== 'string') {
|
|
194
|
+
return res.status(400).json({ error: "Invalid directive entry: missing directive string" });
|
|
195
|
+
}
|
|
196
|
+
const scan = (0, self_1.scanDirective)(d.directive);
|
|
197
|
+
if (!scan.safe) {
|
|
198
|
+
return res.status(400).json({
|
|
199
|
+
error: `Safety check failed for directive: ${scan.reason}`,
|
|
200
|
+
directiveId: d.id,
|
|
201
|
+
reason: scan.reason,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const repo = new self_1.SqliteSelfRepository({ dbPath: process.env.STORAGE_PATH || process.env.SQLITE_DB_PATH || 'siduri.sqlite' });
|
|
206
|
+
try {
|
|
207
|
+
await repo.setIdentity({
|
|
208
|
+
companionId,
|
|
209
|
+
name: manifest.identity.name,
|
|
210
|
+
archetype: manifest.identity.archetype,
|
|
211
|
+
version: manifest.version || '1.0.0',
|
|
212
|
+
updatedAt: new Date().toISOString(),
|
|
213
|
+
});
|
|
214
|
+
if (manifest.personality) {
|
|
215
|
+
await repo.setPersonality(companionId, manifest.personality);
|
|
216
|
+
}
|
|
217
|
+
if (directivesToCommit.length > 0) {
|
|
218
|
+
await repo.commitDirectives(companionId, directivesToCommit);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
repo.close();
|
|
223
|
+
}
|
|
224
|
+
res.json({ success: true });
|
|
225
|
+
}
|
|
226
|
+
catch (e) {
|
|
227
|
+
res.status(500).json({ error: e.message });
|
|
228
|
+
}
|
|
229
|
+
});
|
|
165
230
|
// CHAT (API context boundary validation)
|
|
166
231
|
app.post('/chat', auth_1.attachIdentity, async (req, res) => {
|
|
167
232
|
const { id, message, history } = req.body;
|
|
@@ -247,7 +312,9 @@ function createApp(runtimes = new Map()) {
|
|
|
247
312
|
if (avatarEvent) {
|
|
248
313
|
res.write(`event: avatar\ndata: ${JSON.stringify(avatarEvent)}\n\n`);
|
|
249
314
|
}
|
|
250
|
-
const
|
|
315
|
+
const maxStreamLen = Number(process.env.SIDURI_MAX_RESPONSE_CHARS || 8000);
|
|
316
|
+
const rawSpeechText = response.delivery?.text || response.response?.subtitle_en || response.response?.spoken_ja || '';
|
|
317
|
+
const speechText = rawSpeechText.slice(0, maxStreamLen);
|
|
251
318
|
const utterance = {
|
|
252
319
|
utteranceId: response.response_id || 'utt-stream',
|
|
253
320
|
companionId,
|
package/dist/b0-b6.test.js
CHANGED
|
@@ -41,7 +41,7 @@ describe('T0 B0 & B6 Runtime Proof Suite', () => {
|
|
|
41
41
|
const config = {
|
|
42
42
|
name: 'NeutralCompanion',
|
|
43
43
|
brain: { provider: 'openrouter' },
|
|
44
|
-
memory: { provider: '
|
|
44
|
+
memory: { provider: 'sqlite' },
|
|
45
45
|
knowledge: { provider: 'e-knowledge' },
|
|
46
46
|
behavior: { provider: 'active-self' },
|
|
47
47
|
voice: { provider: 'none' },
|
package/dist/context-mapper.js
CHANGED
|
@@ -57,15 +57,25 @@ function mapRequestContext(input, options = {}) {
|
|
|
57
57
|
},
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
|
+
// Determine server-enforced authentication status
|
|
61
|
+
const isAuthenticated = input.authenticated !== undefined
|
|
62
|
+
? Boolean(input.authenticated)
|
|
63
|
+
: (actor.authenticated !== undefined ? Boolean(actor.authenticated) : true);
|
|
64
|
+
const safeCapabilities = isAuthenticated
|
|
65
|
+
? (Array.isArray(actor.capabilities) ? actor.capabilities : ['chat'])
|
|
66
|
+
: ['chat'];
|
|
67
|
+
const safeRole = isAuthenticated
|
|
68
|
+
? actor.authorizationRole
|
|
69
|
+
: 'viewer';
|
|
60
70
|
const constructed = {
|
|
61
71
|
companionId,
|
|
62
72
|
actor: {
|
|
73
|
+
...actor,
|
|
63
74
|
actorId: actor.actorId,
|
|
64
75
|
sessionId: actor.sessionId,
|
|
65
|
-
capabilities:
|
|
66
|
-
authenticated:
|
|
67
|
-
authorizationRole:
|
|
68
|
-
...actor,
|
|
76
|
+
capabilities: safeCapabilities,
|
|
77
|
+
authenticated: isAuthenticated,
|
|
78
|
+
authorizationRole: safeRole,
|
|
69
79
|
},
|
|
70
80
|
conversation: {
|
|
71
81
|
correlationId,
|
|
@@ -73,7 +83,7 @@ function mapRequestContext(input, options = {}) {
|
|
|
73
83
|
isLive: rawCtx.conversation?.isLive,
|
|
74
84
|
...rawCtx.conversation,
|
|
75
85
|
},
|
|
76
|
-
source: input.source || rawCtx.source || 'local',
|
|
86
|
+
source: input.source || rawCtx.source || (isAuthenticated ? 'local' : 'external'),
|
|
77
87
|
subject: rawCtx.subject,
|
|
78
88
|
};
|
|
79
89
|
const validated = (0, core_1.validateRequestContext)(constructed);
|
|
@@ -127,25 +137,28 @@ function mapRequestContext(input, options = {}) {
|
|
|
127
137
|
if (!input.actorId && !input.actor?.actorId) {
|
|
128
138
|
diagnostics.push('anonymous_session_generated');
|
|
129
139
|
}
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
? input.
|
|
134
|
-
:
|
|
140
|
+
const isAuthenticated = input.authenticated !== undefined ? Boolean(input.authenticated) : true;
|
|
141
|
+
const capabilities = isAuthenticated
|
|
142
|
+
? (Array.isArray(input.capabilities)
|
|
143
|
+
? input.capabilities
|
|
144
|
+
: Array.isArray(input.actor?.capabilities)
|
|
145
|
+
? input.actor.capabilities
|
|
146
|
+
: ['chat', 'system'])
|
|
147
|
+
: ['chat'];
|
|
135
148
|
const mappedContext = {
|
|
136
149
|
companionId,
|
|
137
150
|
actor: {
|
|
138
151
|
actorId,
|
|
139
152
|
sessionId,
|
|
140
153
|
capabilities,
|
|
141
|
-
authenticated:
|
|
154
|
+
authenticated: isAuthenticated,
|
|
142
155
|
authorizationRole: input.role ? (input.role.toLowerCase() === 'viewer' ? 'viewer' : 'administrator') : undefined,
|
|
143
156
|
},
|
|
144
157
|
conversation: {
|
|
145
158
|
channel: input.channel || input.conversation?.channel || 'direct',
|
|
146
159
|
correlationId,
|
|
147
160
|
},
|
|
148
|
-
source: input.source || 'local',
|
|
161
|
+
source: input.source || (isAuthenticated ? 'local' : 'external'),
|
|
149
162
|
subject: input.subject,
|
|
150
163
|
};
|
|
151
164
|
const validated = (0, core_1.validateRequestContext)(mappedContext);
|
|
@@ -133,4 +133,29 @@ describe('API Request Context Mapper (Single-Owner, Single-Machine)', () => {
|
|
|
133
133
|
expect(result.accepted).toBe(true);
|
|
134
134
|
expect(result.context?.conversation.correlationId).toMatch(/^corr-/);
|
|
135
135
|
});
|
|
136
|
+
test('Overrides forged client actor authentication and caps capabilities on unauthenticated requests', () => {
|
|
137
|
+
const forgedInput = {
|
|
138
|
+
companionId: 'companion-a',
|
|
139
|
+
authenticated: false, // Server detected unauthenticated request
|
|
140
|
+
source: 'external',
|
|
141
|
+
context: {
|
|
142
|
+
actor: {
|
|
143
|
+
actorId: 'attacker',
|
|
144
|
+
sessionId: 'session-att',
|
|
145
|
+
authenticated: true, // Forged
|
|
146
|
+
capabilities: ['system:exec', 'bash', 'chat'], // Forged elevated capabilities
|
|
147
|
+
authorizationRole: 'OWNER', // Forged role
|
|
148
|
+
},
|
|
149
|
+
conversation: {
|
|
150
|
+
correlationId: 'corr-forged-1',
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
const result = (0, context_mapper_1.mapRequestContext)(forgedInput);
|
|
155
|
+
expect(result.accepted).toBe(true);
|
|
156
|
+
expect(result.context?.actor.authenticated).toBe(false);
|
|
157
|
+
expect(result.context?.actor.capabilities).toEqual(['chat']);
|
|
158
|
+
expect(result.context?.actor.authorizationRole).toBe('viewer');
|
|
159
|
+
expect(result.context?.source).toBe('external');
|
|
160
|
+
});
|
|
136
161
|
});
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
20
|
exports.app = exports.createApp = void 0;
|
|
21
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
22
|
+
dotenv_1.default.config();
|
|
21
23
|
const promises_1 = require("node:fs/promises");
|
|
22
24
|
const node_path_1 = __importDefault(require("node:path"));
|
|
23
25
|
const app_1 = require("./app");
|
|
@@ -26,9 +28,9 @@ const runtime_1 = require("./runtime");
|
|
|
26
28
|
const brain_1 = require("@siduri-x/brain");
|
|
27
29
|
const memory_1 = require("@siduri-x/memory");
|
|
28
30
|
const voice_1 = require("@siduri-x/voice");
|
|
29
|
-
const
|
|
31
|
+
const eknowledge_1 = require("@siduri-x/eknowledge");
|
|
30
32
|
const vision_1 = require("@siduri-x/vision");
|
|
31
|
-
const
|
|
33
|
+
const self_1 = require("@siduri-x/self");
|
|
32
34
|
const body_1 = require("@siduri-x/body");
|
|
33
35
|
const observation_1 = require("@siduri-x/observation");
|
|
34
36
|
__exportStar(require("./context-mapper"), exports);
|
|
@@ -68,7 +70,7 @@ function createKnowledge(config) {
|
|
|
68
70
|
if (!config?.packPath && !config?.registryUrl && !config?.baseUrl) {
|
|
69
71
|
return undefined;
|
|
70
72
|
}
|
|
71
|
-
return new
|
|
73
|
+
return new eknowledge_1.EKnowledgeAdapter(config || {});
|
|
72
74
|
}
|
|
73
75
|
function createVision(config) {
|
|
74
76
|
return isDisabled(config)
|
|
@@ -80,7 +82,7 @@ function createVision(config) {
|
|
|
80
82
|
});
|
|
81
83
|
}
|
|
82
84
|
function createBehavior(config) {
|
|
83
|
-
return isDisabled(config) ? undefined : new
|
|
85
|
+
return isDisabled(config) ? undefined : new self_1.ActiveSelfCompiler();
|
|
84
86
|
}
|
|
85
87
|
function createBody(config) {
|
|
86
88
|
return isDisabled(config)
|
|
@@ -90,15 +92,7 @@ function createBody(config) {
|
|
|
90
92
|
function createMemory(config) {
|
|
91
93
|
if (isDisabled(config))
|
|
92
94
|
return undefined;
|
|
93
|
-
|
|
94
|
-
if (provider === 'in-memory') {
|
|
95
|
-
return new memory_1.InMemoryMemoryOrgan();
|
|
96
|
-
}
|
|
97
|
-
if (provider === 'postgres') {
|
|
98
|
-
const connectionString = config?.connectionString || process.env.DATABASE_URL || 'postgresql://postgres:postgres@localhost:5432/siduri';
|
|
99
|
-
return new memory_1.PostgresMemoryOrgan({ connectionString, maxConnections: config?.maxConnections });
|
|
100
|
-
}
|
|
101
|
-
return undefined;
|
|
95
|
+
return new memory_1.SqliteMemoryStore({ dbPath: config?.dbPath || process.env.STORAGE_PATH || process.env.SQLITE_DB_PATH || 'siduri.sqlite' });
|
|
102
96
|
}
|
|
103
97
|
const PORT = process.env.PORT || 3001;
|
|
104
98
|
const defaultCompanionConfig = {
|
|
@@ -106,7 +100,7 @@ const defaultCompanionConfig = {
|
|
|
106
100
|
name: 'Siduri',
|
|
107
101
|
brain: { provider: 'openrouter', model: 'gpt-4o-mini' },
|
|
108
102
|
voice: { provider: 'voicevox', speakerId: 1 },
|
|
109
|
-
memory: { provider: '
|
|
103
|
+
memory: { provider: 'sqlite' },
|
|
110
104
|
knowledge: {
|
|
111
105
|
provider: process.env.SIDURI_KNOWLEDGE_PROVIDER || 'e-knowledge',
|
|
112
106
|
packPath: process.env.SIDURI_KNOWLEDGE_PACK || '',
|
|
@@ -169,12 +163,23 @@ async function bootDefaultCompanion() {
|
|
|
169
163
|
const vision = createVision(config.vision);
|
|
170
164
|
const observation = new observation_1.FixtureObservationOrgan(vision ?? { analyze: async () => JSON.stringify({ readings: [] }) });
|
|
171
165
|
instance.setObservationOrgan(observation);
|
|
166
|
+
const selfRepo = new self_1.SqliteSelfRepository({ dbPath: process.env.STORAGE_PATH || process.env.SQLITE_DB_PATH || 'siduri.sqlite' });
|
|
172
167
|
const behavior = createBehavior(config.behavior);
|
|
173
168
|
const body = createBody(config.body);
|
|
174
169
|
if (memory && typeof memory.runMigrations === 'function') {
|
|
175
170
|
await memory.runMigrations().catch((e) => console.warn("Migrations warning:", e.message));
|
|
176
171
|
}
|
|
177
|
-
const runtime = new runtime_1.SiduriRuntime('default', config, {
|
|
172
|
+
const runtime = new runtime_1.SiduriRuntime('default', config, {
|
|
173
|
+
brain,
|
|
174
|
+
memory,
|
|
175
|
+
voice,
|
|
176
|
+
knowledge,
|
|
177
|
+
vision,
|
|
178
|
+
behavior,
|
|
179
|
+
body,
|
|
180
|
+
self: selfRepo,
|
|
181
|
+
externalKnowledge: knowledge
|
|
182
|
+
});
|
|
178
183
|
await runtime.initialize();
|
|
179
184
|
runtimes.set('default', runtime);
|
|
180
185
|
console.log("Default companion booted successfully.");
|
package/dist/t4-gating.test.js
CHANGED
|
@@ -54,7 +54,7 @@ describe('T4 Response Gating and Staged Approval Integration Suite', () => {
|
|
|
54
54
|
const config = {
|
|
55
55
|
name: 'NeutralCompanion',
|
|
56
56
|
brain: { provider: 'openrouter' },
|
|
57
|
-
memory: { provider: '
|
|
57
|
+
memory: { provider: 'sqlite' },
|
|
58
58
|
knowledge: { provider: 'e-knowledge' },
|
|
59
59
|
behavior: { provider: 'active-self' },
|
|
60
60
|
voice: { provider: 'voicevox' },
|
|
@@ -60,7 +60,7 @@ describe('T5 Experience Event and Output Adapters Suite', () => {
|
|
|
60
60
|
const config = {
|
|
61
61
|
name: 'NeutralCompanion',
|
|
62
62
|
brain: { provider: 'openrouter' },
|
|
63
|
-
memory: { provider: '
|
|
63
|
+
memory: { provider: 'sqlite' },
|
|
64
64
|
knowledge: { provider: 'e-knowledge' },
|
|
65
65
|
behavior: { provider: 'active-self' },
|
|
66
66
|
voice: { provider: 'voicevox' },
|
package/dist/t6-security.test.js
CHANGED
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const supertest_1 = __importDefault(require("supertest"));
|
|
7
7
|
const app_1 = require("./app");
|
|
8
8
|
const runtime_1 = require("./runtime");
|
|
9
|
-
const
|
|
9
|
+
const self_1 = require("@siduri-x/self");
|
|
10
10
|
describe('T6 Security & Operations Threat Model Suite', () => {
|
|
11
11
|
let mockBrain;
|
|
12
12
|
let mockMemory;
|
|
@@ -34,7 +34,7 @@ describe('T6 Security & Operations Threat Model Suite', () => {
|
|
|
34
34
|
rejectClaim: jest.fn().mockResolvedValue(undefined),
|
|
35
35
|
};
|
|
36
36
|
mockKnowledge = { search: jest.fn().mockResolvedValue([]) };
|
|
37
|
-
const behaviorCompiler = new
|
|
37
|
+
const behaviorCompiler = new self_1.ActiveSelfCompiler();
|
|
38
38
|
mockBehavior = {
|
|
39
39
|
compile: jest.fn().mockImplementation(async (ctx) => behaviorCompiler.compile(ctx)),
|
|
40
40
|
};
|
|
@@ -49,7 +49,7 @@ describe('T6 Security & Operations Threat Model Suite', () => {
|
|
|
49
49
|
const config = {
|
|
50
50
|
name: 'CompanionSec',
|
|
51
51
|
brain: { provider: 'openrouter' },
|
|
52
|
-
memory: { provider: '
|
|
52
|
+
memory: { provider: 'sqlite' },
|
|
53
53
|
knowledge: { provider: 'none' },
|
|
54
54
|
behavior: { provider: 'active-self' },
|
|
55
55
|
voice: { provider: 'voicevox' },
|
|
@@ -232,6 +232,69 @@ 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('Action Boundary: ActionPolicyEngine rejects unauthorized approver from approving critical tools', async () => {
|
|
236
|
+
runtimeA.actionPolicy.registerToolDefinition({
|
|
237
|
+
name: 'admin/delete_cluster',
|
|
238
|
+
providerId: 'admin',
|
|
239
|
+
description: 'Delete cluster',
|
|
240
|
+
inputSchema: {},
|
|
241
|
+
riskLevel: 'CRITICAL',
|
|
242
|
+
allowedRoles: ['administrator'],
|
|
243
|
+
requiresApproval: true,
|
|
244
|
+
});
|
|
245
|
+
const action = {
|
|
246
|
+
actionId: 'act-crit-1',
|
|
247
|
+
toolName: 'admin/delete_cluster',
|
|
248
|
+
parameters: {},
|
|
249
|
+
context: {
|
|
250
|
+
companionId: 'companion-a',
|
|
251
|
+
actor: {
|
|
252
|
+
actorId: 'admin-user',
|
|
253
|
+
sessionId: 'sess-1',
|
|
254
|
+
authorizationRole: 'administrator',
|
|
255
|
+
capabilities: ['admin:delete'],
|
|
256
|
+
authenticated: true,
|
|
257
|
+
},
|
|
258
|
+
conversation: { channel: 'direct', correlationId: 'corr-1' },
|
|
259
|
+
},
|
|
260
|
+
executionId: 'exec-crit-1',
|
|
261
|
+
};
|
|
262
|
+
// 1. Unapproved action evaluation fails
|
|
263
|
+
const eval1 = await runtimeA.actionPolicy.evaluateAction(action);
|
|
264
|
+
expect(eval1.decision.allowed).toBe(false);
|
|
265
|
+
expect(eval1.decision.decisionCode).toBe('REJECTED_HIGH_RISK_UNAPPROVED');
|
|
266
|
+
// 2. Viewer attempt to approve is rejected
|
|
267
|
+
const viewerApproval = await runtimeA.approveAction({
|
|
268
|
+
executionId: 'exec-crit-1',
|
|
269
|
+
approverActorId: 'viewer-attacker',
|
|
270
|
+
approverRole: 'viewer',
|
|
271
|
+
});
|
|
272
|
+
expect(viewerApproval.approved).toBe(false);
|
|
273
|
+
expect(viewerApproval.decisionCode).toBe('REJECTED_UNAUTHORIZED');
|
|
274
|
+
// 3. Operator attempt to approve administrator tool is rejected (role mismatch)
|
|
275
|
+
const operatorApproval = await runtimeA.approveAction({
|
|
276
|
+
executionId: 'exec-crit-1',
|
|
277
|
+
approverActorId: 'operator-alice',
|
|
278
|
+
approverRole: 'operator',
|
|
279
|
+
});
|
|
280
|
+
expect(operatorApproval.approved).toBe(false);
|
|
281
|
+
expect(operatorApproval.decisionCode).toBe('REJECTED_ROLE_MISMATCH');
|
|
282
|
+
// 4. Action evaluation remains denied
|
|
283
|
+
const evalStillDenied = await runtimeA.actionPolicy.evaluateAction(action);
|
|
284
|
+
expect(evalStillDenied.decision.allowed).toBe(false);
|
|
285
|
+
// 5. Authorized administrator approval succeeds
|
|
286
|
+
const adminApproval = await runtimeA.approveAction({
|
|
287
|
+
executionId: 'exec-crit-1',
|
|
288
|
+
approverActorId: 'admin-super',
|
|
289
|
+
approverRole: 'administrator',
|
|
290
|
+
});
|
|
291
|
+
expect(adminApproval.approved).toBe(true);
|
|
292
|
+
expect(adminApproval.decisionCode).toBe('APPROVED');
|
|
293
|
+
// 6. Action evaluation now succeeds and issues capability
|
|
294
|
+
const evalAllowed = await runtimeA.actionPolicy.evaluateAction(action);
|
|
295
|
+
expect(evalAllowed.decision.allowed).toBe(true);
|
|
296
|
+
expect(evalAllowed.capability).toBeDefined();
|
|
297
|
+
});
|
|
235
298
|
test('Adversarial Boundary: Hostile prompt directive in Behavior is quarantined and does not execute tools', async () => {
|
|
236
299
|
// Unsafe directive in memory
|
|
237
300
|
mockMemory.getDirectives.mockResolvedValueOnce([
|
package/dist/t7-release.test.js
CHANGED
|
@@ -54,7 +54,7 @@ describe('T7 Release Readiness End-to-End Verification Suite', () => {
|
|
|
54
54
|
const config = {
|
|
55
55
|
name: 'NeutralCompanion',
|
|
56
56
|
brain: { provider: 'openrouter' },
|
|
57
|
-
memory: { provider: '
|
|
57
|
+
memory: { provider: 'sqlite' },
|
|
58
58
|
knowledge: { provider: 'e-knowledge' },
|
|
59
59
|
behavior: { provider: 'active-self' },
|
|
60
60
|
voice: { provider: 'voicevox' },
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|