@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
package/src/index.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { Express } from 'express';
|
|
4
|
-
import { createApp, AppInstance } from './app';
|
|
4
|
+
import { createApp, AppInstance, AppBrainConfig, AppBehaviorConfig } from './app';
|
|
5
5
|
import { SiduriRuntime } from './runtime';
|
|
6
6
|
import { OpenAICompatibleBrain, OpenRouterBrain } from '@siduri-x/brain';
|
|
7
7
|
import { PostgresMemoryOrgan } from '@siduri-x/memory';
|
|
8
|
-
import { VoiceAdapter } from '@siduri-x/voice';
|
|
9
|
-
import { EKnowledgeAdapter } from '@siduri-x/knowledge';
|
|
10
|
-
import { OpenRouterVisionAdapter } from '@siduri-x/vision';
|
|
8
|
+
import { VoiceAdapter, VoiceConfig } from '@siduri-x/voice';
|
|
9
|
+
import { EKnowledgeAdapter, EKnowledgeConfig } from '@siduri-x/knowledge';
|
|
10
|
+
import { OpenRouterVisionAdapter, OpenRouterVisionConfig } from '@siduri-x/vision';
|
|
11
11
|
import { ActiveSelfCompiler } from '@siduri-x/behavior';
|
|
12
|
-
import { Live2DAdapter } from '@siduri-x/body';
|
|
12
|
+
import { Live2DAdapter, Live2DAdapterConfig } from '@siduri-x/body';
|
|
13
13
|
import { FixtureObservationOrgan } from '@siduri-x/observation';
|
|
14
14
|
|
|
15
15
|
export { createApp, AppInstance };
|
|
@@ -20,49 +20,58 @@ const instance: AppInstance = createApp(runtimes);
|
|
|
20
20
|
export const app: Express = instance.app;
|
|
21
21
|
export default app;
|
|
22
22
|
|
|
23
|
-
function createBrain(config
|
|
24
|
-
const provider = config
|
|
23
|
+
function createBrain(config?: AppBrainConfig) {
|
|
24
|
+
const provider = config?.provider || 'openrouter';
|
|
25
25
|
const defaultKeyEnv = provider === 'openai-compatible' ? 'OPENAI_COMPATIBLE_API_KEY' : 'OPENROUTER_API_KEY';
|
|
26
|
-
const apiKey = config
|
|
26
|
+
const apiKey = config?.apiKey || process.env[config?.apiKeyEnv || defaultKeyEnv] || '';
|
|
27
27
|
if (provider === 'openai-compatible') {
|
|
28
28
|
return new OpenAICompatibleBrain({
|
|
29
29
|
apiKey,
|
|
30
|
-
model: config
|
|
31
|
-
baseUrl: config
|
|
30
|
+
model: config?.model || 'local-model',
|
|
31
|
+
baseUrl: config?.baseUrl || 'http://127.0.0.1:1234/v1',
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
|
-
return new OpenRouterBrain({ apiKey, model: config
|
|
34
|
+
return new OpenRouterBrain({ apiKey, model: config?.model || 'gpt-4o-mini' });
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
function isDisabled(config
|
|
37
|
+
function isDisabled(config?: { provider?: string }): boolean {
|
|
38
38
|
return !config || config.provider === 'none';
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function createVoice(config
|
|
41
|
+
function createVoice(config?: VoiceConfig) {
|
|
42
42
|
return isDisabled(config)
|
|
43
43
|
? undefined
|
|
44
|
-
: new VoiceAdapter({
|
|
44
|
+
: new VoiceAdapter({
|
|
45
|
+
provider: (config?.provider as any) || 'voicevox',
|
|
46
|
+
baseUrl: config?.baseUrl || process.env.VOICEVOX_URL || 'http://localhost:50021',
|
|
47
|
+
speakerId: config?.speakerId || 1,
|
|
48
|
+
...config,
|
|
49
|
+
});
|
|
45
50
|
}
|
|
46
51
|
|
|
47
|
-
function createKnowledge(config
|
|
52
|
+
function createKnowledge(config?: EKnowledgeConfig) {
|
|
48
53
|
if (isDisabled(config)) return undefined;
|
|
49
|
-
if (!config?.packPath && !config?.registryUrl && !config?.baseUrl
|
|
54
|
+
if (!config?.packPath && !config?.registryUrl && !config?.baseUrl) {
|
|
50
55
|
return undefined;
|
|
51
56
|
}
|
|
52
|
-
return new EKnowledgeAdapter(config);
|
|
57
|
+
return new EKnowledgeAdapter(config || {});
|
|
53
58
|
}
|
|
54
59
|
|
|
55
|
-
function createVision(config
|
|
60
|
+
function createVision(config?: OpenRouterVisionConfig & { provider?: string }) {
|
|
56
61
|
return isDisabled(config)
|
|
57
62
|
? undefined
|
|
58
|
-
: new OpenRouterVisionAdapter({
|
|
63
|
+
: new OpenRouterVisionAdapter({
|
|
64
|
+
apiKey: config?.apiKey || process.env.OPENROUTER_API_KEY || '',
|
|
65
|
+
model: config?.model || 'gpt-4-vision',
|
|
66
|
+
...config,
|
|
67
|
+
});
|
|
59
68
|
}
|
|
60
69
|
|
|
61
|
-
function createBehavior(config
|
|
70
|
+
function createBehavior(config?: AppBehaviorConfig) {
|
|
62
71
|
return isDisabled(config) ? undefined : new ActiveSelfCompiler();
|
|
63
72
|
}
|
|
64
73
|
|
|
65
|
-
function createBody(config
|
|
74
|
+
function createBody(config?: Live2DAdapterConfig & { provider?: string }) {
|
|
66
75
|
return isDisabled(config)
|
|
67
76
|
? undefined
|
|
68
77
|
: new Live2DAdapter(config);
|
package/src/runtime.test.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { SiduriRuntime } from './runtime';
|
|
1
|
+
import { SiduriRuntime, dispatchCompanionChat } from './runtime';
|
|
2
2
|
import { DefaultHandsOrgan } from '@siduri-x/hands';
|
|
3
3
|
import { DefaultEarOrgan } from '@siduri-x/ear';
|
|
4
|
+
import { DefaultMouthOrgan } from '@siduri-x/mouth';
|
|
4
5
|
import { ActionPolicyEngine, RequestContext } from '@siduri-x/core';
|
|
5
6
|
|
|
6
7
|
describe('Siduri Runtime Orchestration', () => {
|
|
@@ -281,4 +282,57 @@ describe('Siduri Runtime Orchestration', () => {
|
|
|
281
282
|
/Ear text input exceeds maximum allowed length/
|
|
282
283
|
);
|
|
283
284
|
});
|
|
285
|
+
|
|
286
|
+
test('Decoupled Output Delivery: Brain decisions are delivered and formatted through MouthOrgan', async () => {
|
|
287
|
+
let deliveredOutput: any = null;
|
|
288
|
+
const mouth = new DefaultMouthOrgan({
|
|
289
|
+
channels: [
|
|
290
|
+
{
|
|
291
|
+
id: 'test-web-channel',
|
|
292
|
+
name: 'Web Channel',
|
|
293
|
+
medium: 'web',
|
|
294
|
+
deliver: async (out) => {
|
|
295
|
+
deliveredOutput = out;
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const mockBrain = {
|
|
302
|
+
generatePlan: async () => ({
|
|
303
|
+
speech: '**Hello** from Siduri!',
|
|
304
|
+
language: 'en',
|
|
305
|
+
}),
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const mockMemory = {
|
|
309
|
+
initialize: async () => {},
|
|
310
|
+
searchClaims: async () => [],
|
|
311
|
+
getDirectives: async () => [],
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const runtime = new SiduriRuntime(
|
|
315
|
+
'companion-mouth-test',
|
|
316
|
+
{ name: 'MouthCompanion' } as any,
|
|
317
|
+
{
|
|
318
|
+
brain: mockBrain as any,
|
|
319
|
+
memory: mockMemory as any,
|
|
320
|
+
mouth,
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
await runtime.initialize();
|
|
324
|
+
|
|
325
|
+
const response = await dispatchCompanionChat(runtime, {
|
|
326
|
+
message: 'Hi there',
|
|
327
|
+
role: 'OWNER',
|
|
328
|
+
medium: 'web',
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
expect(response.delivery).toBeDefined();
|
|
332
|
+
expect(response.delivery?.medium).toBe('web');
|
|
333
|
+
expect(response.delivery?.displayText).toBe('**Hello** from Siduri!');
|
|
334
|
+
expect(deliveredOutput).toBeDefined();
|
|
335
|
+
expect(deliveredOutput.medium).toBe('web');
|
|
336
|
+
expect(deliveredOutput.displayText).toBe('**Hello** from Siduri!');
|
|
337
|
+
});
|
|
284
338
|
});
|
package/dist/app.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { Express } from 'express';
|
|
2
|
-
import { SiduriRuntime } from './runtime';
|
|
3
|
-
import { FixtureObservationOrgan } from '@siduri-x/observation';
|
|
4
|
-
export interface AppInstance {
|
|
5
|
-
app: Express;
|
|
6
|
-
runtimes: Map<string, SiduriRuntime>;
|
|
7
|
-
setObservationOrgan: (org: FixtureObservationOrgan) => void;
|
|
8
|
-
}
|
|
9
|
-
export declare function createApp(runtimes?: Map<string, SiduriRuntime>): AppInstance;
|
package/dist/app.js
DELETED
|
@@ -1,480 +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 cors_2 = require("./cors");
|
|
10
|
-
const runtime_1 = require("./runtime");
|
|
11
|
-
const brain_1 = require("@siduri-x/brain");
|
|
12
|
-
const memory_1 = require("@siduri-x/memory");
|
|
13
|
-
const voice_1 = require("@siduri-x/voice");
|
|
14
|
-
const knowledge_1 = require("@siduri-x/knowledge");
|
|
15
|
-
const vision_1 = require("@siduri-x/vision");
|
|
16
|
-
const behavior_1 = require("@siduri-x/behavior");
|
|
17
|
-
const body_1 = require("@siduri-x/body");
|
|
18
|
-
const hands_1 = require("@siduri-x/hands");
|
|
19
|
-
const ear_1 = require("@siduri-x/ear");
|
|
20
|
-
const auth_1 = require("./auth");
|
|
21
|
-
const context_mapper_1 = require("./context-mapper");
|
|
22
|
-
function createApp(runtimes = new Map()) {
|
|
23
|
-
const app = (0, express_1.default)();
|
|
24
|
-
app.use((0, cors_1.default)((0, cors_2.createCorsOptions)()));
|
|
25
|
-
app.use(express_1.default.json());
|
|
26
|
-
let observationOrgan;
|
|
27
|
-
function createBrain(config) {
|
|
28
|
-
const provider = config.provider || 'openrouter';
|
|
29
|
-
const defaultKeyEnv = provider === 'openai-compatible' ? 'OPENAI_COMPATIBLE_API_KEY' : 'OPENROUTER_API_KEY';
|
|
30
|
-
const apiKey = config.apiKey || process.env[config.apiKeyEnv || defaultKeyEnv] || '';
|
|
31
|
-
if (provider === 'openai-compatible') {
|
|
32
|
-
return new brain_1.OpenAICompatibleBrain({
|
|
33
|
-
apiKey,
|
|
34
|
-
model: config.model || 'local-model',
|
|
35
|
-
baseUrl: config.baseUrl || 'http://127.0.0.1:1234/v1',
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
return new brain_1.OpenRouterBrain({ apiKey, model: config.model || 'gpt-4o-mini' });
|
|
39
|
-
}
|
|
40
|
-
function isDisabled(config) {
|
|
41
|
-
return !config || config.provider === 'none';
|
|
42
|
-
}
|
|
43
|
-
function createVoice(config) {
|
|
44
|
-
return isDisabled(config)
|
|
45
|
-
? undefined
|
|
46
|
-
: new voice_1.VoiceAdapter({ provider: config.provider || 'voicevox', baseUrl: process.env.VOICEVOX_URL || 'http://localhost:50021', speakerId: config.speakerId || 1 });
|
|
47
|
-
}
|
|
48
|
-
function createKnowledge(config) {
|
|
49
|
-
return isDisabled(config) ? undefined : new knowledge_1.EKnowledgeAdapter(config);
|
|
50
|
-
}
|
|
51
|
-
function createVision(config) {
|
|
52
|
-
return isDisabled(config)
|
|
53
|
-
? undefined
|
|
54
|
-
: new vision_1.OpenRouterVisionAdapter({ apiKey: process.env.OPENROUTER_API_KEY || '', model: config.model || 'gpt-4-vision' });
|
|
55
|
-
}
|
|
56
|
-
function createBehavior(config) {
|
|
57
|
-
return isDisabled(config) ? undefined : new behavior_1.ActiveSelfCompiler();
|
|
58
|
-
}
|
|
59
|
-
function createBody(config) {
|
|
60
|
-
return isDisabled(config)
|
|
61
|
-
? undefined
|
|
62
|
-
: new body_1.Live2DAdapter(config);
|
|
63
|
-
}
|
|
64
|
-
function createHands(config) {
|
|
65
|
-
return isDisabled(config)
|
|
66
|
-
? new hands_1.DefaultHandsOrgan()
|
|
67
|
-
: new hands_1.DefaultHandsOrgan(config);
|
|
68
|
-
}
|
|
69
|
-
function createEar(config) {
|
|
70
|
-
return isDisabled(config)
|
|
71
|
-
? new ear_1.DefaultEarOrgan()
|
|
72
|
-
: new ear_1.DefaultEarOrgan(config);
|
|
73
|
-
}
|
|
74
|
-
app.post('/boot', (0, auth_1.requireRole)(['OWNER']), async (req, res) => {
|
|
75
|
-
try {
|
|
76
|
-
const { id, config } = req.body;
|
|
77
|
-
if (runtimes.has(id)) {
|
|
78
|
-
return res.status(400).json({ error: "Already booted" });
|
|
79
|
-
}
|
|
80
|
-
const brain = createBrain(config.brain);
|
|
81
|
-
const memory = new memory_1.PostgresMemoryOrgan({ connectionString: process.env.DATABASE_URL || 'postgresql://postgres:postgres@localhost:5432/siduri' });
|
|
82
|
-
const voice = createVoice(config.voice);
|
|
83
|
-
const knowledge = createKnowledge(config.knowledge);
|
|
84
|
-
const vision = createVision(config.vision);
|
|
85
|
-
const behavior = createBehavior(config.behavior);
|
|
86
|
-
const body = createBody(config.body);
|
|
87
|
-
const hands = createHands(config.hands);
|
|
88
|
-
const ear = createEar(config.ear);
|
|
89
|
-
const runtime = new runtime_1.SiduriRuntime(id, config, { brain, memory, voice, knowledge, vision, behavior, body, hands, ear });
|
|
90
|
-
await runtime.initialize();
|
|
91
|
-
runtimes.set(id, runtime);
|
|
92
|
-
res.json({ success: true, id });
|
|
93
|
-
}
|
|
94
|
-
catch (e) {
|
|
95
|
-
res.status(500).json({ error: e.message });
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
// STATUS / HEALTH ENDPOINTS
|
|
99
|
-
app.get('/health', (req, res) => res.json({ status: "ok" }));
|
|
100
|
-
app.get('/version', (req, res) => res.json({ name: "siduri-x-api", version: "0.2.0-x" }));
|
|
101
|
-
app.get('/ready', (req, res) => {
|
|
102
|
-
const isReady = runtimes.size > 0;
|
|
103
|
-
res.status(isReady ? 200 : 503).json({
|
|
104
|
-
status: isReady ? "ready" : "not_ready",
|
|
105
|
-
companionCount: runtimes.size,
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
app.get('/voice/health', (req, res) => {
|
|
109
|
-
const hasVoice = Array.from(runtimes.values()).some((r) => Boolean(r.voice));
|
|
110
|
-
res.json({ provider: "siduri-voice", configured: hasVoice });
|
|
111
|
-
});
|
|
112
|
-
app.get('/obs/health', (req, res) => res.json({ connected: Boolean(observationOrgan) }));
|
|
113
|
-
app.get('/me', auth_1.attachIdentity, (req, res) => {
|
|
114
|
-
const identity = req.identity;
|
|
115
|
-
res.json({
|
|
116
|
-
actorId: identity.role === 'OWNER' ? 'owner-user' : 'anonymous-session',
|
|
117
|
-
role: identity.role,
|
|
118
|
-
authenticated: identity.role === 'OWNER',
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
app.put('/me', (0, auth_1.requireRole)(['OWNER']), (req, res) => res.json({ success: true }));
|
|
122
|
-
// CHAT (API context boundary validation)
|
|
123
|
-
app.post('/chat', auth_1.attachIdentity, async (req, res) => {
|
|
124
|
-
const { id, message, history } = req.body;
|
|
125
|
-
const identity = req.identity;
|
|
126
|
-
// Single-owner companion model: /chat defaults to OWNER identity.
|
|
127
|
-
// Explicit non-owner role in request body or context (e.g. adversarial test suites) is respected.
|
|
128
|
-
const effectiveRole = req.body?.role === 'VIEWER' || req.body?.context?.actor?.authorizationRole === 'viewer'
|
|
129
|
-
? 'VIEWER'
|
|
130
|
-
: (identity?.role === 'OPERATOR' ? 'OPERATOR' : 'OWNER');
|
|
131
|
-
const isOwner = effectiveRole === 'OWNER';
|
|
132
|
-
// Call context mapper at the API boundary
|
|
133
|
-
const mappingResult = (0, context_mapper_1.mapRequestContext)({
|
|
134
|
-
...req.body,
|
|
135
|
-
id: id || req.body.companionId,
|
|
136
|
-
role: effectiveRole,
|
|
137
|
-
authenticated: isOwner,
|
|
138
|
-
generateCorrelationId: true,
|
|
139
|
-
}, {
|
|
140
|
-
endpointPolicy: 'public',
|
|
141
|
-
defaultPublicAudience: 'audience-public',
|
|
142
|
-
});
|
|
143
|
-
if (!mappingResult.accepted) {
|
|
144
|
-
return res.status(400).json({
|
|
145
|
-
accepted: false,
|
|
146
|
-
error: mappingResult.error,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
const companionId = mappingResult.context.companionId;
|
|
150
|
-
const runtime = runtimes.get(companionId);
|
|
151
|
-
if (!runtime)
|
|
152
|
-
return res.status(404).json({ error: "Companion not found" });
|
|
153
|
-
try {
|
|
154
|
-
const response = await (0, runtime_1.dispatchCompanionChat)(runtime, {
|
|
155
|
-
id: companionId,
|
|
156
|
-
companionId,
|
|
157
|
-
message,
|
|
158
|
-
context: mappingResult.context,
|
|
159
|
-
history,
|
|
160
|
-
});
|
|
161
|
-
res.json(response);
|
|
162
|
-
}
|
|
163
|
-
catch (e) {
|
|
164
|
-
res.status(500).json({ error: e.message });
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
// MEMORY GETTERS
|
|
168
|
-
app.get('/memory/proposals', (0, auth_1.requireRole)(['OWNER', 'OPERATOR']), async (req, res) => {
|
|
169
|
-
const id = req.query.id || Array.from(runtimes.keys())[0];
|
|
170
|
-
const runtime = runtimes.get(id);
|
|
171
|
-
if (!runtime)
|
|
172
|
-
return res.status(404).json({ error: "Companion not found" });
|
|
173
|
-
if (!runtime.memory)
|
|
174
|
-
return res.json({ proposals: [] });
|
|
175
|
-
try {
|
|
176
|
-
const proposals = await runtime.memory.getPendingClaims();
|
|
177
|
-
res.json({ proposals });
|
|
178
|
-
}
|
|
179
|
-
catch (e) {
|
|
180
|
-
res.status(500).json({ error: e.message });
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
app.get('/memory', (0, auth_1.requireRole)(['OWNER', 'OPERATOR']), async (req, res) => {
|
|
184
|
-
const id = req.query.id || Array.from(runtimes.keys())[0];
|
|
185
|
-
const runtime = runtimes.get(id);
|
|
186
|
-
if (!runtime)
|
|
187
|
-
return res.status(404).json({ error: "Companion not found" });
|
|
188
|
-
if (!runtime.memory)
|
|
189
|
-
return res.json({ items: [] });
|
|
190
|
-
try {
|
|
191
|
-
const items = await runtime.memory.getClaims();
|
|
192
|
-
res.json({ items });
|
|
193
|
-
}
|
|
194
|
-
catch (e) {
|
|
195
|
-
res.status(500).json({ error: e.message });
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
app.get('/memory/claims', (0, auth_1.requireRole)(['OWNER', 'OPERATOR']), async (req, res) => {
|
|
199
|
-
const id = req.query.id || Array.from(runtimes.keys())[0];
|
|
200
|
-
const runtime = runtimes.get(id);
|
|
201
|
-
if (!runtime)
|
|
202
|
-
return res.status(404).json({ error: "Companion not found" });
|
|
203
|
-
if (!runtime.memory)
|
|
204
|
-
return res.json({ claims: [] });
|
|
205
|
-
try {
|
|
206
|
-
const claims = await runtime.memory.getClaims();
|
|
207
|
-
res.json({ claims });
|
|
208
|
-
}
|
|
209
|
-
catch (e) {
|
|
210
|
-
res.status(500).json({ error: e.message });
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
app.get('/memory/behavioral', (0, auth_1.requireRole)(['OWNER', 'OPERATOR']), async (req, res) => {
|
|
214
|
-
const id = req.query.id || Array.from(runtimes.keys())[0];
|
|
215
|
-
const runtime = runtimes.get(id);
|
|
216
|
-
if (!runtime)
|
|
217
|
-
return res.status(404).json({ error: "Companion not found" });
|
|
218
|
-
if (!runtime.memory)
|
|
219
|
-
return res.json({ directives: [] });
|
|
220
|
-
try {
|
|
221
|
-
const directives = await runtime.memory.getDirectives();
|
|
222
|
-
res.json({ directives });
|
|
223
|
-
}
|
|
224
|
-
catch (e) {
|
|
225
|
-
res.status(500).json({ error: e.message });
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
// MEMORY MUTATIONS - PROPOSALS
|
|
229
|
-
app.post('/memory/proposals/update', (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 || typeof runtime.memory.updateClaim !== 'function') {
|
|
235
|
-
return res.status(400).json({ error: "Memory organ does not support updating claims" });
|
|
236
|
-
}
|
|
237
|
-
try {
|
|
238
|
-
const claimId = req.body.id || req.body.claimId;
|
|
239
|
-
if (!claimId) {
|
|
240
|
-
return res.status(400).json({ error: "Missing required claim id" });
|
|
241
|
-
}
|
|
242
|
-
const updated = await runtime.memory.updateClaim(claimId, req.body.updates || req.body);
|
|
243
|
-
res.json({ success: true, claim: updated });
|
|
244
|
-
}
|
|
245
|
-
catch (e) {
|
|
246
|
-
res.status(500).json({ error: e.message });
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
app.post('/memory/proposals/approve', (0, auth_1.requireRole)(['OWNER', 'OPERATOR']), async (req, res) => {
|
|
250
|
-
const id = req.body.companionId || Array.from(runtimes.keys())[0];
|
|
251
|
-
const runtime = runtimes.get(id);
|
|
252
|
-
if (!runtime)
|
|
253
|
-
return res.status(404).json({ error: "Companion not found" });
|
|
254
|
-
if (!runtime.memory)
|
|
255
|
-
return res.status(400).json({ error: "Memory organ not configured" });
|
|
256
|
-
try {
|
|
257
|
-
await runtime.memory.approveClaim(req.body.id);
|
|
258
|
-
res.json({ approved: true });
|
|
259
|
-
}
|
|
260
|
-
catch (e) {
|
|
261
|
-
res.status(500).json({ error: e.message });
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
app.post('/memory/proposals/reject', (0, auth_1.requireRole)(['OWNER', 'OPERATOR']), async (req, res) => {
|
|
265
|
-
const id = req.body.companionId || Array.from(runtimes.keys())[0];
|
|
266
|
-
const runtime = runtimes.get(id);
|
|
267
|
-
if (!runtime)
|
|
268
|
-
return res.status(404).json({ error: "Companion not found" });
|
|
269
|
-
if (!runtime.memory)
|
|
270
|
-
return res.status(400).json({ error: "Memory organ not configured" });
|
|
271
|
-
try {
|
|
272
|
-
await runtime.memory.rejectClaim(req.body.id);
|
|
273
|
-
res.json({ rejected: true });
|
|
274
|
-
}
|
|
275
|
-
catch (e) {
|
|
276
|
-
res.status(500).json({ error: e.message });
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
// MEMORY MUTATIONS - BEHAVIORAL
|
|
280
|
-
app.post('/memory/behavioral/approve', (0, auth_1.requireRole)(['OWNER']), async (req, res) => {
|
|
281
|
-
const id = req.body.companionId || Array.from(runtimes.keys())[0];
|
|
282
|
-
const runtime = runtimes.get(id);
|
|
283
|
-
if (!runtime)
|
|
284
|
-
return res.status(404).json({ error: "Companion not found" });
|
|
285
|
-
if (!runtime.memory)
|
|
286
|
-
return res.status(400).json({ error: "Memory organ not configured" });
|
|
287
|
-
try {
|
|
288
|
-
await runtime.memory.approveDirective(req.body.id);
|
|
289
|
-
res.json({ approved: true });
|
|
290
|
-
}
|
|
291
|
-
catch (e) {
|
|
292
|
-
res.status(500).json({ error: e.message });
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
app.post('/memory/behavioral/reject', (0, auth_1.requireRole)(['OWNER']), async (req, res) => {
|
|
296
|
-
const id = req.body.companionId || Array.from(runtimes.keys())[0];
|
|
297
|
-
const runtime = runtimes.get(id);
|
|
298
|
-
if (!runtime)
|
|
299
|
-
return res.status(404).json({ error: "Companion not found" });
|
|
300
|
-
if (!runtime.memory)
|
|
301
|
-
return res.status(400).json({ error: "Memory organ not configured" });
|
|
302
|
-
try {
|
|
303
|
-
await runtime.memory.rejectDirective(req.body.id);
|
|
304
|
-
res.json({ rejected: true });
|
|
305
|
-
}
|
|
306
|
-
catch (e) {
|
|
307
|
-
res.status(500).json({ error: e.message });
|
|
308
|
-
}
|
|
309
|
-
});
|
|
310
|
-
app.post('/memory/behavioral/revoke', (0, auth_1.requireRole)(['OWNER']), async (req, res) => {
|
|
311
|
-
const id = req.body.companionId || Array.from(runtimes.keys())[0];
|
|
312
|
-
const runtime = runtimes.get(id);
|
|
313
|
-
if (!runtime)
|
|
314
|
-
return res.status(404).json({ error: "Companion not found" });
|
|
315
|
-
if (!runtime.memory)
|
|
316
|
-
return res.status(400).json({ error: "Memory organ not configured" });
|
|
317
|
-
try {
|
|
318
|
-
await runtime.memory.revokeDirective(req.body.id);
|
|
319
|
-
res.json({ revoked: true });
|
|
320
|
-
}
|
|
321
|
-
catch (e) {
|
|
322
|
-
res.status(500).json({ error: e.message });
|
|
323
|
-
}
|
|
324
|
-
});
|
|
325
|
-
app.post('/memory/behavioral/disable', (0, auth_1.requireRole)(['OWNER']), async (req, res) => {
|
|
326
|
-
const id = req.body.companionId || Array.from(runtimes.keys())[0];
|
|
327
|
-
const runtime = runtimes.get(id);
|
|
328
|
-
if (!runtime)
|
|
329
|
-
return res.status(404).json({ error: "Companion not found" });
|
|
330
|
-
if (!runtime.memory)
|
|
331
|
-
return res.status(400).json({ error: "Memory organ not configured" });
|
|
332
|
-
try {
|
|
333
|
-
await runtime.memory.disableDirective(req.body.id);
|
|
334
|
-
res.json({ disabled: true });
|
|
335
|
-
}
|
|
336
|
-
catch (e) {
|
|
337
|
-
res.status(500).json({ error: e.message });
|
|
338
|
-
}
|
|
339
|
-
});
|
|
340
|
-
const isDevMode = process.env.NODE_ENV !== 'production' || process.env.SIDURI_DEV_MODE === 'true';
|
|
341
|
-
if (isDevMode) {
|
|
342
|
-
app.post('/dev/memory/reset', (0, auth_1.requireRole)(['OWNER']), async (req, res) => {
|
|
343
|
-
const id = req.body.companionId || Array.from(runtimes.keys())[0];
|
|
344
|
-
const runtime = runtimes.get(id);
|
|
345
|
-
if (!runtime)
|
|
346
|
-
return res.status(404).json({ error: "Companion not found" });
|
|
347
|
-
if (!runtime.memory || typeof runtime.memory.resetMemory !== 'function') {
|
|
348
|
-
return res.status(400).json({ error: "Memory organ does not support reset" });
|
|
349
|
-
}
|
|
350
|
-
try {
|
|
351
|
-
await runtime.memory.resetMemory();
|
|
352
|
-
res.json({ reset: true });
|
|
353
|
-
}
|
|
354
|
-
catch (e) {
|
|
355
|
-
res.status(500).json({ error: e.message });
|
|
356
|
-
}
|
|
357
|
-
});
|
|
358
|
-
app.post('/dev/mock-response', async (req, res) => {
|
|
359
|
-
const companionId = req.body?.companionId || Array.from(runtimes.keys())[0] || 'default';
|
|
360
|
-
const runtime = runtimes.get(companionId);
|
|
361
|
-
if (!runtime)
|
|
362
|
-
return res.status(404).json({ accepted: false, error: 'Companion not found' });
|
|
363
|
-
const staged = runtime.gating.stageResponse({
|
|
364
|
-
requestContext: {
|
|
365
|
-
companionId,
|
|
366
|
-
actor: {
|
|
367
|
-
actorId: 'operator-a',
|
|
368
|
-
sessionId: 'sess-op',
|
|
369
|
-
authorizationRole: 'operator',
|
|
370
|
-
capabilities: ['chat:public', 'memory:approve'],
|
|
371
|
-
authenticated: true,
|
|
372
|
-
},
|
|
373
|
-
conversation: {
|
|
374
|
-
channel: 'public',
|
|
375
|
-
audienceId: 'audience-public',
|
|
376
|
-
correlationId: req.body?.correlation_id || `corr-${Date.now()}`,
|
|
377
|
-
},
|
|
378
|
-
},
|
|
379
|
-
candidateSpeech: req.body?.speech || 'Mocked staged response for review',
|
|
380
|
-
candidateLanguage: req.body?.language || 'en',
|
|
381
|
-
requiresApproval: req.body?.requiresApproval ?? true,
|
|
382
|
-
});
|
|
383
|
-
res.json({
|
|
384
|
-
accepted: true,
|
|
385
|
-
staged: true,
|
|
386
|
-
status: staged.status,
|
|
387
|
-
response_id: staged.responseId,
|
|
388
|
-
correlation_id: staged.correlationId,
|
|
389
|
-
speech: staged.speech,
|
|
390
|
-
});
|
|
391
|
-
});
|
|
392
|
-
app.post('/dev/approve-response', async (req, res) => {
|
|
393
|
-
const companionId = req.body?.companionId || Array.from(runtimes.keys())[0] || 'default';
|
|
394
|
-
const runtime = runtimes.get(companionId);
|
|
395
|
-
if (!runtime)
|
|
396
|
-
return res.status(404).json({ approved: false, error: 'Companion not found' });
|
|
397
|
-
let responseId = req.body?.responseId;
|
|
398
|
-
let correlationId = req.body?.correlation_id;
|
|
399
|
-
if (!responseId && correlationId) {
|
|
400
|
-
const found = runtime.gating.findStagedPlanByCorrelation(companionId, correlationId);
|
|
401
|
-
if (found) {
|
|
402
|
-
responseId = found.responseId;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
else if (responseId && !correlationId) {
|
|
406
|
-
const found = runtime.gating.getStagedPlan(responseId);
|
|
407
|
-
if (found) {
|
|
408
|
-
correlationId = found.correlationId;
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
if (!responseId) {
|
|
412
|
-
return res.status(400).json({ approved: false, error: 'UNKNOWN_APPROVAL_ID' });
|
|
413
|
-
}
|
|
414
|
-
const result = runtime.gating.approveResponse({
|
|
415
|
-
responseId,
|
|
416
|
-
companionId,
|
|
417
|
-
correlationId: correlationId || '',
|
|
418
|
-
audienceId: req.body?.audienceId,
|
|
419
|
-
});
|
|
420
|
-
if (!result.success) {
|
|
421
|
-
return res.status(400).json({ approved: false, error: result.reason });
|
|
422
|
-
}
|
|
423
|
-
// Now evaluated as approved
|
|
424
|
-
const evaluation = runtime.gating.evaluateGate(result.plan);
|
|
425
|
-
res.json({
|
|
426
|
-
approved: true,
|
|
427
|
-
status: evaluation.disposition,
|
|
428
|
-
response_id: result.plan.responseId,
|
|
429
|
-
speech: result.plan.speech,
|
|
430
|
-
language: result.plan.language,
|
|
431
|
-
});
|
|
432
|
-
});
|
|
433
|
-
app.post('/dev/reject-response', async (req, res) => {
|
|
434
|
-
const companionId = req.body?.companionId || Array.from(runtimes.keys())[0] || 'default';
|
|
435
|
-
const runtime = runtimes.get(companionId);
|
|
436
|
-
if (!runtime)
|
|
437
|
-
return res.status(404).json({ rejected: false, error: 'Companion not found' });
|
|
438
|
-
let responseId = req.body?.responseId;
|
|
439
|
-
let correlationId = req.body?.correlation_id;
|
|
440
|
-
if (!responseId && correlationId) {
|
|
441
|
-
const found = runtime.gating.findStagedPlanByCorrelation(companionId, correlationId);
|
|
442
|
-
if (found) {
|
|
443
|
-
responseId = found.responseId;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
else if (responseId && !correlationId) {
|
|
447
|
-
const found = runtime.gating.getStagedPlan(responseId);
|
|
448
|
-
if (found) {
|
|
449
|
-
correlationId = found.correlationId;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
if (!responseId) {
|
|
453
|
-
return res.status(400).json({ rejected: false, error: 'UNKNOWN_APPROVAL_ID' });
|
|
454
|
-
}
|
|
455
|
-
const result = runtime.gating.rejectResponse({
|
|
456
|
-
responseId,
|
|
457
|
-
companionId,
|
|
458
|
-
correlationId: correlationId || '',
|
|
459
|
-
reason: req.body?.reason,
|
|
460
|
-
});
|
|
461
|
-
if (!result.success) {
|
|
462
|
-
return res.status(400).json({ rejected: false, error: result.reason });
|
|
463
|
-
}
|
|
464
|
-
res.json({
|
|
465
|
-
rejected: true,
|
|
466
|
-
status: result.plan.status,
|
|
467
|
-
response_id: result.plan.responseId,
|
|
468
|
-
});
|
|
469
|
-
});
|
|
470
|
-
app.post('/dev/mock-observation', async (req, res) => {
|
|
471
|
-
if (!observationOrgan)
|
|
472
|
-
return res.status(503).json({ accepted: false, reason: 'observation_unavailable' });
|
|
473
|
-
const result = await observationOrgan.ingest(new Uint8Array([115, 121, 110, 116, 104, 101, 116, 105, 99]), 'fixture-observation', 'configured-vision');
|
|
474
|
-
if (!result.observation)
|
|
475
|
-
return res.status(result.duplicate ? 200 : 409).json({ accepted: false, ...result });
|
|
476
|
-
res.status(202).json({ accepted: true, observation: result.observation });
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
return { app, runtimes, setObservationOrgan: (org) => { observationOrgan = org; } };
|
|
480
|
-
}
|
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;
|