@siduri-x/api 2.0.1 → 2.0.3

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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @siduri-x/api@2.0.1 build /home/zagin/Projects/vxnus-studio/projects/siduri-x/apps/api
2
+ > @siduri-x/api@2.0.3 build /home/zagin/Projects/vxnus-studio/projects/siduri-x/apps/api
3
3
  > tsc
4
4
 
@@ -1,9 +1,9 @@
1
1
 
2
- > @siduri-x/api@2.0.1 test /home/zagin/Projects/vxnus-studio/projects/siduri-x/apps/api
2
+ > @siduri-x/api@2.0.3 test /home/zagin/Projects/vxnus-studio/projects/siduri-x/apps/api
3
3
  > jest --config jest.config.json
4
4
 
5
- PASS src/context-mapper.test.ts (10.14 s)
6
- PASS src/runtime.test.ts (10.545 s)
5
+ PASS src/context-mapper.test.ts (17.122 s)
6
+ PASS src/runtime.test.ts (17.986 s)
7
7
  ● Console
8
8
 
9
9
  console.error
@@ -20,21 +20,24 @@ PASS src/runtime.test.ts (10.545 s)
20
20
  at ../../packages/core/dist/context-retriever.js:23:25
21
21
  at async Promise.all (index 0)
22
22
  at retrieveRuntimeContext (../../packages/core/dist/context-retriever.js:19:78)
23
- at SiduriRuntime.processPerception (../../packages/core/dist/runtime.js:253:34)
23
+ at contextRetrievalStage (../../packages/core/dist/perception-pipeline.js:66:30)
24
+ at PerceptionPipeline.execute (../../packages/core/dist/perception-pipeline.js:21:34)
24
25
  at Object.<anonymous> (src/runtime.test.ts:74:22)
25
26
 
26
- PASS src/index.test.ts (10.888 s)
27
- PASS src/auth.test.ts
27
+ PASS src/t5-experience.test.ts (19.901 s)
28
+ PASS src/t4-gating.test.ts (19.152 s)
29
+ PASS src/index.test.ts (19.326 s)
30
+ PASS src/teach-mode.test.ts (20.058 s)
31
+ PASS src/t7-release.test.ts
28
32
  PASS src/smoke.test.ts
29
- PASS src/t4-gating.test.ts (11.522 s)
33
+ PASS src/auth.test.ts
34
+ PASS src/t6-security.test.ts (19.898 s)
35
+ PASS src/boot.test.ts
30
36
  PASS src/b0-b6.test.ts
31
- PASS src/t5-experience.test.ts (11.592 s)
32
- PASS src/t7-release.test.ts (11.335 s)
33
- PASS src/teach-mode.test.ts
34
- PASS src/t6-security.test.ts (12.22 s)
37
+ PASS src/knowledge.test.ts
35
38
  
36
- Test Suites: 11 passed, 11 total
37
- Tests: 72 passed, 72 total
39
+ Test Suites: 13 passed, 13 total
40
+ Tests: 90 passed, 90 total
38
41
  Snapshots: 0 total
39
- Time: 15.203 s, estimated 16 s
42
+ Time: 23.887 s
40
43
  Ran all test suites.
package/dist/app.d.ts CHANGED
@@ -1,40 +1,8 @@
1
1
  import { Express } from 'express';
2
2
  import { SiduriRuntime } from './runtime';
3
- import { VoiceConfig } from '@siduri-x/voice';
4
- import { EKnowledgeConfig } from '@siduri-x/eknowledge';
5
- import { OpenRouterVisionConfig } from '@siduri-x/vision';
6
- import { Live2DAdapterConfig } from '@siduri-x/body';
7
3
  import { FixtureObservationOrgan } from '@siduri-x/observation';
