@siduri-x/api 1.0.5 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-test.log +43 -0
- package/dist/app.d.ts +2 -34
- package/dist/app.js +208 -127
- package/dist/b0-b6.test.js +1 -1
- package/dist/boot.d.ts +92 -0
- package/dist/boot.js +146 -0
- package/dist/boot.test.d.ts +1 -0
- package/dist/boot.test.js +84 -0
- package/dist/context-mapper.d.ts +3 -1
- package/dist/context-mapper.js +84 -13
- package/dist/context-mapper.test.js +116 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +13 -88
- package/dist/index.test.js +4 -4
- package/dist/knowledge.test.d.ts +1 -0
- package/dist/knowledge.test.js +138 -0
- package/dist/t4-gating.test.js +1 -1
- package/dist/t5-experience.test.js +1 -1
- package/dist/t6-security.test.js +115 -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 +223 -170
- package/src/b0-b6.test.ts +1 -1
- package/src/boot.test.ts +103 -0
- package/src/boot.ts +190 -0
- package/src/context-mapper.test.ts +127 -0
- package/src/context-mapper.ts +94 -13
- package/src/index.test.ts +4 -4
- package/src/index.ts +14 -99
- package/src/knowledge.test.ts +156 -0
- package/src/t4-gating.test.ts +1 -1
- package/src/t5-experience.test.ts +1 -1
- package/src/t6-security.test.ts +126 -2
- package/src/t7-release.test.ts +1 -1
- package/src/teach-mode.test.ts +152 -0
package/package.json
CHANGED
|
@@ -1,36 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@siduri-x/api",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
7
|
"main": "dist/index.js",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22.16.0"
|
|
10
|
+
},
|
|
8
11
|
"dependencies": {
|
|
9
|
-
"cors": "^2.8.
|
|
10
|
-
"dotenv": "^
|
|
11
|
-
"express": "^
|
|
12
|
-
"@siduri-x/
|
|
13
|
-
"@siduri-x/
|
|
14
|
-
"@siduri-x/
|
|
15
|
-
"@siduri-x/
|
|
16
|
-
"@siduri-x/
|
|
17
|
-
"@siduri-x/hands": "
|
|
18
|
-
"@siduri-x/knowledge": "
|
|
19
|
-
"@siduri-x/memory": "
|
|
20
|
-
"@siduri-x/
|
|
21
|
-
"@siduri-x/
|
|
22
|
-
"@siduri-x/
|
|
23
|
-
"@siduri-x/
|
|
12
|
+
"cors": "^2.8.6",
|
|
13
|
+
"dotenv": "^17.4.2",
|
|
14
|
+
"express": "^5.2.1",
|
|
15
|
+
"@siduri-x/body": "2.0.1",
|
|
16
|
+
"@siduri-x/brain": "2.0.1",
|
|
17
|
+
"@siduri-x/core": "2.0.2",
|
|
18
|
+
"@siduri-x/ear": "2.0.1",
|
|
19
|
+
"@siduri-x/eknowledge": "2.0.1",
|
|
20
|
+
"@siduri-x/hands": "2.0.1",
|
|
21
|
+
"@siduri-x/knowledge": "2.0.1",
|
|
22
|
+
"@siduri-x/memory": "2.0.1",
|
|
23
|
+
"@siduri-x/mouth": "2.0.1",
|
|
24
|
+
"@siduri-x/observation": "2.0.1",
|
|
25
|
+
"@siduri-x/self": "2.0.1",
|
|
26
|
+
"@siduri-x/vision": "2.0.1",
|
|
27
|
+
"@siduri-x/voice": "2.0.1"
|
|
24
28
|
},
|
|
25
29
|
"devDependencies": {
|
|
26
|
-
"@types/cors": "^2.8.
|
|
27
|
-
"@types/express": "^
|
|
28
|
-
"@types/jest": "^
|
|
29
|
-
"@types/supertest": "^
|
|
30
|
-
"jest": "^
|
|
31
|
-
"supertest": "^
|
|
30
|
+
"@types/cors": "^2.8.19",
|
|
31
|
+
"@types/express": "^5.0.6",
|
|
32
|
+
"@types/jest": "^30.0.0",
|
|
33
|
+
"@types/supertest": "^7.2.1",
|
|
34
|
+
"jest": "^30.5.1",
|
|
35
|
+
"supertest": "^7.2.2",
|
|
32
36
|
"ts-jest": "^29.4.12",
|
|
33
|
-
"typescript": "^5.
|
|
37
|
+
"typescript": "^5.9.3"
|
|
34
38
|
},
|
|
35
39
|
"scripts": {
|
|
36
40
|
"build": "tsc",
|
package/src/app.ts
CHANGED
|
@@ -2,49 +2,25 @@ import express, { Express } from 'express';
|
|
|
2
2
|
import cors from 'cors';
|
|
3
3
|
import { createCorsOptions } from './cors';
|
|
4
4
|
import { SiduriRuntime, dispatchCompanionChat } from './runtime';
|
|
5
|
-
import {
|
|
6
|
-
import { PostgresMemoryOrgan } from '@siduri-x/memory';
|
|
7
|
-
import { VoiceAdapter, VoiceConfig } from '@siduri-x/voice';
|
|
8
|
-
import { EKnowledgeAdapter, EKnowledgeConfig } from '@siduri-x/knowledge';
|
|
9
|
-
import { OpenRouterVisionAdapter, OpenRouterVisionConfig } from '@siduri-x/vision';
|
|
10
|
-
import { ActiveSelfCompiler } from '@siduri-x/behavior';
|
|
11
|
-
import { Live2DAdapter, Live2DAdapterConfig } from '@siduri-x/body';
|
|
5
|
+
import { SelfPackageParser, SqliteSelfRepository, scanDirective } from '@siduri-x/self';
|
|
12
6
|
import { FixtureObservationOrgan } from '@siduri-x/observation';
|
|
13
|
-
import {
|
|
14
|
-
import { DefaultEarOrgan, EarOrganConfig } from '@siduri-x/ear';
|
|
15
|
-
import { DefaultMouthOrgan, DefaultMouthOrganConfig } from '@siduri-x/mouth';
|
|
16
|
-
import { attachIdentity, requireAuth, Identity } from './auth';
|
|
7
|
+
import { attachIdentity, requireAuth, Identity, isLocalRequest } from './auth';
|
|
17
8
|
import { mapRequestContext } from './context-mapper';
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface AppBootCompanionConfig {
|
|
36
|
-
name: string;
|
|
37
|
-
brain?: AppBrainConfig;
|
|
38
|
-
voice?: VoiceConfig;
|
|
39
|
-
knowledge?: EKnowledgeConfig;
|
|
40
|
-
vision?: OpenRouterVisionConfig;
|
|
41
|
-
behavior?: AppBehaviorConfig;
|
|
42
|
-
body?: Live2DAdapterConfig;
|
|
43
|
-
hands?: DefaultHandsOrganConfig;
|
|
44
|
-
ear?: EarOrganConfig;
|
|
45
|
-
mouth?: DefaultMouthOrganConfig;
|
|
46
|
-
[key: string]: unknown;
|
|
47
|
-
}
|
|
9
|
+
import {
|
|
10
|
+
bootCompanion,
|
|
11
|
+
AppBrainConfig,
|
|
12
|
+
AppBehaviorConfig,
|
|
13
|
+
AppBootCompanionConfig,
|
|
14
|
+
BootCompanionOptions,
|
|
15
|
+
} from './boot';
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
AppBrainConfig,
|
|
19
|
+
AppBehaviorConfig,
|
|
20
|
+
AppBootCompanionConfig,
|
|
21
|
+
BootCompanionOptions,
|
|
22
|
+
bootCompanion,
|
|
23
|
+
};
|
|
48
24
|
|
|
49
25
|
export interface AppInstance {
|
|
50
26
|
app: Express;
|
|
@@ -59,77 +35,6 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
59
35
|
|
|
60
36
|
let observationOrgan: FixtureObservationOrgan | undefined;
|
|
61
37
|
|
|
62
|
-
function createBrain(config?: AppBrainConfig) {
|
|
63
|
-
const provider = config?.provider || 'openrouter';
|
|
64
|
-
const defaultKeyEnv = provider === 'openai-compatible' ? 'OPENAI_COMPATIBLE_API_KEY' : 'OPENROUTER_API_KEY';
|
|
65
|
-
const apiKey = config?.apiKey || process.env[config?.apiKeyEnv || defaultKeyEnv] || '';
|
|
66
|
-
if (provider === 'openai-compatible') {
|
|
67
|
-
return new OpenAICompatibleBrain({
|
|
68
|
-
apiKey,
|
|
69
|
-
model: config?.model || 'local-model',
|
|
70
|
-
baseUrl: config?.baseUrl || 'http://127.0.0.1:1234/v1',
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
return new OpenRouterBrain({ apiKey, model: config?.model || 'gpt-4o-mini' });
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function isDisabled(config?: { provider?: string }): boolean {
|
|
77
|
-
return !config || config.provider === 'none';
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function createVoice(config?: VoiceConfig) {
|
|
81
|
-
return isDisabled(config)
|
|
82
|
-
? undefined
|
|
83
|
-
: new VoiceAdapter({
|
|
84
|
-
provider: (config?.provider as any) || 'voicevox',
|
|
85
|
-
baseUrl: config?.baseUrl || process.env.VOICEVOX_URL || 'http://localhost:50021',
|
|
86
|
-
speakerId: config?.speakerId || 1,
|
|
87
|
-
...config,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function createKnowledge(config?: EKnowledgeConfig) {
|
|
92
|
-
return isDisabled(config) ? undefined : new EKnowledgeAdapter(config || {});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function createVision(config?: OpenRouterVisionConfig & { provider?: string }) {
|
|
96
|
-
return isDisabled(config)
|
|
97
|
-
? undefined
|
|
98
|
-
: new OpenRouterVisionAdapter({
|
|
99
|
-
apiKey: config?.apiKey || process.env.OPENROUTER_API_KEY || '',
|
|
100
|
-
model: config?.model || 'gpt-4-vision',
|
|
101
|
-
...config,
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function createBehavior(config?: AppBehaviorConfig) {
|
|
106
|
-
return isDisabled(config) ? undefined : new ActiveSelfCompiler();
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function createBody(config?: Live2DAdapterConfig & { provider?: string }) {
|
|
110
|
-
return isDisabled(config)
|
|
111
|
-
? undefined
|
|
112
|
-
: new Live2DAdapter(config);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function createHands(config?: DefaultHandsOrganConfig & { provider?: string }) {
|
|
116
|
-
return isDisabled(config)
|
|
117
|
-
? new DefaultHandsOrgan()
|
|
118
|
-
: new DefaultHandsOrgan(config);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function createEar(config?: EarOrganConfig & { provider?: string }) {
|
|
122
|
-
return isDisabled(config)
|
|
123
|
-
? new DefaultEarOrgan()
|
|
124
|
-
: new DefaultEarOrgan(config);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function createMouth(config?: DefaultMouthOrganConfig & { provider?: string }, voice?: any) {
|
|
128
|
-
return isDisabled(config)
|
|
129
|
-
? new DefaultMouthOrgan({ voice })
|
|
130
|
-
: new DefaultMouthOrgan({ ...config, voice });
|
|
131
|
-
}
|
|
132
|
-
|
|
133
38
|
app.post('/boot', requireAuth, async (req, res) => {
|
|
134
39
|
try {
|
|
135
40
|
const { id, config } = req.body;
|
|
@@ -137,32 +42,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
137
42
|
return res.status(400).json({ error: "Already booted" });
|
|
138
43
|
}
|
|
139
44
|
|
|
140
|
-
const
|
|
141
|
-
const memory = new PostgresMemoryOrgan({ connectionString: process.env.DATABASE_URL || 'postgresql://postgres:postgres@localhost:5432/siduri' });
|
|
142
|
-
const voice = createVoice(config.voice);
|
|
143
|
-
const knowledge = createKnowledge(config.knowledge);
|
|
144
|
-
const vision = createVision(config.vision);
|
|
145
|
-
const behavior = createBehavior(config.behavior);
|
|
146
|
-
const body = createBody(config.body);
|
|
147
|
-
const hands = createHands(config.hands);
|
|
148
|
-
const ear = createEar(config.ear);
|
|
149
|
-
const mouth = createMouth(config.mouth, voice);
|
|
150
|
-
|
|
151
|
-
const runtime = new SiduriRuntime(id, config, {
|
|
152
|
-
brain,
|
|
153
|
-
memory,
|
|
154
|
-
voice,
|
|
155
|
-
knowledge,
|
|
156
|
-
vision,
|
|
157
|
-
behavior,
|
|
158
|
-
body,
|
|
159
|
-
hands,
|
|
160
|
-
ear,
|
|
161
|
-
mouth,
|
|
162
|
-
observation: observationOrgan,
|
|
163
|
-
});
|
|
164
|
-
await runtime.initialize();
|
|
165
|
-
|
|
45
|
+
const runtime = await bootCompanion(id, config, { observationOrgan });
|
|
166
46
|
runtimes.set(id, runtime);
|
|
167
47
|
|
|
168
48
|
res.json({ success: true, id });
|
|
@@ -211,17 +91,90 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
211
91
|
});
|
|
212
92
|
app.put('/me', requireAuth, (req, res) => res.json({ success: true }));
|
|
213
93
|
|
|
94
|
+
// TEACH MODE ENDPOINTS
|
|
95
|
+
app.post('/teach/upload-self', requireAuth, async (req, res) => {
|
|
96
|
+
try {
|
|
97
|
+
const { content } = req.body;
|
|
98
|
+
if (!content) return res.status(400).json({ error: "Missing content" });
|
|
99
|
+
const parsed = SelfPackageParser.parse(content);
|
|
100
|
+
res.json(parsed);
|
|
101
|
+
} catch (e: any) {
|
|
102
|
+
res.status(500).json({ error: e.message });
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
app.post('/teach/install-self', requireAuth, async (req, res) => {
|
|
107
|
+
try {
|
|
108
|
+
const { companionId, manifest, approvedDirectiveIds } = req.body;
|
|
109
|
+
if (!companionId || !manifest || !Array.isArray(approvedDirectiveIds)) {
|
|
110
|
+
return res.status(400).json({ error: "Missing required fields" });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!manifest.identity || !manifest.identity.name) {
|
|
114
|
+
return res.status(400).json({ error: "Invalid manifest: missing identity.name" });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const directivesToCommit = manifest.directives?.filter((d: any) => approvedDirectiveIds.includes(d.id)) || [];
|
|
118
|
+
for (const d of directivesToCommit) {
|
|
119
|
+
if (!d || typeof d.directive !== 'string') {
|
|
120
|
+
return res.status(400).json({ error: "Invalid directive entry: missing directive string" });
|
|
121
|
+
}
|
|
122
|
+
const scan = scanDirective(d.directive);
|
|
123
|
+
if (!scan.safe) {
|
|
124
|
+
return res.status(400).json({
|
|
125
|
+
error: `Safety check failed for directive: ${scan.reason}`,
|
|
126
|
+
directiveId: d.id,
|
|
127
|
+
reason: scan.reason,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const repo = new SqliteSelfRepository({ dbPath: process.env.STORAGE_PATH || process.env.SQLITE_DB_PATH || 'siduri.sqlite' });
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
await repo.setIdentity({
|
|
136
|
+
companionId,
|
|
137
|
+
name: manifest.identity.name,
|
|
138
|
+
archetype: manifest.identity.archetype,
|
|
139
|
+
version: manifest.version || '1.0.0',
|
|
140
|
+
updatedAt: new Date().toISOString(),
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (manifest.personality) {
|
|
144
|
+
await repo.setPersonality(companionId, manifest.personality);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (directivesToCommit.length > 0) {
|
|
148
|
+
await repo.commitDirectives(companionId, directivesToCommit);
|
|
149
|
+
}
|
|
150
|
+
} finally {
|
|
151
|
+
repo.close();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
res.json({ success: true });
|
|
155
|
+
} catch (e: any) {
|
|
156
|
+
res.status(500).json({ error: e.message });
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
214
160
|
// CHAT (API context boundary validation)
|
|
215
161
|
app.post('/chat', attachIdentity, async (req, res) => {
|
|
216
162
|
const { id, message, history } = req.body;
|
|
217
163
|
const identity = (req as any).identity as Identity;
|
|
164
|
+
const local = isLocalRequest(req);
|
|
165
|
+
const hasConfiguredToken = Boolean(process.env.AUTH_TOKEN || process.env.OPERATOR_TOKEN || process.env.OWNER_TOKEN);
|
|
166
|
+
const isLocalDirectOwner = local && !hasConfiguredToken;
|
|
218
167
|
|
|
219
|
-
|
|
168
|
+
const authenticated = identity?.authenticated || isLocalDirectOwner;
|
|
169
|
+
const serverRole = identity?.authenticated ? identity.role : (isLocalDirectOwner ? 'OWNER' : 'VIEWER');
|
|
170
|
+
|
|
171
|
+
// Single-owner companion model: map request context directly with server identity enforcement
|
|
220
172
|
const mappingResult = mapRequestContext({
|
|
221
173
|
...req.body,
|
|
222
174
|
id: id || req.body.companionId,
|
|
223
|
-
authenticated
|
|
224
|
-
|
|
175
|
+
authenticated,
|
|
176
|
+
serverRole,
|
|
177
|
+
source: identity?.source ?? (local ? 'local' : 'external'),
|
|
225
178
|
generateCorrelationId: true,
|
|
226
179
|
});
|
|
227
180
|
|
|
@@ -255,12 +208,19 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
255
208
|
app.post('/chat/stream', attachIdentity, async (req, res) => {
|
|
256
209
|
const { id, message, history } = req.body;
|
|
257
210
|
const identity = (req as any).identity as Identity;
|
|
211
|
+
const local = isLocalRequest(req);
|
|
212
|
+
const hasConfiguredToken = Boolean(process.env.AUTH_TOKEN || process.env.OPERATOR_TOKEN || process.env.OWNER_TOKEN);
|
|
213
|
+
const isLocalDirectOwner = local && !hasConfiguredToken;
|
|
214
|
+
|
|
215
|
+
const authenticated = identity?.authenticated || isLocalDirectOwner;
|
|
216
|
+
const serverRole = identity?.authenticated ? identity.role : (isLocalDirectOwner ? 'OWNER' : 'VIEWER');
|
|
258
217
|
|
|
259
218
|
const mappingResult = mapRequestContext({
|
|
260
219
|
...req.body,
|
|
261
220
|
id: id || req.body.companionId,
|
|
262
|
-
authenticated
|
|
263
|
-
|
|
221
|
+
authenticated,
|
|
222
|
+
serverRole,
|
|
223
|
+
source: identity?.source ?? (local ? 'local' : 'external'),
|
|
264
224
|
generateCorrelationId: true,
|
|
265
225
|
});
|
|
266
226
|
|
|
@@ -283,8 +243,8 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
283
243
|
const abortController = new AbortController();
|
|
284
244
|
const onClose = () => {
|
|
285
245
|
abortController.abort('client_disconnect');
|
|
286
|
-
if (typeof runtime.
|
|
287
|
-
runtime.
|
|
246
|
+
if (runtime.mouth && typeof runtime.mouth.interrupt === 'function') {
|
|
247
|
+
runtime.mouth.interrupt('client_disconnect');
|
|
288
248
|
}
|
|
289
249
|
};
|
|
290
250
|
req.on('close', onClose);
|
|
@@ -309,7 +269,9 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
309
269
|
res.write(`event: avatar\ndata: ${JSON.stringify(avatarEvent)}\n\n`);
|
|
310
270
|
}
|
|
311
271
|
|
|
312
|
-
const
|
|
272
|
+
const maxStreamLen = Number(process.env.SIDURI_MAX_RESPONSE_CHARS || 8000);
|
|
273
|
+
const rawSpeechText = response.delivery?.text || response.response?.subtitle_en || response.response?.spoken_ja || '';
|
|
274
|
+
const speechText = rawSpeechText.slice(0, maxStreamLen);
|
|
313
275
|
const utterance = {
|
|
314
276
|
utteranceId: response.response_id || 'utt-stream',
|
|
315
277
|
companionId,
|
|
@@ -355,12 +317,16 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
355
317
|
const reason = req.body?.reason || 'user_barge_in';
|
|
356
318
|
|
|
357
319
|
if (runtime) {
|
|
358
|
-
runtime.
|
|
320
|
+
if (runtime.mouth && typeof runtime.mouth.interrupt === 'function') {
|
|
321
|
+
runtime.mouth.interrupt(reason);
|
|
322
|
+
}
|
|
359
323
|
return res.json({ success: true, interrupted: true, companionId, reason });
|
|
360
324
|
}
|
|
361
325
|
|
|
362
326
|
for (const r of runtimes.values()) {
|
|
363
|
-
r.
|
|
327
|
+
if (r.mouth && typeof r.mouth.interrupt === 'function') {
|
|
328
|
+
r.mouth.interrupt(reason);
|
|
329
|
+
}
|
|
364
330
|
}
|
|
365
331
|
return res.json({ success: true, interrupted: true, reason });
|
|
366
332
|
});
|
|
@@ -371,12 +337,16 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
371
337
|
const reason = req.body?.reason || 'user_barge_in';
|
|
372
338
|
|
|
373
339
|
if (runtime) {
|
|
374
|
-
runtime.
|
|
340
|
+
if (runtime.mouth && typeof runtime.mouth.interrupt === 'function') {
|
|
341
|
+
runtime.mouth.interrupt(reason);
|
|
342
|
+
}
|
|
375
343
|
return res.json({ success: true, interrupted: true, companionId, reason });
|
|
376
344
|
}
|
|
377
345
|
|
|
378
346
|
for (const r of runtimes.values()) {
|
|
379
|
-
r.
|
|
347
|
+
if (r.mouth && typeof r.mouth.interrupt === 'function') {
|
|
348
|
+
r.mouth.interrupt(reason);
|
|
349
|
+
}
|
|
380
350
|
}
|
|
381
351
|
return res.json({ success: true, interrupted: true, reason });
|
|
382
352
|
});
|
|
@@ -388,7 +358,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
388
358
|
if (!runtime) return res.status(404).json({ error: "Companion not found" });
|
|
389
359
|
if (!runtime.memory) return res.json({ proposals: [] });
|
|
390
360
|
try {
|
|
391
|
-
const proposals = await runtime.getPendingClaims();
|
|
361
|
+
const proposals = await runtime.memory.getPendingClaims();
|
|
392
362
|
res.json({ proposals });
|
|
393
363
|
} catch (e: any) {
|
|
394
364
|
res.status(500).json({ error: e.message });
|
|
@@ -401,7 +371,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
401
371
|
if (!runtime) return res.status(404).json({ error: "Companion not found" });
|
|
402
372
|
if (!runtime.memory) return res.json({ items: [] });
|
|
403
373
|
try {
|
|
404
|
-
const items = await runtime.getClaims();
|
|
374
|
+
const items = await runtime.memory.getClaims();
|
|
405
375
|
res.json({ items });
|
|
406
376
|
} catch (e: any) {
|
|
407
377
|
res.status(500).json({ error: e.message });
|
|
@@ -414,7 +384,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
414
384
|
if (!runtime) return res.status(404).json({ error: "Companion not found" });
|
|
415
385
|
if (!runtime.memory) return res.json({ claims: [] });
|
|
416
386
|
try {
|
|
417
|
-
const claims = await runtime.getClaims();
|
|
387
|
+
const claims = await runtime.memory.getClaims();
|
|
418
388
|
res.json({ claims });
|
|
419
389
|
} catch (e: any) {
|
|
420
390
|
res.status(500).json({ error: e.message });
|
|
@@ -427,13 +397,96 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
427
397
|
if (!runtime) return res.status(404).json({ error: "Companion not found" });
|
|
428
398
|
if (!runtime.memory) return res.json({ directives: [] });
|
|
429
399
|
try {
|
|
430
|
-
const directives = await runtime.getDirectives();
|
|
400
|
+
const directives = await runtime.memory.getDirectives();
|
|
431
401
|
res.json({ directives });
|
|
432
402
|
} catch (e: any) {
|
|
433
403
|
res.status(500).json({ error: e.message });
|
|
434
404
|
}
|
|
435
405
|
});
|
|
436
406
|
|
|
407
|
+
// KNOWLEDGE / LIFE DB GETTERS
|
|
408
|
+
app.get('/knowledge/life', requireAuth, async (req, res) => {
|
|
409
|
+
const id = req.query.id as string || Array.from(runtimes.keys())[0];
|
|
410
|
+
const runtime = runtimes.get(id);
|
|
411
|
+
if (!runtime) return res.status(404).json({ error: "Companion not found" });
|
|
412
|
+
const query = (req.query.q as string) || '';
|
|
413
|
+
if (!runtime.knowledge || typeof (runtime.knowledge as any).queryLifeContext !== 'function') {
|
|
414
|
+
return res.json({ matchedInventory: [], recentFinances: [], upcomingSchedule: [], preferences: [], formattedContext: '' });
|
|
415
|
+
}
|
|
416
|
+
try {
|
|
417
|
+
const result = await (runtime.knowledge as any).queryLifeContext(id, query);
|
|
418
|
+
res.json(result);
|
|
419
|
+
} catch (e: any) {
|
|
420
|
+
res.status(500).json({ error: e.message });
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
app.get('/knowledge/inventory', requireAuth, async (req, res) => {
|
|
425
|
+
const id = req.query.id as string || Array.from(runtimes.keys())[0];
|
|
426
|
+
const runtime = runtimes.get(id);
|
|
427
|
+
if (!runtime) return res.status(404).json({ error: "Companion not found" });
|
|
428
|
+
const domain = req.query.domain as string | undefined;
|
|
429
|
+
if (!runtime.knowledge || !(runtime.knowledge as any).inventory) {
|
|
430
|
+
return res.json({ items: [] });
|
|
431
|
+
}
|
|
432
|
+
try {
|
|
433
|
+
const items = await (runtime.knowledge as any).inventory.getItems(id, domain);
|
|
434
|
+
res.json({ items });
|
|
435
|
+
} catch (e: any) {
|
|
436
|
+
res.status(500).json({ error: e.message });
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
app.get('/knowledge/finance', requireAuth, async (req, res) => {
|
|
441
|
+
const id = req.query.id as string || Array.from(runtimes.keys())[0];
|
|
442
|
+
const runtime = runtimes.get(id);
|
|
443
|
+
if (!runtime) return res.status(404).json({ error: "Companion not found" });
|
|
444
|
+
if (!runtime.knowledge || !(runtime.knowledge as any).finance) {
|
|
445
|
+
return res.json({ summary: null, entries: [] });
|
|
446
|
+
}
|
|
447
|
+
try {
|
|
448
|
+
const limit = Number(req.query.limit || 20);
|
|
449
|
+
const [summary, entries] = await Promise.all([
|
|
450
|
+
(runtime.knowledge as any).finance.getSummary(id),
|
|
451
|
+
(runtime.knowledge as any).finance.getEntries(id, limit),
|
|
452
|
+
]);
|
|
453
|
+
res.json({ summary, entries });
|
|
454
|
+
} catch (e: any) {
|
|
455
|
+
res.status(500).json({ error: e.message });
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
app.get('/knowledge/schedule', requireAuth, async (req, res) => {
|
|
460
|
+
const id = req.query.id as string || Array.from(runtimes.keys())[0];
|
|
461
|
+
const runtime = runtimes.get(id);
|
|
462
|
+
if (!runtime) return res.status(404).json({ error: "Companion not found" });
|
|
463
|
+
if (!runtime.knowledge || !(runtime.knowledge as any).schedule) {
|
|
464
|
+
return res.json({ items: [] });
|
|
465
|
+
}
|
|
466
|
+
try {
|
|
467
|
+
const items = await (runtime.knowledge as any).schedule.getUpcoming(id);
|
|
468
|
+
res.json({ items });
|
|
469
|
+
} catch (e: any) {
|
|
470
|
+
res.status(500).json({ error: e.message });
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
app.get('/knowledge/preferences', requireAuth, async (req, res) => {
|
|
475
|
+
const id = req.query.id as string || Array.from(runtimes.keys())[0];
|
|
476
|
+
const runtime = runtimes.get(id);
|
|
477
|
+
if (!runtime) return res.status(404).json({ error: "Companion not found" });
|
|
478
|
+
const category = req.query.category as string | undefined;
|
|
479
|
+
if (!runtime.knowledge || !(runtime.knowledge as any).preferences) {
|
|
480
|
+
return res.json({ preferences: [] });
|
|
481
|
+
}
|
|
482
|
+
try {
|
|
483
|
+
const preferences = await (runtime.knowledge as any).preferences.getPreferences(id, category);
|
|
484
|
+
res.json({ preferences });
|
|
485
|
+
} catch (e: any) {
|
|
486
|
+
res.status(500).json({ error: e.message });
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
|
|
437
490
|
// MEMORY MUTATIONS - PROPOSALS
|
|
438
491
|
app.post('/memory/proposals/update', requireAuth, async (req, res) => {
|
|
439
492
|
const id = req.body.companionId as string || Array.from(runtimes.keys())[0];
|
|
@@ -447,7 +500,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
447
500
|
if (!claimId) {
|
|
448
501
|
return res.status(400).json({ error: "Missing required claim id" });
|
|
449
502
|
}
|
|
450
|
-
const updated = await runtime.updateClaim(claimId, req.body.updates || req.body);
|
|
503
|
+
const updated = await runtime.memory.updateClaim(claimId, req.body.updates || req.body);
|
|
451
504
|
res.json({ success: true, claim: updated });
|
|
452
505
|
} catch (e: any) {
|
|
453
506
|
res.status(500).json({ error: e.message });
|
|
@@ -460,7 +513,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
460
513
|
if (!runtime) return res.status(404).json({ error: "Companion not found" });
|
|
461
514
|
if (!runtime.memory) return res.status(400).json({ error: "Memory organ not configured" });
|
|
462
515
|
try {
|
|
463
|
-
await runtime.approveClaim(req.body.id);
|
|
516
|
+
await runtime.memory.approveClaim(req.body.id);
|
|
464
517
|
res.json({ approved: true });
|
|
465
518
|
} catch (e: any) {
|
|
466
519
|
res.status(500).json({ error: e.message });
|
|
@@ -473,7 +526,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
473
526
|
if (!runtime) return res.status(404).json({ error: "Companion not found" });
|
|
474
527
|
if (!runtime.memory) return res.status(400).json({ error: "Memory organ not configured" });
|
|
475
528
|
try {
|
|
476
|
-
await runtime.rejectClaim(req.body.id);
|
|
529
|
+
await runtime.memory.rejectClaim(req.body.id);
|
|
477
530
|
res.json({ rejected: true });
|
|
478
531
|
} catch (e: any) {
|
|
479
532
|
res.status(500).json({ error: e.message });
|
|
@@ -487,7 +540,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
487
540
|
if (!runtime) return res.status(404).json({ error: "Companion not found" });
|
|
488
541
|
if (!runtime.memory) return res.status(400).json({ error: "Memory organ not configured" });
|
|
489
542
|
try {
|
|
490
|
-
await runtime.approveDirective(req.body.id);
|
|
543
|
+
await runtime.memory.approveDirective(req.body.id);
|
|
491
544
|
res.json({ approved: true });
|
|
492
545
|
} catch (e: any) {
|
|
493
546
|
res.status(500).json({ error: e.message });
|
|
@@ -500,7 +553,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
500
553
|
if (!runtime) return res.status(404).json({ error: "Companion not found" });
|
|
501
554
|
if (!runtime.memory) return res.status(400).json({ error: "Memory organ not configured" });
|
|
502
555
|
try {
|
|
503
|
-
await runtime.rejectDirective(req.body.id);
|
|
556
|
+
await runtime.memory.rejectDirective(req.body.id);
|
|
504
557
|
res.json({ rejected: true });
|
|
505
558
|
} catch (e: any) {
|
|
506
559
|
res.status(500).json({ error: e.message });
|
|
@@ -513,7 +566,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
513
566
|
if (!runtime) return res.status(404).json({ error: "Companion not found" });
|
|
514
567
|
if (!runtime.memory) return res.status(400).json({ error: "Memory organ not configured" });
|
|
515
568
|
try {
|
|
516
|
-
await runtime.revokeDirective(req.body.id);
|
|
569
|
+
await runtime.memory.revokeDirective(req.body.id);
|
|
517
570
|
res.json({ revoked: true });
|
|
518
571
|
} catch (e: any) {
|
|
519
572
|
res.status(500).json({ error: e.message });
|
|
@@ -526,7 +579,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
526
579
|
if (!runtime) return res.status(404).json({ error: "Companion not found" });
|
|
527
580
|
if (!runtime.memory) return res.status(400).json({ error: "Memory organ not configured" });
|
|
528
581
|
try {
|
|
529
|
-
await runtime.disableDirective(req.body.id);
|
|
582
|
+
await runtime.memory.disableDirective(req.body.id);
|
|
530
583
|
res.json({ disabled: true });
|
|
531
584
|
} catch (e: any) {
|
|
532
585
|
res.status(500).json({ error: e.message });
|
|
@@ -543,7 +596,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
543
596
|
return res.status(400).json({ error: "Memory organ does not support reset" });
|
|
544
597
|
}
|
|
545
598
|
try {
|
|
546
|
-
await runtime.resetMemory();
|
|
599
|
+
await runtime.memory.resetMemory();
|
|
547
600
|
res.json({ reset: true });
|
|
548
601
|
} catch (e: any) {
|
|
549
602
|
res.status(500).json({ error: e.message });
|
|
@@ -554,7 +607,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
554
607
|
const companionId = req.body?.companionId || Array.from(runtimes.keys())[0] || 'default';
|
|
555
608
|
const runtime = runtimes.get(companionId);
|
|
556
609
|
if (!runtime) return res.status(404).json({ accepted: false, error: 'Companion not found' });
|
|
557
|
-
const staged = runtime.stageResponse({
|
|
610
|
+
const staged = runtime.gating.stageResponse({
|
|
558
611
|
requestContext: {
|
|
559
612
|
companionId,
|
|
560
613
|
actor: {
|
|
@@ -590,12 +643,12 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
590
643
|
let correlationId = req.body?.correlation_id;
|
|
591
644
|
|
|
592
645
|
if (!responseId && correlationId) {
|
|
593
|
-
const found = runtime.findStagedPlanByCorrelation(companionId, correlationId);
|
|
646
|
+
const found = runtime.gating.findStagedPlanByCorrelation(companionId, correlationId);
|
|
594
647
|
if (found) {
|
|
595
648
|
responseId = found.responseId;
|
|
596
649
|
}
|
|
597
650
|
} else if (responseId && !correlationId) {
|
|
598
|
-
const found = runtime.getStagedPlan(responseId);
|
|
651
|
+
const found = runtime.gating.getStagedPlan(responseId);
|
|
599
652
|
if (found) {
|
|
600
653
|
correlationId = found.correlationId;
|
|
601
654
|
}
|
|
@@ -605,7 +658,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
605
658
|
return res.status(400).json({ approved: false, error: 'UNKNOWN_APPROVAL_ID' });
|
|
606
659
|
}
|
|
607
660
|
|
|
608
|
-
const result = runtime.approveResponse({
|
|
661
|
+
const result = runtime.gating.approveResponse({
|
|
609
662
|
responseId,
|
|
610
663
|
companionId,
|
|
611
664
|
correlationId: correlationId || '',
|
|
@@ -616,7 +669,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
616
669
|
}
|
|
617
670
|
|
|
618
671
|
// Now evaluated as approved
|
|
619
|
-
const evaluation = runtime.evaluateGate(result.plan!);
|
|
672
|
+
const evaluation = runtime.gating.evaluateGate(result.plan!);
|
|
620
673
|
res.json({
|
|
621
674
|
approved: true,
|
|
622
675
|
status: evaluation.disposition,
|
|
@@ -635,12 +688,12 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
635
688
|
let correlationId = req.body?.correlation_id;
|
|
636
689
|
|
|
637
690
|
if (!responseId && correlationId) {
|
|
638
|
-
const found = runtime.findStagedPlanByCorrelation(companionId, correlationId);
|
|
691
|
+
const found = runtime.gating.findStagedPlanByCorrelation(companionId, correlationId);
|
|
639
692
|
if (found) {
|
|
640
693
|
responseId = found.responseId;
|
|
641
694
|
}
|
|
642
695
|
} else if (responseId && !correlationId) {
|
|
643
|
-
const found = runtime.getStagedPlan(responseId);
|
|
696
|
+
const found = runtime.gating.getStagedPlan(responseId);
|
|
644
697
|
if (found) {
|
|
645
698
|
correlationId = found.correlationId;
|
|
646
699
|
}
|
|
@@ -650,7 +703,7 @@ export function createApp(runtimes: Map<string, SiduriRuntime> = new Map()): App
|
|
|
650
703
|
return res.status(400).json({ rejected: false, error: 'UNKNOWN_APPROVAL_ID' });
|
|
651
704
|
}
|
|
652
705
|
|
|
653
|
-
const result = runtime.rejectResponse({
|
|
706
|
+
const result = runtime.gating.rejectResponse({
|
|
654
707
|
responseId,
|
|
655
708
|
companionId,
|
|
656
709
|
correlationId: correlationId || '',
|