@siduri-x/api 1.0.1 → 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/package.json +11 -10
- package/src/app.ts +276 -49
- package/src/index.test.ts +53 -0
- package/src/index.ts +30 -21
- package/src/runtime.test.ts +55 -1
- package/dist/app.d.ts +0 -9
- package/dist/app.js +0 -480
- package/dist/auth.d.ts +0 -8
- package/dist/auth.js +0 -40
- package/dist/auth.test.d.ts +0 -1
- package/dist/auth.test.js +0 -48
- package/dist/b0-b6.test.d.ts +0 -1
- package/dist/b0-b6.test.js +0 -121
- package/dist/context-mapper.d.ts +0 -15
- package/dist/context-mapper.js +0 -287
- package/dist/context-mapper.test.d.ts +0 -1
- package/dist/context-mapper.test.js +0 -233
- package/dist/cors.d.ts +0 -3
- package/dist/cors.js +0 -42
- package/dist/index.d.ts +0 -6
- package/dist/index.js +0 -167
- package/dist/index.test.d.ts +0 -1
- package/dist/index.test.js +0 -115
- package/dist/runtime.d.ts +0 -1
- package/dist/runtime.js +0 -17
- package/dist/runtime.test.d.ts +0 -1
- package/dist/runtime.test.js +0 -240
- package/dist/smoke.test.d.ts +0 -0
- package/dist/smoke.test.js +0 -6
- package/dist/t4-gating.test.d.ts +0 -1
- package/dist/t4-gating.test.js +0 -193
- package/dist/t5-experience.test.d.ts +0 -1
- package/dist/t5-experience.test.js +0 -156
- package/dist/t6-security.test.d.ts +0 -1
- package/dist/t6-security.test.js +0 -424
- package/dist/t7-release.test.d.ts +0 -1
- package/dist/t7-release.test.js +0 -119
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const context_mapper_1 = require("./context-mapper");
|
|
4
|
-
describe('API Request Context Mapper (P2 & T1-01 to T1-10)', () => {
|
|
5
|
-
// T1-01: Anonymous public chat, no audience
|
|
6
|
-
test('T1-01: Anonymous public chat with no audience resolves to configured public audience and no subject', () => {
|
|
7
|
-
const input = {
|
|
8
|
-
companionId: 'companion-a',
|
|
9
|
-
context: {
|
|
10
|
-
actor: {
|
|
11
|
-
actorId: 'anonymous-session-a',
|
|
12
|
-
sessionId: 'session-a',
|
|
13
|
-
authorizationRole: 'viewer',
|
|
14
|
-
capabilities: ['chat:public'],
|
|
15
|
-
authenticated: false,
|
|
16
|
-
},
|
|
17
|
-
conversation: {
|
|
18
|
-
channel: 'public',
|
|
19
|
-
correlationId: 'corr-public-a',
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
message: 'Hello.',
|
|
23
|
-
history: [],
|
|
24
|
-
};
|
|
25
|
-
const result = (0, context_mapper_1.mapRequestContext)(input, {
|
|
26
|
-
endpointPolicy: 'public',
|
|
27
|
-
defaultPublicAudience: 'audience-public',
|
|
28
|
-
});
|
|
29
|
-
expect(result.accepted).toBe(true);
|
|
30
|
-
expect(result.context?.companionId).toBe('companion-a');
|
|
31
|
-
expect(result.context?.conversation.channel).toBe('public');
|
|
32
|
-
expect(result.context?.conversation.audienceId).toBe('audience-public');
|
|
33
|
-
expect(result.context?.subject).toBeUndefined();
|
|
34
|
-
expect(result.diagnostics).toContain('audience_defaulted_by_public_policy');
|
|
35
|
-
});
|
|
36
|
-
// T1-02: OWNER public chat
|
|
37
|
-
test('T1-02: OWNER public chat maps authorization metadata only with no relationship or private routing', () => {
|
|
38
|
-
const input = {
|
|
39
|
-
id: 'companion-a',
|
|
40
|
-
role: 'OWNER',
|
|
41
|
-
correlationId: 'corr-owner-public',
|
|
42
|
-
message: 'Public broadcast message',
|
|
43
|
-
};
|
|
44
|
-
const result = (0, context_mapper_1.mapRequestContext)(input, { endpointPolicy: 'public' });
|
|
45
|
-
expect(result.accepted).toBe(true);
|
|
46
|
-
expect(result.context?.actor.authorizationRole).toBe('administrator');
|
|
47
|
-
expect(result.context?.conversation.channel).toBe('public');
|
|
48
|
-
expect(result.context?.conversation.audienceId).toBe('audience-public');
|
|
49
|
-
expect(result.context?.subject).toBeUndefined();
|
|
50
|
-
expect(result.diagnostics).toContain('legacy_role_mapped_to_authorization');
|
|
51
|
-
});
|
|
52
|
-
// T1-03: Private chat without capability
|
|
53
|
-
test('T1-03: Private chat without capability is rejected before organ invocation', () => {
|
|
54
|
-
const input = {
|
|
55
|
-
companionId: 'companion-a',
|
|
56
|
-
context: {
|
|
57
|
-
actor: {
|
|
58
|
-
actorId: 'actor-a',
|
|
59
|
-
sessionId: 'session-a',
|
|
60
|
-
authorizationRole: 'viewer',
|
|
61
|
-
capabilities: ['chat:public'], // Missing 'chat:private'
|
|
62
|
-
authenticated: true,
|
|
63
|
-
},
|
|
64
|
-
conversation: {
|
|
65
|
-
channel: 'private',
|
|
66
|
-
audienceId: 'audience-private-a',
|
|
67
|
-
correlationId: 'corr-private-a',
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
message: 'Show private data',
|
|
71
|
-
};
|
|
72
|
-
const result = (0, context_mapper_1.mapRequestContext)(input, { endpointPolicy: 'private' });
|
|
73
|
-
expect(result.accepted).toBe(false);
|
|
74
|
-
expect(result.error?.code).toBe('UNAUTHORIZED_CHANNEL_OR_CAPABILITY');
|
|
75
|
-
expect(result.error?.fields).toContain('actor.capabilities');
|
|
76
|
-
});
|
|
77
|
-
// T1-04: Operator request without operator context
|
|
78
|
-
test('T1-04: Operator request without operator context is rejected with diagnostic', () => {
|
|
79
|
-
const input = {
|
|
80
|
-
id: 'companion-a',
|
|
81
|
-
role: 'OPERATOR',
|
|
82
|
-
correlationId: 'corr-op-missing',
|
|
83
|
-
};
|
|
84
|
-
const result = (0, context_mapper_1.mapRequestContext)(input, { endpointPolicy: 'operator' });
|
|
85
|
-
expect(result.accepted).toBe(false);
|
|
86
|
-
expect(result.error?.code).toBe('MISSING_CONTEXT');
|
|
87
|
-
expect(result.error?.fields).toEqual(expect.arrayContaining(['conversation.channel', 'conversation.audienceId', 'actor.capabilities']));
|
|
88
|
-
});
|
|
89
|
-
// T1-05: Global primary_user input
|
|
90
|
-
test('T1-05: Global primary_user input is rejected/quarantined at mapper', () => {
|
|
91
|
-
const input = {
|
|
92
|
-
companionId: 'companion-a',
|
|
93
|
-
context: {
|
|
94
|
-
actor: {
|
|
95
|
-
actorId: 'actor-a',
|
|
96
|
-
sessionId: 'session-a',
|
|
97
|
-
authorizationRole: 'viewer',
|
|
98
|
-
capabilities: ['chat:public'],
|
|
99
|
-
authenticated: true,
|
|
100
|
-
},
|
|
101
|
-
conversation: {
|
|
102
|
-
channel: 'public',
|
|
103
|
-
audienceId: 'audience-public',
|
|
104
|
-
correlationId: 'corr-primary-user',
|
|
105
|
-
},
|
|
106
|
-
subject: {
|
|
107
|
-
subjectId: 'primary_user',
|
|
108
|
-
kind: 'actor',
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
};
|
|
112
|
-
const result = (0, context_mapper_1.mapRequestContext)(input);
|
|
113
|
-
expect(result.accepted).toBe(false);
|
|
114
|
-
expect(result.error?.code).toBe('FORBIDDEN_CONTEXT');
|
|
115
|
-
expect(result.error?.field).toBe('subject.subjectId');
|
|
116
|
-
});
|
|
117
|
-
// T1-06: MASTER_PRIVATE in public mode
|
|
118
|
-
test('T1-06: MASTER_PRIVATE in public mode is rejected with no fallback', () => {
|
|
119
|
-
const input = {
|
|
120
|
-
companionId: 'companion-a',
|
|
121
|
-
context: {
|
|
122
|
-
actor: {
|
|
123
|
-
actorId: 'actor-a',
|
|
124
|
-
sessionId: 'session-a',
|
|
125
|
-
authorizationRole: 'viewer',
|
|
126
|
-
capabilities: ['chat:public'],
|
|
127
|
-
authenticated: false,
|
|
128
|
-
},
|
|
129
|
-
conversation: {
|
|
130
|
-
channel: 'public',
|
|
131
|
-
audienceId: 'MASTER_PRIVATE',
|
|
132
|
-
correlationId: 'corr-master-private',
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
};
|
|
136
|
-
const result = (0, context_mapper_1.mapRequestContext)(input, { endpointPolicy: 'public' });
|
|
137
|
-
expect(result.accepted).toBe(false);
|
|
138
|
-
expect(result.error?.code).toBe('LEGACY_PERSONAL_AUDIENCE');
|
|
139
|
-
expect(result.error?.field).toBe('audienceId');
|
|
140
|
-
});
|
|
141
|
-
// T1-07: Missing actor on explicit teaching
|
|
142
|
-
test('T1-07: Missing actor on explicit teaching produces no durable profile and pending/anonymous context only', () => {
|
|
143
|
-
const input = {
|
|
144
|
-
companionId: 'companion-a',
|
|
145
|
-
context: {
|
|
146
|
-
actor: {
|
|
147
|
-
actorId: '',
|
|
148
|
-
sessionId: 'session-a',
|
|
149
|
-
authorizationRole: 'viewer',
|
|
150
|
-
capabilities: ['chat:public'],
|
|
151
|
-
authenticated: false,
|
|
152
|
-
},
|
|
153
|
-
conversation: {
|
|
154
|
-
channel: 'public',
|
|
155
|
-
audienceId: 'audience-public',
|
|
156
|
-
correlationId: 'corr-teach-no-actor',
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
};
|
|
160
|
-
const result = (0, context_mapper_1.mapRequestContext)(input);
|
|
161
|
-
expect(result.accepted).toBe(false);
|
|
162
|
-
expect(result.error?.code).toBe('MISSING_CONTEXT');
|
|
163
|
-
expect(result.error?.fields).toContain('actor.actorId');
|
|
164
|
-
});
|
|
165
|
-
// T1-08: Two companion IDs, same actor remain isolated
|
|
166
|
-
test('T1-08: Two companion IDs with same actor remain isolated under context mapper', () => {
|
|
167
|
-
const actor = {
|
|
168
|
-
actorId: 'actor-a',
|
|
169
|
-
sessionId: 'session-a',
|
|
170
|
-
authorizationRole: 'viewer',
|
|
171
|
-
capabilities: ['chat:public'],
|
|
172
|
-
authenticated: true,
|
|
173
|
-
};
|
|
174
|
-
const conv = {
|
|
175
|
-
channel: 'public',
|
|
176
|
-
audienceId: 'audience-public',
|
|
177
|
-
correlationId: 'corr-iso-1',
|
|
178
|
-
};
|
|
179
|
-
const reqA = { companionId: 'companion-a', context: { actor, conversation: conv } };
|
|
180
|
-
const reqB = { companionId: 'companion-b', context: { actor, conversation: conv } };
|
|
181
|
-
const resA = (0, context_mapper_1.mapRequestContext)(reqA);
|
|
182
|
-
const resB = (0, context_mapper_1.mapRequestContext)(reqB);
|
|
183
|
-
expect(resA.accepted).toBe(true);
|
|
184
|
-
expect(resB.accepted).toBe(true);
|
|
185
|
-
expect(resA.context?.companionId).toBe('companion-a');
|
|
186
|
-
expect(resB.context?.companionId).toBe('companion-b');
|
|
187
|
-
});
|
|
188
|
-
// T1-09: Missing correlation ID
|
|
189
|
-
test('T1-09: Missing correlation ID is rejected before stateful operation', () => {
|
|
190
|
-
const input = {
|
|
191
|
-
companionId: 'companion-a',
|
|
192
|
-
context: {
|
|
193
|
-
actor: {
|
|
194
|
-
actorId: 'actor-a',
|
|
195
|
-
sessionId: 'session-a',
|
|
196
|
-
authorizationRole: 'viewer',
|
|
197
|
-
capabilities: ['chat:public'],
|
|
198
|
-
authenticated: true,
|
|
199
|
-
},
|
|
200
|
-
conversation: {
|
|
201
|
-
channel: 'public',
|
|
202
|
-
audienceId: 'audience-public',
|
|
203
|
-
correlationId: '',
|
|
204
|
-
},
|
|
205
|
-
},
|
|
206
|
-
};
|
|
207
|
-
const result = (0, context_mapper_1.mapRequestContext)(input);
|
|
208
|
-
expect(result.accepted).toBe(false);
|
|
209
|
-
expect(result.error?.code).toBe('MISSING_CONTEXT');
|
|
210
|
-
expect(result.error?.fields).toContain('conversation.correlationId');
|
|
211
|
-
});
|
|
212
|
-
// T1-10: Ambiguous legacy request
|
|
213
|
-
test('T1-10: Ambiguous legacy request emits structured diagnostic and rejects ambiguous mapping', () => {
|
|
214
|
-
const input = {
|
|
215
|
-
companionId: 'companion-a',
|
|
216
|
-
role: 'VIEWER',
|
|
217
|
-
context: {
|
|
218
|
-
actor: {
|
|
219
|
-
actorId: 'actor-a',
|
|
220
|
-
sessionId: 'session-a',
|
|
221
|
-
authorizationRole: 'viewer',
|
|
222
|
-
capabilities: ['chat:public'],
|
|
223
|
-
authenticated: true,
|
|
224
|
-
},
|
|
225
|
-
// Role supplied at top level without channel or audience
|
|
226
|
-
},
|
|
227
|
-
};
|
|
228
|
-
const result = (0, context_mapper_1.mapRequestContext)(input);
|
|
229
|
-
expect(result.accepted).toBe(false);
|
|
230
|
-
expect(result.error?.code).toBe('AMBIGUOUS_CONTEXT');
|
|
231
|
-
expect(result.error?.conflicts).toEqual(expect.arrayContaining(['role_does_not_select_audience', 'role_does_not_select_subject']));
|
|
232
|
-
});
|
|
233
|
-
});
|
package/dist/cors.d.ts
DELETED
package/dist/cors.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getAllowedOrigins = getAllowedOrigins;
|
|
4
|
-
exports.createCorsOptions = createCorsOptions;
|
|
5
|
-
function getAllowedOrigins() {
|
|
6
|
-
const allowed = new Set([
|
|
7
|
-
'http://localhost:3000',
|
|
8
|
-
'http://127.0.0.1:3000',
|
|
9
|
-
'http://localhost:3001',
|
|
10
|
-
'http://127.0.0.1:3001',
|
|
11
|
-
]);
|
|
12
|
-
if (process.env.PORT) {
|
|
13
|
-
allowed.add(`http://localhost:${process.env.PORT}`);
|
|
14
|
-
allowed.add(`http://127.0.0.1:${process.env.PORT}`);
|
|
15
|
-
}
|
|
16
|
-
const envOrigins = process.env.ALLOWED_ORIGINS;
|
|
17
|
-
if (envOrigins) {
|
|
18
|
-
for (const origin of envOrigins.split(',')) {
|
|
19
|
-
const trimmed = origin.trim();
|
|
20
|
-
if (trimmed) {
|
|
21
|
-
allowed.add(trimmed);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return allowed;
|
|
26
|
-
}
|
|
27
|
-
function createCorsOptions() {
|
|
28
|
-
return {
|
|
29
|
-
origin: (origin, callback) => {
|
|
30
|
-
// Allow non-browser requests with no origin header (e.g., native tools, curl)
|
|
31
|
-
if (!origin) {
|
|
32
|
-
return callback(null, true);
|
|
33
|
-
}
|
|
34
|
-
const allowedOrigins = getAllowedOrigins();
|
|
35
|
-
if (allowedOrigins.has(origin)) {
|
|
36
|
-
return callback(null, true);
|
|
37
|
-
}
|
|
38
|
-
return callback(null, false);
|
|
39
|
-
},
|
|
40
|
-
credentials: true,
|
|
41
|
-
};
|
|
42
|
-
}
|
package/dist/index.d.ts
DELETED
package/dist/index.js
DELETED
|
@@ -1,167 +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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
-
};
|
|
19
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.app = exports.createApp = void 0;
|
|
21
|
-
const promises_1 = require("node:fs/promises");
|
|
22
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
23
|
-
const app_1 = require("./app");
|
|
24
|
-
Object.defineProperty(exports, "createApp", { enumerable: true, get: function () { return app_1.createApp; } });
|
|
25
|
-
const runtime_1 = require("./runtime");
|
|
26
|
-
const brain_1 = require("@siduri-x/brain");
|
|
27
|
-
const memory_1 = require("@siduri-x/memory");
|
|
28
|
-
const voice_1 = require("@siduri-x/voice");
|
|
29
|
-
const knowledge_1 = require("@siduri-x/knowledge");
|
|
30
|
-
const vision_1 = require("@siduri-x/vision");
|
|
31
|
-
const behavior_1 = require("@siduri-x/behavior");
|
|
32
|
-
const body_1 = require("@siduri-x/body");
|
|
33
|
-
const observation_1 = require("@siduri-x/observation");
|
|
34
|
-
__exportStar(require("./context-mapper"), exports);
|
|
35
|
-
const runtimes = new Map();
|
|
36
|
-
const instance = (0, app_1.createApp)(runtimes);
|
|
37
|
-
exports.app = instance.app;
|
|
38
|
-
exports.default = exports.app;
|
|
39
|
-
function createBrain(config) {
|
|
40
|
-
const provider = config.provider || 'openrouter';
|
|
41
|
-
const defaultKeyEnv = provider === 'openai-compatible' ? 'OPENAI_COMPATIBLE_API_KEY' : 'OPENROUTER_API_KEY';
|
|
42
|
-
const apiKey = config.apiKey || process.env[config.apiKeyEnv || defaultKeyEnv] || '';
|
|
43
|
-
if (provider === 'openai-compatible') {
|
|
44
|
-
return new brain_1.OpenAICompatibleBrain({
|
|
45
|
-
apiKey,
|
|
46
|
-
model: config.model || 'local-model',
|
|
47
|
-
baseUrl: config.baseUrl || 'http://127.0.0.1:1234/v1',
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
return new brain_1.OpenRouterBrain({ apiKey, model: config.model || 'gpt-4o-mini' });
|
|
51
|
-
}
|
|
52
|
-
function isDisabled(config) {
|
|
53
|
-
return !config || config.provider === 'none';
|
|
54
|
-
}
|
|
55
|
-
function createVoice(config) {
|
|
56
|
-
return isDisabled(config)
|
|
57
|
-
? undefined
|
|
58
|
-
: new voice_1.VoiceAdapter({ provider: config.provider || 'voicevox', baseUrl: process.env.VOICEVOX_URL || 'http://localhost:50021', speakerId: config.speakerId || 1 });
|
|
59
|
-
}
|
|
60
|
-
function createKnowledge(config) {
|
|
61
|
-
if (isDisabled(config))
|
|
62
|
-
return undefined;
|
|
63
|
-
if (!config?.packPath && !config?.registryUrl && !config?.baseUrl && !config?.hubUrl) {
|
|
64
|
-
return undefined;
|
|
65
|
-
}
|
|
66
|
-
return new knowledge_1.EKnowledgeAdapter(config);
|
|
67
|
-
}
|
|
68
|
-
function createVision(config) {
|
|
69
|
-
return isDisabled(config)
|
|
70
|
-
? undefined
|
|
71
|
-
: new vision_1.OpenRouterVisionAdapter({ apiKey: process.env.OPENROUTER_API_KEY || '', model: config.model || 'gpt-4-vision' });
|
|
72
|
-
}
|
|
73
|
-
function createBehavior(config) {
|
|
74
|
-
return isDisabled(config) ? undefined : new behavior_1.ActiveSelfCompiler();
|
|
75
|
-
}
|
|
76
|
-
function createBody(config) {
|
|
77
|
-
return isDisabled(config)
|
|
78
|
-
? undefined
|
|
79
|
-
: new body_1.Live2DAdapter(config);
|
|
80
|
-
}
|
|
81
|
-
const PORT = process.env.PORT || 3001;
|
|
82
|
-
const defaultCompanionConfig = {
|
|
83
|
-
id: 'default',
|
|
84
|
-
name: 'Siduri',
|
|
85
|
-
brain: { provider: 'openrouter', model: 'gpt-4o-mini' },
|
|
86
|
-
voice: { provider: 'voicevox', speakerId: 1 },
|
|
87
|
-
memory: { provider: 'postgres' },
|
|
88
|
-
knowledge: {
|
|
89
|
-
provider: process.env.SIDURI_KNOWLEDGE_PROVIDER || 'e-knowledge',
|
|
90
|
-
packPath: process.env.SIDURI_KNOWLEDGE_PACK || '',
|
|
91
|
-
registryUrl: process.env.SIDURI_KNOWLEDGE_REGISTRY_URL || '',
|
|
92
|
-
packId: process.env.SIDURI_KNOWLEDGE_PACK_ID || '',
|
|
93
|
-
timeoutMs: Number(process.env.SIDURI_KNOWLEDGE_TIMEOUT_MS || 5000),
|
|
94
|
-
preferredMode: process.env.SIDURI_KNOWLEDGE_MODE || 'lexical',
|
|
95
|
-
},
|
|
96
|
-
behavior: { provider: 'active_self' },
|
|
97
|
-
body: {
|
|
98
|
-
provider: 'live2d',
|
|
99
|
-
},
|
|
100
|
-
vision: { provider: 'openrouter', model: 'gpt-4-vision' }
|
|
101
|
-
};
|
|
102
|
-
async function loadCompanionConfig() {
|
|
103
|
-
const configPath = process.env.SIDURI_CONFIG || node_path_1.default.resolve(process.cwd(), 'siduri.config.json');
|
|
104
|
-
let fileConfig = {};
|
|
105
|
-
try {
|
|
106
|
-
fileConfig = JSON.parse(await (0, promises_1.readFile)(configPath, 'utf8'));
|
|
107
|
-
console.log(`Loaded companion configuration from ${configPath}`);
|
|
108
|
-
}
|
|
109
|
-
catch (error) {
|
|
110
|
-
if (error?.code !== 'ENOENT')
|
|
111
|
-
throw new Error(`Unable to read ${configPath}: ${error.message}`);
|
|
112
|
-
console.log(`No ${configPath} found; using environment/default configuration.`);
|
|
113
|
-
}
|
|
114
|
-
const config = {
|
|
115
|
-
...defaultCompanionConfig,
|
|
116
|
-
...fileConfig,
|
|
117
|
-
id: fileConfig.id || defaultCompanionConfig.id,
|
|
118
|
-
brain: { ...defaultCompanionConfig.brain, ...fileConfig.brain },
|
|
119
|
-
voice: { ...defaultCompanionConfig.voice, ...fileConfig.voice },
|
|
120
|
-
memory: { ...defaultCompanionConfig.memory, ...fileConfig.memory },
|
|
121
|
-
knowledge: { ...defaultCompanionConfig.knowledge, ...fileConfig.knowledge },
|
|
122
|
-
behavior: { ...defaultCompanionConfig.behavior, ...fileConfig.behavior },
|
|
123
|
-
body: { ...defaultCompanionConfig.body, ...fileConfig.body },
|
|
124
|
-
vision: { ...defaultCompanionConfig.vision, ...fileConfig.vision },
|
|
125
|
-
};
|
|
126
|
-
if (process.env.SIDURI_KNOWLEDGE_PROVIDER)
|
|
127
|
-
config.knowledge.provider = process.env.SIDURI_KNOWLEDGE_PROVIDER;
|
|
128
|
-
if (process.env.SIDURI_KNOWLEDGE_PACK)
|
|
129
|
-
config.knowledge.packPath = process.env.SIDURI_KNOWLEDGE_PACK;
|
|
130
|
-
if (process.env.SIDURI_KNOWLEDGE_REGISTRY_URL)
|
|
131
|
-
config.knowledge.registryUrl = process.env.SIDURI_KNOWLEDGE_REGISTRY_URL;
|
|
132
|
-
if (process.env.SIDURI_KNOWLEDGE_PACK_ID)
|
|
133
|
-
config.knowledge.packId = process.env.SIDURI_KNOWLEDGE_PACK_ID;
|
|
134
|
-
if (process.env.SIDURI_KNOWLEDGE_MODE)
|
|
135
|
-
config.knowledge.preferredMode = process.env.SIDURI_KNOWLEDGE_MODE;
|
|
136
|
-
return config;
|
|
137
|
-
}
|
|
138
|
-
async function bootDefaultCompanion() {
|
|
139
|
-
if (runtimes.has('default'))
|
|
140
|
-
return;
|
|
141
|
-
console.log("Booting default companion...");
|
|
142
|
-
const config = await loadCompanionConfig();
|
|
143
|
-
const brain = createBrain(config.brain);
|
|
144
|
-
const memory = new memory_1.PostgresMemoryOrgan({ connectionString: process.env.DATABASE_URL || 'postgresql://postgres:postgres@localhost:5432/siduri' });
|
|
145
|
-
const voice = createVoice(config.voice);
|
|
146
|
-
const knowledge = createKnowledge(config.knowledge);
|
|
147
|
-
const vision = createVision(config.vision);
|
|
148
|
-
const observation = new observation_1.FixtureObservationOrgan(vision ?? { analyze: async () => JSON.stringify({ readings: [] }) });
|
|
149
|
-
instance.setObservationOrgan(observation);
|
|
150
|
-
const behavior = createBehavior(config.behavior);
|
|
151
|
-
const body = createBody(config.body);
|
|
152
|
-
await memory.runMigrations().catch(e => console.warn("Migrations warning:", e.message));
|
|
153
|
-
const runtime = new runtime_1.SiduriRuntime('default', config, { brain, memory, voice, knowledge, vision, behavior, body });
|
|
154
|
-
await runtime.initialize();
|
|
155
|
-
runtimes.set('default', runtime);
|
|
156
|
-
console.log("Default companion booted successfully.");
|
|
157
|
-
}
|
|
158
|
-
if (process.env.NODE_ENV !== 'test') {
|
|
159
|
-
bootDefaultCompanion().then(() => {
|
|
160
|
-
exports.app.listen(Number(PORT), '127.0.0.1', () => {
|
|
161
|
-
console.log(`Siduri-X API running on port ${PORT} (127.0.0.1)`);
|
|
162
|
-
});
|
|
163
|
-
}).catch(e => {
|
|
164
|
-
console.error("Failed to boot default companion:", e);
|
|
165
|
-
process.exit(1);
|
|
166
|
-
});
|
|
167
|
-
}
|
package/dist/index.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/index.test.js
DELETED
|
@@ -1,115 +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
|
-
describe('API Boundary Context Validation (P2 Route Integration)', () => {
|
|
9
|
-
const fakeRuntime = {
|
|
10
|
-
handleUserMessage: jest.fn().mockResolvedValue({
|
|
11
|
-
response: { subtitle_en: 'Hello there' },
|
|
12
|
-
metadata: {},
|
|
13
|
-
}),
|
|
14
|
-
};
|
|
15
|
-
let app;
|
|
16
|
-
let runtimes;
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
runtimes = new Map([['companion-a', fakeRuntime]]);
|
|
19
|
-
const created = (0, app_1.createApp)(runtimes);
|
|
20
|
-
app = created.app;
|
|
21
|
-
fakeRuntime.handleUserMessage.mockClear();
|
|
22
|
-
});
|
|
23
|
-
test('accepts valid anonymous chat request and maps through API boundary', async () => {
|
|
24
|
-
const res = await (0, supertest_1.default)(app)
|
|
25
|
-
.post('/chat')
|
|
26
|
-
.send({
|
|
27
|
-
id: 'companion-a',
|
|
28
|
-
message: 'Hello neutral world',
|
|
29
|
-
history: [],
|
|
30
|
-
});
|
|
31
|
-
expect(res.status).toBe(200);
|
|
32
|
-
expect(fakeRuntime.handleUserMessage).toHaveBeenCalledWith('Hello neutral world', 'OWNER', []);
|
|
33
|
-
});
|
|
34
|
-
test('accepts neutral context chat envelope at /chat route', async () => {
|
|
35
|
-
const res = await (0, supertest_1.default)(app)
|
|
36
|
-
.post('/chat')
|
|
37
|
-
.send({
|
|
38
|
-
companionId: 'companion-a',
|
|
39
|
-
context: {
|
|
40
|
-
actor: {
|
|
41
|
-
actorId: 'actor-a',
|
|
42
|
-
sessionId: 'session-a',
|
|
43
|
-
authorizationRole: 'viewer',
|
|
44
|
-
capabilities: ['chat:public'],
|
|
45
|
-
authenticated: false,
|
|
46
|
-
},
|
|
47
|
-
conversation: {
|
|
48
|
-
channel: 'public',
|
|
49
|
-
audienceId: 'audience-public',
|
|
50
|
-
correlationId: 'corr-route-1',
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
message: 'Hello structured context',
|
|
54
|
-
history: [],
|
|
55
|
-
});
|
|
56
|
-
expect(res.status).toBe(200);
|
|
57
|
-
expect(fakeRuntime.handleUserMessage).toHaveBeenCalledWith('Hello structured context', 'VIEWER', []);
|
|
58
|
-
});
|
|
59
|
-
test('rejects MASTER_PRIVATE in public request with 400 and structured error', async () => {
|
|
60
|
-
const res = await (0, supertest_1.default)(app)
|
|
61
|
-
.post('/chat')
|
|
62
|
-
.send({
|
|
63
|
-
companionId: 'companion-a',
|
|
64
|
-
context: {
|
|
65
|
-
actor: {
|
|
66
|
-
actorId: 'actor-a',
|
|
67
|
-
sessionId: 'session-a',
|
|
68
|
-
authorizationRole: 'viewer',
|
|
69
|
-
capabilities: ['chat:public'],
|
|
70
|
-
authenticated: false,
|
|
71
|
-
},
|
|
72
|
-
conversation: {
|
|
73
|
-
channel: 'public',
|
|
74
|
-
audienceId: 'MASTER_PRIVATE',
|
|
75
|
-
correlationId: 'corr-err-1',
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
message: 'Forbidden audience test',
|
|
79
|
-
});
|
|
80
|
-
expect(res.status).toBe(400);
|
|
81
|
-
expect(res.body.accepted).toBe(false);
|
|
82
|
-
expect(res.body.error.code).toBe('LEGACY_PERSONAL_AUDIENCE');
|
|
83
|
-
expect(fakeRuntime.handleUserMessage).not.toHaveBeenCalled();
|
|
84
|
-
});
|
|
85
|
-
test('rejects global primary_user subject with 400 and structured error', async () => {
|
|
86
|
-
const res = await (0, supertest_1.default)(app)
|
|
87
|
-
.post('/chat')
|
|
88
|
-
.send({
|
|
89
|
-
companionId: 'companion-a',
|
|
90
|
-
context: {
|
|
91
|
-
actor: {
|
|
92
|
-
actorId: 'actor-a',
|
|
93
|
-
sessionId: 'session-a',
|
|
94
|
-
authorizationRole: 'viewer',
|
|
95
|
-
capabilities: ['chat:public'],
|
|
96
|
-
authenticated: true,
|
|
97
|
-
},
|
|
98
|
-
conversation: {
|
|
99
|
-
channel: 'public',
|
|
100
|
-
audienceId: 'audience-public',
|
|
101
|
-
correlationId: 'corr-err-2',
|
|
102
|
-
},
|
|
103
|
-
subject: {
|
|
104
|
-
subjectId: 'primary_user',
|
|
105
|
-
kind: 'actor',
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
message: 'Forbidden primary user test',
|
|
109
|
-
});
|
|
110
|
-
expect(res.status).toBe(400);
|
|
111
|
-
expect(res.body.accepted).toBe(false);
|
|
112
|
-
expect(res.body.error.code).toBe('FORBIDDEN_CONTEXT');
|
|
113
|
-
expect(fakeRuntime.handleUserMessage).not.toHaveBeenCalled();
|
|
114
|
-
});
|
|
115
|
-
});
|
package/dist/runtime.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from '@siduri-x/core';
|
package/dist/runtime.js
DELETED
|
@@ -1,17 +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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("@siduri-x/core"), exports);
|
package/dist/runtime.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|