8
- import { DefaultHandsOrganConfig } from '@siduri-x/hands';
9
- import { EarOrganConfig } from '@siduri-x/ear';
10
- import { DefaultMouthOrganConfig } from '@siduri-x/mouth';
11
- export interface AppBrainConfig {
12
- provider?: 'openrouter' | 'openai-compatible' | string;
13
- model?: string;
14
- apiKey?: string;
15
- apiKeyEnv?: string;
16
- baseUrl?: string;
17
- timeoutMs?: number;
18
- [key: string]: unknown;
19
- }
20
- export interface AppBehaviorConfig {
21
- provider?: 'active_self' | 'none' | string;
22
- preset?: string;
23
- [key: string]: unknown;
24
- }
25
- export interface AppBootCompanionConfig {
26
- name: string;
27
- brain?: AppBrainConfig;
28
- voice?: VoiceConfig;
29
- knowledge?: EKnowledgeConfig;
30
- vision?: OpenRouterVisionConfig;
31
- behavior?: AppBehaviorConfig;
32
- body?: Live2DAdapterConfig;
33
- hands?: DefaultHandsOrganConfig;
34
- ear?: EarOrganConfig;
35
- mouth?: DefaultMouthOrganConfig;
36
- [key: string]: unknown;
37
- }
4
+ import { bootCompanion, AppBrainConfig, AppBehaviorConfig, AppBootCompanionConfig, BootCompanionOptions } from './boot';
5
+ export { AppBrainConfig, AppBehaviorConfig, AppBootCompanionConfig, BootCompanionOptions, bootCompanion, };
38
6
  export interface AppInstance {
39
7
  app: Express;
40
8
  runtimes: Map<string, SiduriRuntime>;
package/dist/app.js CHANGED
@@ -3,123 +3,29 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.bootCompanion = void 0;
6
7
  exports.createApp = createApp;
7
8
  const express_1 = __importDefault(require("express"));
8
9
  const cors_1 = __importDefault(require("cors"));
9
10
  const cors_2 = require("./cors");
10
11
  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 eknowledge_1 = require("@siduri-x/eknowledge");
15
- const vision_1 = require("@siduri-x/vision");
16
12
  const self_1 = require("@siduri-x/self");
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 mouth_1 = require("@siduri-x/mouth");
21
13
  const auth_1 = require("./auth");
22
14
  const context_mapper_1 = require("./context-mapper");
15
+ const boot_1 = require("./boot");
16
+ Object.defineProperty(exports, "bootCompanion", { enumerable: true, get: function () { return boot_1.bootCompanion; } });
23
17
  function createApp(runtimes = new Map()) {
24
18
  const app = (0, express_1.default)();
25
19
  app.use((0, cors_1.default)((0, cors_2.createCorsOptions)()));
26
20
  app.use(express_1.default.json());
27
21
  let observationOrgan;
28
- function createBrain(config) {
29
- const provider = config?.provider || 'openrouter';
30
- const defaultKeyEnv = provider === 'openai-compatible' ? 'OPENAI_COMPATIBLE_API_KEY' : 'OPENROUTER_API_KEY';
31
- const apiKey = config?.apiKey || process.env[config?.apiKeyEnv || defaultKeyEnv] || '';
32
- if (provider === 'openai-compatible') {
33
- return new brain_1.OpenAICompatibleBrain({
34
- apiKey,
35
- model: config?.model || 'local-model',
36
- baseUrl: config?.baseUrl || 'http://127.0.0.1:1234/v1',
37
- });
38
- }
39
- return new brain_1.OpenRouterBrain({ apiKey, model: config?.model || 'gpt-4o-mini' });
40
- }
41
- function isDisabled(config) {
42
- return !config || config.provider === 'none';
43
- }
44
- function createVoice(config) {
45
- return isDisabled(config)
46
- ? undefined
47
- : new voice_1.VoiceAdapter({
48
- provider: config?.provider || 'voicevox',
49
- baseUrl: config?.baseUrl || process.env.VOICEVOX_URL || 'http://localhost:50021',
50
- speakerId: config?.speakerId || 1,
51
- ...config,
52
- });
53
- }
54
- function createKnowledge(config) {
55
- return isDisabled(config) ? undefined : new eknowledge_1.EKnowledgeAdapter(config || {});
56
- }
57
- function createVision(config) {
58
- return isDisabled(config)
59
- ? undefined
60
- : new vision_1.OpenRouterVisionAdapter({
61
- apiKey: config?.apiKey || process.env.OPENROUTER_API_KEY || '',
62
- model: config?.model || 'gpt-4-vision',
63
- ...config,
64
- });
65
- }
66
- function createBehavior(config) {
67
- return isDisabled(config) ? undefined : new self_1.ActiveSelfCompiler();
68
- }
69
- function createBody(config) {
70
- return isDisabled(config)
71
- ? undefined
72
- : new body_1.Live2DAdapter(config);
73
- }
74
- function createHands(config) {
75
- return isDisabled(config)
76
- ? new hands_1.DefaultHandsOrgan()
77
- : new hands_1.DefaultHandsOrgan(config);
78
- }
79
- function createEar(config) {
80
- return isDisabled(config)
81
- ? new ear_1.DefaultEarOrgan()
82
- : new ear_1.DefaultEarOrgan(config);
83
- }
84
- function createMouth(config, voice) {
85
- return isDisabled(config)
86
- ? new mouth_1.DefaultMouthOrgan({ voice })
87
- : new mouth_1.DefaultMouthOrgan({ ...config, voice });
88
- }
89
22
  app.post('/boot', auth_1.requireAuth, async (req, res) => {
90
23
  try {
91
24
  const { id, config } = req.body;
92
25
  if (runtimes.has(id)) {
93
26
  return res.status(400).json({ error: "Already booted" });
94
27
  }
95
- const organs = config?.organs || {};
96
- const brain = createBrain(organs.brain || config?.brain);
97
- const memory = new memory_1.SqliteMemoryStore({ dbPath: process.env.STORAGE_PATH || process.env.SQLITE_DB_PATH || 'siduri.sqlite' });
98
- const selfRepo = new self_1.SqliteSelfRepository({ dbPath: process.env.STORAGE_PATH || process.env.SQLITE_DB_PATH || 'siduri.sqlite' });
99
- const voice = createVoice(organs.voice || config?.voice);
100
- const knowledge = createKnowledge(organs.knowledge || config?.knowledge);
101
- const vision = createVision(organs.vision || config?.vision);
102
- const behavior = createBehavior(organs.behavior || config?.behavior);
103
- const body = createBody(organs.body || config?.body);
104
- const hands = createHands(organs.hands || config?.hands);
105
- const ear = createEar(organs.ear || config?.ear);
106
- const mouth = createMouth(organs.mouth || config?.mouth, voice);
107
- const runtime = new runtime_1.SiduriRuntime(id, config, {
108
- brain,
109
- memory,
110
- voice,
111
- knowledge,
112
- vision,
113
- behavior,
114
- body,
115
- hands,
116
- ear,
117
- mouth,
118
- observation: observationOrgan,
119
- self: selfRepo,
120
- externalKnowledge: knowledge,
121
- });
122
- await runtime.initialize();
28
+ const runtime = await (0, boot_1.bootCompanion)(id, config, { observationOrgan });
123
29
  runtimes.set(id, runtime);
124
30
  res.json({ success: true, id });
125
31
  }
@@ -208,12 +114,31 @@ function createApp(runtimes = new Map()) {
208
114
  companionId,
209
115
  name: manifest.identity.name,
210
116
  archetype: manifest.identity.archetype,
117
+ origin: manifest.identity.origin,
118
+ ethos: manifest.identity.ethos,
211
119
  version: manifest.version || '1.0.0',
212
120
  updatedAt: new Date().toISOString(),
213
121
  });
214
122
  if (manifest.personality) {
215
123
  await repo.setPersonality(companionId, manifest.personality);
216
124
  }
125
+ if (Array.isArray(manifest.relationships)) {
126
+ for (const rel of manifest.relationships) {
127
+ if (rel && rel.entityId) {
128
+ await repo.updateRelationship(companionId, {
129
+ companionId,
130
+ entityId: rel.entityId,
131
+ entityType: 'human',
132
+ role: rel.role || 'user',
133
+ stance: rel.stance || 'neutral',
134
+ interactionConventions: rel.conventions || [],
135
+ });
136
+ }
137
+ }
138
+ }
139
+ if (Array.isArray(manifest.dialogueExamples) && repo.setExemplars) {
140
+ await repo.setExemplars(companionId, manifest.dialogueExamples);
141
+ }
217
142
  if (directivesToCommit.length > 0) {
218
143
  await repo.commitDirectives(companionId, directivesToCommit);
219
144
  }
@@ -231,12 +156,18 @@ function createApp(runtimes = new Map()) {
231
156
  app.post('/chat', auth_1.attachIdentity, async (req, res) => {
232
157
  const { id, message, history } = req.body;
233
158
  const identity = req.identity;
234
- // Single-owner companion model: map request context directly
159
+ const local = (0, auth_1.isLocalRequest)(req);
160
+ const hasConfiguredToken = Boolean(process.env.AUTH_TOKEN || process.env.OPERATOR_TOKEN || process.env.OWNER_TOKEN);
161
+ const isLocalDirectOwner = local && !hasConfiguredToken;
162
+ const authenticated = identity?.authenticated || isLocalDirectOwner;
163
+ const serverRole = identity?.authenticated ? identity.role : (isLocalDirectOwner ? 'OWNER' : 'VIEWER');
164
+ // Single-owner companion model: map request context directly with server identity enforcement
235
165
  const mappingResult = (0, context_mapper_1.mapRequestContext)({
236
166
  ...req.body,
237
167
  id: id || req.body.companionId,
238
- authenticated: identity?.authenticated ?? true,
239
- source: identity?.source ?? 'local',
168
+ authenticated,
169
+ serverRole,
170
+ source: identity?.source ?? (local ? 'local' : 'external'),
240
171
  generateCorrelationId: true,
241
172
  });
242
173
  if (!mappingResult.accepted) {
@@ -268,11 +199,17 @@ function createApp(runtimes = new Map()) {
268
199
  app.post('/chat/stream', auth_1.attachIdentity, async (req, res) => {
269
200
  const { id, message, history } = req.body;
270
201
  const identity = req.identity;
202
+ const local = (0, auth_1.isLocalRequest)(req);
203
+ const hasConfiguredToken = Boolean(process.env.AUTH_TOKEN || process.env.OPERATOR_TOKEN || process.env.OWNER_TOKEN);
204
+ const isLocalDirectOwner = local && !hasConfiguredToken;
205
+ const authenticated = identity?.authenticated || isLocalDirectOwner;
206
+ const serverRole = identity?.authenticated ? identity.role : (isLocalDirectOwner ? 'OWNER' : 'VIEWER');
271
207
  const mappingResult = (0, context_mapper_1.mapRequestContext)({
272
208
  ...req.body,
273
209
  id: id || req.body.companionId,
274
- authenticated: identity?.authenticated ?? true,
275
- source: identity?.source ?? 'local',
210
+ authenticated,
211
+ serverRole,
212
+ source: identity?.source ?? (local ? 'local' : 'external'),
276
213
  generateCorrelationId: true,
277
214
  });
278
215
  if (!mappingResult.accepted) {
@@ -292,8 +229,8 @@ function createApp(runtimes = new Map()) {
292
229
  const abortController = new AbortController();
293
230
  const onClose = () => {
294
231
  abortController.abort('client_disconnect');
295
- if (typeof runtime.interruptMouth === 'function') {
296
- runtime.interruptMouth('client_disconnect');
232
+ if (runtime.mouth && typeof runtime.mouth.interrupt === 'function') {
233
+ runtime.mouth.interrupt('client_disconnect');
297
234
  }
298
235
  };
299
236
  req.on('close', onClose);
@@ -307,7 +244,7 @@ function createApp(runtimes = new Map()) {
307
244
  medium: 'web',
308
245
  signal: abortController.signal,
309
246
  });
310
- res.write(`event: staged\ndata: ${JSON.stringify({ response_id: response.response_id, correlation_id: response.correlation_id, status: response.status })}\n\n`);
247
+ res.write(`event: staged\ndata: ${JSON.stringify({ response_id: response.response_id, correlation_id: response.correlation_id, status: response.status, mode: response.metadata?.mode })}\n\n`);
311
248
  const avatarEvent = response.metadata?.events?.find((e) => (e.kind === 'avatar' || e.kind === 'body') && (e.approval === 'APPROVED' || !e.approval));
312
249
  if (avatarEvent) {
313
250
  res.write(`event: avatar\ndata: ${JSON.stringify(avatarEvent)}\n\n`);
@@ -360,11 +297,15 @@ function createApp(runtimes = new Map()) {
360
297
  const runtime = companionId ? runtimes.get(companionId) : undefined;
361
298
  const reason = req.body?.reason || 'user_barge_in';
362
299
  if (runtime) {
363
- runtime.interruptMouth(reason);
300
+ if (runtime.mouth && typeof runtime.mouth.interrupt === 'function') {
301
+ runtime.mouth.interrupt(reason);
302
+ }
364
303
  return res.json({ success: true, interrupted: true, companionId, reason });
365
304
  }
366
305
  for (const r of runtimes.values()) {
367
- r.interruptMouth(reason);
306
+ if (r.mouth && typeof r.mouth.interrupt === 'function') {
307
+ r.mouth.interrupt(reason);
308
+ }
368
309
  }
369
310
  return res.json({ success: true, interrupted: true, reason });
370
311
  });
@@ -373,11 +314,15 @@ function createApp(runtimes = new Map()) {
373
314
  const runtime = companionId ? runtimes.get(companionId) : undefined;
374
315
  const reason = req.body?.reason || 'user_barge_in';
375
316
  if (runtime) {
376
- runtime.interruptMouth(reason);
317
+ if (runtime.mouth && typeof runtime.mouth.interrupt === 'function') {
318
+ runtime.mouth.interrupt(reason);
319
+ }
377
320
  return res.json({ success: true, interrupted: true, companionId, reason });
378
321
  }
379
322
  for (const r of runtimes.values()) {
380
- r.interruptMouth(reason);
323
+ if (r.mouth && typeof r.mouth.interrupt === 'function') {
324
+ r.mouth.interrupt(reason);
325
+ }
381
326
  }
382
327
  return res.json({ success: true, interrupted: true, reason });
383
328
  });
@@ -390,7 +335,7 @@ function createApp(runtimes = new Map()) {
390
335
  if (!runtime.memory)
391
336
  return res.json({ proposals: [] });
392
337
  try {
393
- const proposals = await runtime.getPendingClaims();
338
+ const proposals = await runtime.memory.getPendingClaims();
394
339
  res.json({ proposals });
395
340
  }
396
341
  catch (e) {
@@ -405,7 +350,7 @@ function createApp(runtimes = new Map()) {
405
350
  if (!runtime.memory)
406
351
  return res.json({ items: [] });
407
352
  try {
408
- const items = await runtime.getClaims();
353
+ const items = await runtime.memory.getClaims();
409
354
  res.json({ items });
410
355
  }
411
356
  catch (e) {
@@ -420,7 +365,7 @@ function createApp(runtimes = new Map()) {
420
365
  if (!runtime.memory)
421
366
  return res.json({ claims: [] });
422
367
  try {
423
- const claims = await runtime.getClaims();
368
+ const claims = await runtime.memory.getClaims();
424
369
  res.json({ claims });
425
370
  }
426
371
  catch (e) {
@@ -435,13 +380,101 @@ function createApp(runtimes = new Map()) {
435
380
  if (!runtime.memory)
436
381
  return res.json({ directives: [] });
437
382
  try {
438
- const directives = await runtime.getDirectives();
383
+ const directives = await runtime.memory.getDirectives();
439
384
  res.json({ directives });
440
385
  }
441
386
  catch (e) {
442
387
  res.status(500).json({ error: e.message });
443
388
  }
444
389
  });
390
+ // KNOWLEDGE / LIFE DB GETTERS
391
+ app.get('/knowledge/life', auth_1.requireAuth, async (req, res) => {
392
+ const id = req.query.id || Array.from(runtimes.keys())[0];
393
+ const runtime = runtimes.get(id);
394
+ if (!runtime)
395
+ return res.status(404).json({ error: "Companion not found" });
396
+ const query = req.query.q || '';
397
+ if (!runtime.knowledge || typeof runtime.knowledge.queryLifeContext !== 'function') {
398
+ return res.json({ matchedInventory: [], recentFinances: [], upcomingSchedule: [], preferences: [], formattedContext: '' });
399
+ }
400
+ try {
401
+ const result = await runtime.knowledge.queryLifeContext(id, query);
402
+ res.json(result);
403
+ }
404
+ catch (e) {
405
+ res.status(500).json({ error: e.message });
406
+ }
407
+ });
408
+ app.get('/knowledge/inventory', auth_1.requireAuth, async (req, res) => {
409
+ const id = req.query.id || Array.from(runtimes.keys())[0];
410
+ const runtime = runtimes.get(id);
411
+ if (!runtime)
412
+ return res.status(404).json({ error: "Companion not found" });
413
+ const domain = req.query.domain;
414
+ if (!runtime.knowledge || !runtime.knowledge.inventory) {
415
+ return res.json({ items: [] });
416
+ }
417
+ try {
418
+ const items = await runtime.knowledge.inventory.getItems(id, domain);
419
+ res.json({ items });
420
+ }
421
+ catch (e) {
422
+ res.status(500).json({ error: e.message });
423
+ }
424
+ });
425
+ app.get('/knowledge/finance', auth_1.requireAuth, async (req, res) => {
426
+ const id = req.query.id || Array.from(runtimes.keys())[0];
427
+ const runtime = runtimes.get(id);
428
+ if (!runtime)
429
+ return res.status(404).json({ error: "Companion not found" });
430
+ if (!runtime.knowledge || !runtime.knowledge.finance) {
431
+ return res.json({ summary: null, entries: [] });
432
+ }
433
+ try {
434
+ const limit = Number(req.query.limit || 20);
435
+ const [summary, entries] = await Promise.all([
436
+ runtime.knowledge.finance.getSummary(id),
437
+ runtime.knowledge.finance.getEntries(id, limit),
438
+ ]);
439
+ res.json({ summary, entries });
440
+ }
441
+ catch (e) {
442
+ res.status(500).json({ error: e.message });
443
+ }
444
+ });
445
+ app.get('/knowledge/schedule', auth_1.requireAuth, async (req, res) => {
446
+ const id = req.query.id || Array.from(runtimes.keys())[0];
447
+ const runtime = runtimes.get(id);
448
+ if (!runtime)
449
+ return res.status(404).json({ error: "Companion not found" });
450
+ if (!runtime.knowledge || !runtime.knowledge.schedule) {
451
+ return res.json({ items: [] });
452
+ }
453
+ try {
454
+ const items = await runtime.knowledge.schedule.getUpcoming(id);
455
+ res.json({ items });
456
+ }
457
+ catch (e) {
458
+ res.status(500).json({ error: e.message });
459
+ }
460
+ });
461
+ app.get('/knowledge/preferences', auth_1.requireAuth, async (req, res) => {
462
+ const id = req.query.id || Array.from(runtimes.keys())[0];
463
+ const runtime = runtimes.get(id);
464
+ if (!runtime)
465
+ return res.status(404).json({ error: "Companion not found" });
466
+ const category = req.query.category;
467
+ if (!runtime.knowledge || !runtime.knowledge.preferences) {
468
+ return res.json({ preferences: [] });
469
+ }
470
+ try {
471
+ const preferences = await runtime.knowledge.preferences.getPreferences(id, category);
472
+ res.json({ preferences });
473
+ }
474
+ catch (e) {
475
+ res.status(500).json({ error: e.message });
476
+ }
477
+ });
445
478
  // MEMORY MUTATIONS - PROPOSALS
446
479
  app.post('/memory/proposals/update', auth_1.requireAuth, async (req, res) => {
447
480
  const id = req.body.companionId || Array.from(runtimes.keys())[0];
@@ -456,7 +489,7 @@ function createApp(runtimes = new Map()) {
456
489
  if (!claimId) {
457
490
  return res.status(400).json({ error: "Missing required claim id" });
458
491
  }
459
- const updated = await runtime.updateClaim(claimId, req.body.updates || req.body);
492
+ const updated = await runtime.memory.updateClaim(claimId, req.body.updates || req.body);
460
493
  res.json({ success: true, claim: updated });
461
494
  }
462
495
  catch (e) {
@@ -471,7 +504,7 @@ function createApp(runtimes = new Map()) {
471
504
  if (!runtime.memory)
472
505
  return res.status(400).json({ error: "Memory organ not configured" });
473
506
  try {
474
- await runtime.approveClaim(req.body.id);
507
+ await runtime.memory.approveClaim(req.body.id);
475
508
  res.json({ approved: true });
476
509
  }
477
510
  catch (e) {
@@ -486,7 +519,7 @@ function createApp(runtimes = new Map()) {
486
519
  if (!runtime.memory)
487
520
  return res.status(400).json({ error: "Memory organ not configured" });
488
521
  try {
489
- await runtime.rejectClaim(req.body.id);
522
+ await runtime.memory.rejectClaim(req.body.id);
490
523
  res.json({ rejected: true });
491
524
  }
492
525
  catch (e) {
@@ -502,7 +535,7 @@ function createApp(runtimes = new Map()) {
502
535
  if (!runtime.memory)
503
536
  return res.status(400).json({ error: "Memory organ not configured" });
504
537
  try {
505
- await runtime.approveDirective(req.body.id);
538
+ await runtime.memory.approveDirective(req.body.id);
506
539
  res.json({ approved: true });
507
540
  }
508
541
  catch (e) {
@@ -517,7 +550,7 @@ function createApp(runtimes = new Map()) {
517
550
  if (!runtime.memory)
518
551
  return res.status(400).json({ error: "Memory organ not configured" });
519
552
  try {
520
- await runtime.rejectDirective(req.body.id);
553
+ await runtime.memory.rejectDirective(req.body.id);
521
554
  res.json({ rejected: true });
522
555
  }
523
556
  catch (e) {
@@ -532,7 +565,7 @@ function createApp(runtimes = new Map()) {
532
565
  if (!runtime.memory)
533
566
  return res.status(400).json({ error: "Memory organ not configured" });
534
567
  try {
535
- await runtime.revokeDirective(req.body.id);
568
+ await runtime.memory.revokeDirective(req.body.id);
536
569
  res.json({ revoked: true });
537
570
  }
538
571
  catch (e) {
@@ -547,7 +580,7 @@ function createApp(runtimes = new Map()) {
547
580
  if (!runtime.memory)
548
581
  return res.status(400).json({ error: "Memory organ not configured" });
549
582
  try {
550
- await runtime.disableDirective(req.body.id);
583
+ await runtime.memory.disableDirective(req.body.id);
551
584
  res.json({ disabled: true });
552
585
  }
553
586
  catch (e) {
@@ -565,7 +598,7 @@ function createApp(runtimes = new Map()) {
565
598
  return res.status(400).json({ error: "Memory organ does not support reset" });
566
599
  }
567
600
  try {
568
- await runtime.resetMemory();
601
+ await runtime.memory.resetMemory();
569
602
  res.json({ reset: true });
570
603
  }
571
604
  catch (e) {
@@ -577,7 +610,7 @@ function createApp(runtimes = new Map()) {
577
610
  const runtime = runtimes.get(companionId);
578
611
  if (!runtime)
579
612
  return res.status(404).json({ accepted: false, error: 'Companion not found' });
580
- const staged = runtime.stageResponse({
613
+ const staged = runtime.gating.stageResponse({
581
614
  requestContext: {
582
615
  companionId,
583
616
  actor: {
@@ -611,13 +644,13 @@ function createApp(runtimes = new Map()) {
611
644
  let responseId = req.body?.responseId;
612
645
  let correlationId = req.body?.correlation_id;
613
646
  if (!responseId && correlationId) {
614
- const found = runtime.findStagedPlanByCorrelation(companionId, correlationId);
647
+ const found = runtime.gating.findStagedPlanByCorrelation(companionId, correlationId);
615
648
  if (found) {
616
649
  responseId = found.responseId;
617
650
  }
618
651
  }
619
652
  else if (responseId && !correlationId) {
620
- const found = runtime.getStagedPlan(responseId);
653
+ const found = runtime.gating.getStagedPlan(responseId);
621
654
  if (found) {
622
655
  correlationId = found.correlationId;
623
656
  }
@@ -625,7 +658,7 @@ function createApp(runtimes = new Map()) {
625
658
  if (!responseId) {
626
659
  return res.status(400).json({ approved: false, error: 'UNKNOWN_APPROVAL_ID' });
627
660
  }
628
- const result = runtime.approveResponse({
661
+ const result = runtime.gating.approveResponse({
629
662
  responseId,
630
663
  companionId,
631
664
  correlationId: correlationId || '',
@@ -634,7 +667,7 @@ function createApp(runtimes = new Map()) {
634
667
  return res.status(400).json({ approved: false, error: result.reason });
635
668
  }
636
669
  // Now evaluated as approved
637
- const evaluation = runtime.evaluateGate(result.plan);
670
+ const evaluation = runtime.gating.evaluateGate(result.plan);
638
671
  res.json({
639
672
  approved: true,
640
673
  status: evaluation.disposition,
@@ -651,13 +684,13 @@ function createApp(runtimes = new Map()) {
651
684
  let responseId = req.body?.responseId;
652
685
  let correlationId = req.body?.correlation_id;
653
686
  if (!responseId && correlationId) {
654
- const found = runtime.findStagedPlanByCorrelation(companionId, correlationId);
687
+ const found = runtime.gating.findStagedPlanByCorrelation(companionId, correlationId);
655
688
  if (found) {
656
689
  responseId = found.responseId;
657
690
  }
658
691
  }
659
692
  else if (responseId && !correlationId) {
660
- const found = runtime.getStagedPlan(responseId);
693
+ const found = runtime.gating.getStagedPlan(responseId);
661
694
  if (found) {
662
695
  correlationId = found.correlationId;
663
696
  }
@@ -665,7 +698,7 @@ function createApp(runtimes = new Map()) {
665
698
  if (!responseId) {
666
699
  return res.status(400).json({ rejected: false, error: 'UNKNOWN_APPROVAL_ID' });
667
700
  }
668
- const result = runtime.rejectResponse({
701
+ const result = runtime.gating.rejectResponse({
669
702
  responseId,
670
703
  companionId,
671
704
  correlationId: correlationId || '',