@siduri-x/api 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/dist/app.js DELETED
@@ -1,436 +0,0 @@
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
- exports.createApp = createApp;
7
- const express_1 = __importDefault(require("express"));
8
- const cors_1 = __importDefault(require("cors"));
9
- const runtime_1 = require("./runtime");
10
- const brain_1 = require("@siduri-x/brain");
11
- const memory_1 = require("@siduri-x/memory");
12
- const voice_1 = require("@siduri-x/voice");
13
- const knowledge_1 = require("@siduri-x/knowledge");
14
- const vision_1 = require("@siduri-x/vision");
15
- const behavior_1 = require("@siduri-x/behavior");
16
- const body_1 = require("@siduri-x/body");
17
- const hands_1 = require("@siduri-x/hands");
18
- const ear_1 = require("@siduri-x/ear");
19
- const auth_1 = require("./auth");
20
- const context_mapper_1 = require("./context-mapper");
21
- function createApp(runtimes = new Map()) {
22
- const app = (0, express_1.default)();
23
- app.use((0, cors_1.default)());
24
- app.use(express_1.default.json());
25
- let observationOrgan;
26
- function createBrain(config) {
27
- const provider = config.provider || 'openrouter';
28
- const defaultKeyEnv = provider === 'openai-compatible' ? 'OPENAI_COMPATIBLE_API_KEY' : 'OPENROUTER_API_KEY';
29
- const apiKey = config.apiKey || process.env[config.apiKeyEnv || defaultKeyEnv] || '';
30
- if (provider === 'openai-compatible') {
31
- return new brain_1.OpenAICompatibleBrain({
32
- apiKey,
33
- model: config.model || 'local-model',
34
- baseUrl: config.baseUrl || 'http://127.0.0.1:1234/v1',
35
- });
36
- }
37
- return new brain_1.OpenRouterBrain({ apiKey, model: config.model || 'gpt-4o-mini' });
38
- }
39
- function isDisabled(config) {
40
- return !config || config.provider === 'none';
41
- }
42
- function createVoice(config) {
43
- return isDisabled(config)
44
- ? undefined
45
- : new voice_1.VoicevoxAdapter({ baseUrl: process.env.VOICEVOX_URL || 'http://localhost:50021', speakerId: config.speakerId || 1 });
46
- }
47
- function createKnowledge(config) {
48
- return isDisabled(config) ? undefined : new knowledge_1.EKnowledgeAdapter(config);
49
- }
50
- function createVision(config) {
51
- return isDisabled(config)
52
- ? undefined
53
- : new vision_1.OpenRouterVisionAdapter({ apiKey: process.env.OPENROUTER_API_KEY || '', model: config.model || 'gpt-4-vision' });
54
- }
55
- function createBehavior(config) {
56
- return isDisabled(config) ? undefined : new behavior_1.ActiveSelfCompiler();
57
- }
58
- function createBody(config) {
59
- return isDisabled(config)
60
- ? undefined
61
- : new body_1.Live2DAdapter(config);
62
- }
63
- function createHands(config) {
64
- return isDisabled(config)
65
- ? new hands_1.DefaultHandsOrgan()
66
- : new hands_1.DefaultHandsOrgan(config);
67
- }
68
- function createEar(config) {
69
- return isDisabled(config)
70
- ? new ear_1.DefaultEarOrgan()
71
- : new ear_1.DefaultEarOrgan(config);
72
- }
73
- app.post('/boot', (0, auth_1.requireRole)(['OWNER']), async (req, res) => {
74
- try {
75
- const { id, config } = req.body;
76
- if (runtimes.has(id)) {
77
- return res.status(400).json({ error: "Already booted" });
78
- }
79
- const brain = createBrain(config.brain);
80
- const memory = new memory_1.PostgresMemoryOrgan({ connectionString: process.env.DATABASE_URL || 'postgresql://postgres:postgres@localhost:5432/siduri' });
81
- const voice = createVoice(config.voice);
82
- const knowledge = createKnowledge(config.knowledge);
83
- const vision = createVision(config.vision);
84
- const behavior = createBehavior(config.behavior);
85
- const body = createBody(config.body);
86
- const hands = createHands(config.hands);
87
- const ear = createEar(config.ear);
88
- const runtime = new runtime_1.SiduriRuntime(id, config, { brain, memory, voice, knowledge, vision, behavior, body, hands, ear });
89
- await runtime.initialize();
90
- runtimes.set(id, runtime);
91
- res.json({ success: true, id });
92
- }
93
- catch (e) {
94
- res.status(500).json({ error: e.message });
95
- }
96
- });
97
- // STATUS / HEALTH ENDPOINTS
98
- app.get('/health', (req, res) => res.json({ status: "ok" }));
99
- app.get('/version', (req, res) => res.json({ name: "siduri-y-api", version: "0.2.0-y" }));
100
- app.get('/ready', (req, res) => res.json({ status: "ready", dependencies: {} }));
101
- app.get('/voice/health', (req, res) => res.json({ provider: "voicevox", healthy: true }));
102
- app.get('/obs/health', (req, res) => res.json({ connected: true }));
103
- app.get('/platforms/status', (req, res) => res.json({ platforms: {} }));
104
- app.get('/me', auth_1.attachIdentity, (req, res) => {
105
- const identity = req.identity;
106
- res.json({
107
- actorId: identity.role === 'OWNER' ? 'owner-user' : 'anonymous-session',
108
- role: identity.role,
109
- authenticated: identity.role === 'OWNER',
110
- });
111
- });
112
- app.put('/me', (0, auth_1.requireRole)(['OWNER']), (req, res) => res.json({ success: true }));
113
- // CHAT (API context boundary validation)
114
- app.post('/chat', auth_1.attachIdentity, async (req, res) => {
115
- const { id, message, history } = req.body;
116
- const identity = req.identity;
117
- // Call context mapper at the API boundary
118
- const mappingResult = (0, context_mapper_1.mapRequestContext)({
119
- ...req.body,
120
- id: id || req.body.companionId,
121
- role: req.body.role || identity?.role,
122
- generateCorrelationId: true,
123
- }, {
124
- endpointPolicy: 'public',
125
- defaultPublicAudience: 'audience-public',
126
- });
127
- if (!mappingResult.accepted) {
128
- return res.status(400).json({
129
- accepted: false,
130
- error: mappingResult.error,
131
- });
132
- }
133
- const companionId = mappingResult.context.companionId;
134
- const runtime = runtimes.get(companionId);
135
- if (!runtime)
136
- return res.status(404).json({ error: "Companion not found" });
137
- try {
138
- // Map authorization role to legacy memory scope for backwards-compatible runtime call
139
- const legacyScope = mappingResult.context.actor.authorizationRole === 'administrator'
140
- ? 'OWNER'
141
- : mappingResult.context.actor.authorizationRole === 'operator'
142
- ? 'OPERATOR'
143
- : 'VIEWER';
144
- const response = await runtime.handleUserMessage(message, legacyScope, history);
145
- res.json(response);
146
- }
147
- catch (e) {
148
- res.status(500).json({ error: e.message });
149
- }
150
- });
151
- // MEMORY GETTERS
152
- app.get('/memory/proposals', (0, auth_1.requireRole)(['OWNER', 'OPERATOR']), async (req, res) => {
153
- const id = req.query.id || Array.from(runtimes.keys())[0];
154
- const runtime = runtimes.get(id);
155
- if (!runtime)
156
- return res.status(404).json({ error: "Companion not found" });
157
- if (!runtime.memory)
158
- return res.json({ proposals: [] });
159
- try {
160
- const proposals = await runtime.memory.getPendingClaims();
161
- res.json({ proposals });
162
- }
163
- catch (e) {
164
- res.status(500).json({ error: e.message });
165
- }
166
- });
167
- app.get('/memory', (0, auth_1.requireRole)(['OWNER', 'OPERATOR']), async (req, res) => {
168
- const id = req.query.id || Array.from(runtimes.keys())[0];
169
- const runtime = runtimes.get(id);
170
- if (!runtime)
171
- return res.status(404).json({ error: "Companion not found" });
172
- if (!runtime.memory)
173
- return res.json({ items: [] });
174
- try {
175
- const items = await runtime.memory.getClaims();
176
- res.json({ items });
177
- }
178
- catch (e) {
179
- res.status(500).json({ error: e.message });
180
- }
181
- });
182
- app.get('/memory/claims', (0, auth_1.requireRole)(['OWNER', 'OPERATOR']), async (req, res) => {
183
- const id = req.query.id || Array.from(runtimes.keys())[0];
184
- const runtime = runtimes.get(id);
185
- if (!runtime)
186
- return res.status(404).json({ error: "Companion not found" });
187
- if (!runtime.memory)
188
- return res.json({ claims: [] });
189
- try {
190
- const claims = await runtime.memory.getClaims();
191
- res.json({ claims });
192
- }
193
- catch (e) {
194
- res.status(500).json({ error: e.message });
195
- }
196
- });
197
- app.get('/memory/behavioral', (0, auth_1.requireRole)(['OWNER', 'OPERATOR']), async (req, res) => {
198
- const id = req.query.id || Array.from(runtimes.keys())[0];
199
- const runtime = runtimes.get(id);
200
- if (!runtime)
201
- return res.status(404).json({ error: "Companion not found" });
202
- if (!runtime.memory)
203
- return res.json({ directives: [] });
204
- try {
205
- const directives = await runtime.memory.getDirectives();
206
- res.json({ directives });
207
- }
208
- catch (e) {
209
- res.status(500).json({ error: e.message });
210
- }
211
- });
212
- // MEMORY MUTATIONS - PROPOSALS
213
- app.post('/memory/proposals/update', (0, auth_1.requireRole)(['OWNER', 'OPERATOR']), async (req, res) => res.json({ success: true }));
214
- app.post('/memory/proposals/approve', (0, auth_1.requireRole)(['OWNER', 'OPERATOR']), async (req, res) => {
215
- const id = req.body.companionId || Array.from(runtimes.keys())[0];
216
- const runtime = runtimes.get(id);
217
- if (!runtime)
218
- return res.status(404).json({ error: "Companion not found" });
219
- if (!runtime.memory)
220
- return res.status(400).json({ error: "Memory organ not configured" });
221
- try {
222
- await runtime.memory.approveClaim(req.body.id);
223
- res.json({ approved: true });
224
- }
225
- catch (e) {
226
- res.status(500).json({ error: e.message });
227
- }
228
- });
229
- app.post('/memory/proposals/reject', (0, auth_1.requireRole)(['OWNER', 'OPERATOR']), async (req, res) => {
230
- const id = req.body.companionId || Array.from(runtimes.keys())[0];
231
- const runtime = runtimes.get(id);
232
- if (!runtime)
233
- return res.status(404).json({ error: "Companion not found" });
234
- if (!runtime.memory)
235
- return res.status(400).json({ error: "Memory organ not configured" });
236
- try {
237
- await runtime.memory.rejectClaim(req.body.id);
238
- res.json({ rejected: true });
239
- }
240
- catch (e) {
241
- res.status(500).json({ error: e.message });
242
- }
243
- });
244
- // MEMORY MUTATIONS - BEHAVIORAL
245
- app.post('/memory/behavioral/approve', (0, auth_1.requireRole)(['OWNER']), async (req, res) => {
246
- const id = req.body.companionId || Array.from(runtimes.keys())[0];
247
- const runtime = runtimes.get(id);
248
- if (!runtime)
249
- return res.status(404).json({ error: "Companion not found" });
250
- if (!runtime.memory)
251
- return res.status(400).json({ error: "Memory organ not configured" });
252
- try {
253
- await runtime.memory.approveDirective(req.body.id);
254
- res.json({ approved: true });
255
- }
256
- catch (e) {
257
- res.status(500).json({ error: e.message });
258
- }
259
- });
260
- app.post('/memory/behavioral/reject', (0, auth_1.requireRole)(['OWNER']), async (req, res) => {
261
- const id = req.body.companionId || Array.from(runtimes.keys())[0];
262
- const runtime = runtimes.get(id);
263
- if (!runtime)
264
- return res.status(404).json({ error: "Companion not found" });
265
- if (!runtime.memory)
266
- return res.status(400).json({ error: "Memory organ not configured" });
267
- try {
268
- await runtime.memory.rejectDirective(req.body.id);
269
- res.json({ rejected: true });
270
- }
271
- catch (e) {
272
- res.status(500).json({ error: e.message });
273
- }
274
- });
275
- app.post('/memory/behavioral/revoke', (0, auth_1.requireRole)(['OWNER']), async (req, res) => {
276
- const id = req.body.companionId || Array.from(runtimes.keys())[0];
277
- const runtime = runtimes.get(id);
278
- if (!runtime)
279
- return res.status(404).json({ error: "Companion not found" });
280
- if (!runtime.memory)
281
- return res.status(400).json({ error: "Memory organ not configured" });
282
- try {
283
- await runtime.memory.revokeDirective(req.body.id);
284
- res.json({ revoked: true });
285
- }
286
- catch (e) {
287
- res.status(500).json({ error: e.message });
288
- }
289
- });
290
- app.post('/memory/behavioral/disable', (0, auth_1.requireRole)(['OWNER']), async (req, res) => {
291
- const id = req.body.companionId || Array.from(runtimes.keys())[0];
292
- const runtime = runtimes.get(id);
293
- if (!runtime)
294
- return res.status(404).json({ error: "Companion not found" });
295
- if (!runtime.memory)
296
- return res.status(400).json({ error: "Memory organ not configured" });
297
- try {
298
- await runtime.memory.disableDirective(req.body.id);
299
- res.json({ disabled: true });
300
- }
301
- catch (e) {
302
- res.status(500).json({ error: e.message });
303
- }
304
- });
305
- app.post('/dev/memory/reset', (0, auth_1.requireRole)(['OWNER']), async (req, res) => res.json({ reset: true }));
306
- // MOCKS / DEV / EVIDENCE / PLATFORMS
307
- app.get('/platforms/events', (req, res) => res.json({ events: [] }));
308
- app.get('/platforms/actions', (req, res) => res.json({ actions: [] }));
309
- app.get('/evidence', (req, res) => res.json({ results: [] }));
310
- app.get('/observations', (req, res) => res.json({ observations: observationOrgan?.current() ?? [] }));
311
- app.post('/dev/mock-response', async (req, res) => {
312
- const companionId = req.body?.companionId || Array.from(runtimes.keys())[0] || 'default';
313
- const runtime = runtimes.get(companionId);
314
- if (!runtime)
315
- return res.status(404).json({ accepted: false, error: 'Companion not found' });
316
- const staged = runtime.gating.stageResponse({
317
- requestContext: {
318
- companionId,
319
- actor: {
320
- actorId: 'operator-a',
321
- sessionId: 'sess-op',
322
- authorizationRole: 'operator',
323
- capabilities: ['chat:public', 'memory:approve'],
324
- authenticated: true,
325
- },
326
- conversation: {
327
- channel: 'public',
328
- audienceId: 'audience-public',
329
- correlationId: req.body?.correlation_id || `corr-${Date.now()}`,
330
- },
331
- },
332
- candidateSpeech: req.body?.speech || 'Mocked staged response for review',
333
- candidateLanguage: req.body?.language || 'en',
334
- requiresApproval: req.body?.requiresApproval ?? true,
335
- });
336
- res.json({
337
- accepted: true,
338
- staged: true,
339
- status: staged.status,
340
- response_id: staged.responseId,
341
- correlation_id: staged.correlationId,
342
- speech: staged.speech,
343
- });
344
- });
345
- app.post('/dev/approve-response', async (req, res) => {
346
- const companionId = req.body?.companionId || Array.from(runtimes.keys())[0] || 'default';
347
- const runtime = runtimes.get(companionId);
348
- if (!runtime)
349
- return res.status(404).json({ approved: false, error: 'Companion not found' });
350
- let responseId = req.body?.responseId;
351
- let correlationId = req.body?.correlation_id;
352
- if (!responseId && correlationId) {
353
- const found = runtime.gating.findStagedPlanByCorrelation(companionId, correlationId);
354
- if (found) {
355
- responseId = found.responseId;
356
- }
357
- }
358
- else if (responseId && !correlationId) {
359
- const found = runtime.gating.getStagedPlan(responseId);
360
- if (found) {
361
- correlationId = found.correlationId;
362
- }
363
- }
364
- if (!responseId) {
365
- return res.status(400).json({ approved: false, error: 'UNKNOWN_APPROVAL_ID' });
366
- }
367
- const result = runtime.gating.approveResponse({
368
- responseId,
369
- companionId,
370
- correlationId: correlationId || '',
371
- audienceId: req.body?.audienceId,
372
- });
373
- if (!result.success) {
374
- return res.status(400).json({ approved: false, error: result.reason });
375
- }
376
- // Now evaluated as approved
377
- const evaluation = runtime.gating.evaluateGate(result.plan);
378
- res.json({
379
- approved: true,
380
- status: evaluation.disposition,
381
- response_id: result.plan.responseId,
382
- speech: result.plan.speech,
383
- language: result.plan.language,
384
- });
385
- });
386
- app.post('/dev/reject-response', async (req, res) => {
387
- const companionId = req.body?.companionId || Array.from(runtimes.keys())[0] || 'default';
388
- const runtime = runtimes.get(companionId);
389
- if (!runtime)
390
- return res.status(404).json({ rejected: false, error: 'Companion not found' });
391
- let responseId = req.body?.responseId;
392
- let correlationId = req.body?.correlation_id;
393
- if (!responseId && correlationId) {
394
- const found = runtime.gating.findStagedPlanByCorrelation(companionId, correlationId);
395
- if (found) {
396
- responseId = found.responseId;
397
- }
398
- }
399
- else if (responseId && !correlationId) {
400
- const found = runtime.gating.getStagedPlan(responseId);
401
- if (found) {
402
- correlationId = found.correlationId;
403
- }
404
- }
405
- if (!responseId) {
406
- return res.status(400).json({ rejected: false, error: 'UNKNOWN_APPROVAL_ID' });
407
- }
408
- const result = runtime.gating.rejectResponse({
409
- responseId,
410
- companionId,
411
- correlationId: correlationId || '',
412
- reason: req.body?.reason,
413
- });
414
- if (!result.success) {
415
- return res.status(400).json({ rejected: false, error: result.reason });
416
- }
417
- res.json({
418
- rejected: true,
419
- status: result.plan.status,
420
- response_id: result.plan.responseId,
421
- });
422
- });
423
- app.post('/dev/mock-observation', async (req, res) => {
424
- if (!observationOrgan)
425
- return res.status(503).json({ accepted: false, reason: 'observation_unavailable' });
426
- const result = await observationOrgan.ingest(new Uint8Array([115, 121, 110, 116, 104, 101, 116, 105, 99]), 'fixture-observation', 'configured-vision');
427
- if (!result.observation)
428
- return res.status(result.duplicate ? 200 : 409).json({ accepted: false, ...result });
429
- res.status(202).json({ accepted: true, observation: result.observation });
430
- });
431
- app.post('/platforms/actions/suggest', (req, res) => res.json({ suggested: true }));
432
- app.post('/platforms/actions/approve', (req, res) => res.json({ approved: true }));
433
- app.post('/platforms/actions/reject', (req, res) => res.json({ rejected: true }));
434
- app.post('/platforms/actions/send', (req, res) => res.json({ sent: true }));
435
- return { app, runtimes, setObservationOrgan: (org) => { observationOrgan = org; } };
436
- }
package/dist/auth.d.ts DELETED
@@ -1,8 +0,0 @@
1
- import { Request, Response, NextFunction } from 'express';
2
- export type Role = 'OWNER' | 'OPERATOR' | 'VIEWER';
3
- export interface Identity {
4
- role: Role;
5
- }
6
- export declare function resolveIdentity(req: Request): Identity;
7
- export declare function requireRole(allowedRoles: Role[]): (req: Request, res: Response, next: NextFunction) => Response<any, Record<string, any>> | undefined;
8
- export declare function attachIdentity(req: Request, res: Response, next: NextFunction): void;
package/dist/auth.js DELETED
@@ -1,40 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveIdentity = resolveIdentity;
4
- exports.requireRole = requireRole;
5
- exports.attachIdentity = attachIdentity;
6
- function resolveIdentity(req) {
7
- const authHeader = req.headers.authorization;
8
- const token = authHeader?.startsWith('Bearer ') ? authHeader.split(' ')[1] : undefined;
9
- // 1. Explicit token matches
10
- if (process.env.OWNER_TOKEN && token === process.env.OWNER_TOKEN) {
11
- return { role: 'OWNER' };
12
- }
13
- if (process.env.OPERATOR_TOKEN && token === process.env.OPERATOR_TOKEN) {
14
- return { role: 'OPERATOR' };
15
- }
16
- // 2. Development fallback
17
- const isDev = process.env.NODE_ENV !== 'production';
18
- if (isDev && process.env.DEV_LOCAL_AUTH_ROLE) {
19
- const fallbackRole = process.env.DEV_LOCAL_AUTH_ROLE.toUpperCase();
20
- if (['OWNER', 'OPERATOR', 'VIEWER'].includes(fallbackRole)) {
21
- return { role: fallbackRole };
22
- }
23
- }
24
- // Default
25
- return { role: 'VIEWER' };
26
- }
27
- function requireRole(allowedRoles) {
28
- return (req, res, next) => {
29
- const identity = resolveIdentity(req);
30
- req.identity = identity;
31
- if (!allowedRoles.includes(identity.role)) {
32
- return res.status(403).json({ error: `Forbidden: requires one of ${allowedRoles.join(', ')}` });
33
- }
34
- next();
35
- };
36
- }
37
- function attachIdentity(req, res, next) {
38
- req.identity = resolveIdentity(req);
39
- next();
40
- }
@@ -1 +0,0 @@
1
- export {};
package/dist/auth.test.js DELETED
@@ -1,48 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const auth_1 = require("./auth");
4
- describe('Auth Identity Resolution', () => {
5
- const originalEnv = process.env;
6
- beforeEach(() => {
7
- jest.resetModules();
8
- process.env = { ...originalEnv };
9
- });
10
- afterAll(() => {
11
- process.env = originalEnv;
12
- });
13
- const mockReq = (token) => ({
14
- headers: {
15
- authorization: token ? `Bearer ${token}` : undefined
16
- }
17
- });
18
- test('resolves explicit owner token', () => {
19
- process.env.OWNER_TOKEN = 'owner-secret';
20
- process.env.OPERATOR_TOKEN = 'operator-secret';
21
- process.env.NODE_ENV = 'production';
22
- const ownerId = (0, auth_1.resolveIdentity)(mockReq('owner-secret'));
23
- expect(ownerId.role).toBe('OWNER');
24
- });
25
- test('resolves explicit operator token', () => {
26
- process.env.OWNER_TOKEN = 'owner-secret';
27
- process.env.OPERATOR_TOKEN = 'operator-secret';
28
- process.env.NODE_ENV = 'production';
29
- const opId = (0, auth_1.resolveIdentity)(mockReq('operator-secret'));
30
- expect(opId.role).toBe('OPERATOR');
31
- });
32
- test('defaults to viewer when no token is present in production', () => {
33
- process.env.NODE_ENV = 'production';
34
- const viewerId = (0, auth_1.resolveIdentity)(mockReq());
35
- expect(viewerId.role).toBe('VIEWER');
36
- });
37
- test('defaults to viewer for invalid token in production', () => {
38
- process.env.NODE_ENV = 'production';
39
- const invalidId = (0, auth_1.resolveIdentity)(mockReq('invalid-token'));
40
- expect(invalidId.role).toBe('VIEWER');
41
- });
42
- test('resolves development fallback role', () => {
43
- process.env.NODE_ENV = 'development';
44
- process.env.DEV_LOCAL_AUTH_ROLE = 'OWNER';
45
- const devId = (0, auth_1.resolveIdentity)(mockReq());
46
- expect(devId.role).toBe('OWNER');
47
- });
48
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,121 +0,0 @@
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 app_1 = require("./app");
8
- const runtime_1 = require("./runtime");
9
- describe('T0 B0 & B6 Runtime Proof Suite', () => {
10
- let mockBrain;
11
- let mockMemory;
12
- let mockKnowledge;
13
- let mockBehavior;
14
- let runtime;
15
- let app;
16
- beforeEach(async () => {
17
- mockBrain = {
18
- generatePlan: jest.fn().mockImplementation(async (ctx) => {
19
- return {
20
- speech: 'Hello. I am a neutral companion.',
21
- language: 'en',
22
- };
23
- }),
24
- };
25
- mockMemory = {
26
- initialize: jest.fn().mockResolvedValue(undefined),
27
- searchClaims: jest.fn().mockResolvedValue([]),
28
- getClaims: jest.fn().mockResolvedValue([]),
29
- getDirectives: jest.fn().mockResolvedValue([]),
30
- getPendingClaims: jest.fn().mockResolvedValue([]),
31
- proposeClaim: jest.fn().mockResolvedValue({}),
32
- approveClaim: jest.fn().mockResolvedValue(undefined),
33
- rejectClaim: jest.fn().mockResolvedValue(undefined),
34
- };
35
- mockKnowledge = {
36
- search: jest.fn().mockResolvedValue([]),
37
- };
38
- mockBehavior = {
39
- compile: jest.fn().mockResolvedValue(''),
40
- };
41
- const config = {
42
- name: 'NeutralCompanion',
43
- brain: { provider: 'openrouter' },
44
- memory: { provider: 'postgres' },
45
- knowledge: { provider: 'e-knowledge' },
46
- behavior: { provider: 'active-self' },
47
- voice: { provider: 'none' },
48
- vision: { provider: 'none' },
49
- body: { provider: 'none' },
50
- };
51
- runtime = new runtime_1.SiduriRuntime('companion-a', config, {
52
- brain: mockBrain,
53
- memory: mockMemory,
54
- knowledge: mockKnowledge,
55
- behavior: mockBehavior,
56
- });
57
- await runtime.initialize();
58
- const runtimes = new Map([['companion-a', runtime]]);
59
- const created = (0, app_1.createApp)(runtimes);
60
- app = created.app;
61
- });
62
- // B0: Fresh companion is empty (no prior claims, no user relationship, no knowledge search on greeting)
63
- describe('B0 — Fresh companion is empty', () => {
64
- test('initial state has empty memory and empty directives', async () => {
65
- const claims = await runtime.memory?.getClaims();
66
- const directives = await runtime.memory?.getDirectives();
67
- expect(claims).toEqual([]);
68
- expect(directives).toEqual([]);
69
- });
70
- test('greeting does not query knowledge or inject prior personal knowledge', async () => {
71
- const res = await (0, supertest_1.default)(app)
72
- .post('/chat')
73
- .send({
74
- companionId: 'companion-a',
75
- message: 'Hello.',
76
- history: [],
77
- });
78
- expect(res.status).toBe(200);
79
- expect(mockKnowledge.search).not.toHaveBeenCalled();
80
- expect(mockBrain.generatePlan).toHaveBeenCalledWith(expect.objectContaining({
81
- contextPrompt: '',
82
- recipient: 'VIEWER',
83
- }));
84
- });
85
- });
86
- // B6: Identity and relationship are learned, not inferred (self identity questions do not query external knowledge)
87
- describe('B6 — Identity and relationship are learned, not inferred', () => {
88
- test('asking "Who are you?" suppresses knowledge query and asserts self identity without external search', async () => {
89
- mockBrain.generatePlan.mockResolvedValueOnce({
90
- speech: 'I am NeutralCompanion.',
91
- language: 'en',
92
- });
93
- const res = await (0, supertest_1.default)(app)
94
- .post('/chat')
95
- .send({
96
- companionId: 'companion-a',
97
- message: 'Who are you?',
98
- history: [],
99
- });
100
- expect(res.status).toBe(200);
101
- // B6 oracle: self identity chat does not query external knowledge
102
- expect(mockKnowledge.search).not.toHaveBeenCalled();
103
- expect(mockMemory.searchClaims).toHaveBeenCalled();
104
- expect(mockBrain.generatePlan).toHaveBeenCalledWith(expect.objectContaining({
105
- contextPrompt: '',
106
- }));
107
- expect(res.body.response.subtitle_en).toBe('I am NeutralCompanion.');
108
- });
109
- test('asking "Tell me about yourself" suppresses knowledge query', async () => {
110
- const res = await (0, supertest_1.default)(app)
111
- .post('/chat')
112
- .send({
113
- companionId: 'companion-a',
114
- message: 'Tell me about yourself',
115
- history: [],
116
- });
117
- expect(res.status).toBe(200);
118
- expect(mockKnowledge.search).not.toHaveBeenCalled();
119
- });
120
- });
121
- });
@@ -1,15 +0,0 @@
1
- import { RequestContext, DiagnosticCode, ContextError } from '@siduri-x/core';
2
- export interface ContextMapperOptions {
3
- endpointPolicy?: 'public' | 'private' | 'operator' | 'direct';
4
- defaultPublicAudience?: string;
5
- defaultPrivateAudience?: string;
6
- defaultOperatorAudience?: string;
7
- allowAnonymousPublicChat?: boolean;
8
- }
9
- export interface MapRequestContextResult {
10
- accepted: boolean;
11
- context?: RequestContext;
12
- diagnostics?: DiagnosticCode[];
13
- error?: ContextError;
14
- }
15
- export declare function mapRequestContext(input: any, options?: ContextMapperOptions): MapRequestContextResult;