@siduri-x/core 2.0.0 → 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.
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const perception_pipeline_1 = require("./perception-pipeline");
4
+ const gating_1 = require("./gating");
5
+ const action_policy_1 = require("./action-policy");
6
+ const dispatcher_1 = require("./dispatcher");
7
+ const session_history_1 = require("./session-history");
8
+ describe('PerceptionPipeline (Pipes & Filters Execution)', () => {
9
+ function createMockContext(overrides = {}) {
10
+ return {
11
+ companionId: 'test-companion',
12
+ companionName: 'Test Companion',
13
+ perception: {
14
+ source: 'text_chat',
15
+ text: 'Hello world',
16
+ roleOrContext: 'OWNER',
17
+ },
18
+ organs: {},
19
+ gating: new gating_1.ResponseGatingEngine(),
20
+ actionPolicy: new action_policy_1.ActionPolicyEngine(),
21
+ dispatcher: new dispatcher_1.ExperienceDispatcher(),
22
+ sessionHistory: new session_history_1.SessionHistoryManager(),
23
+ ...overrides,
24
+ };
25
+ }
26
+ test('executes stages in sequence and returns response envelope', async () => {
27
+ const executedStages = [];
28
+ const stageA = async (ctx) => {
29
+ executedStages.push('stageA');
30
+ ctx.rawText = (ctx.perception.text || '').toUpperCase();
31
+ };
32
+ const stageB = async (ctx) => {
33
+ executedStages.push('stageB');
34
+ ctx.responseEnvelope = { result: ctx.rawText };
35
+ };
36
+ const pipeline = new perception_pipeline_1.PerceptionPipeline([stageA, stageB]);
37
+ const ctx = createMockContext();
38
+ const result = await pipeline.execute(ctx);
39
+ expect(executedStages).toEqual(['stageA', 'stageB']);
40
+ expect(result).toEqual({ result: 'HELLO WORLD' });
41
+ });
42
+ test('halts pipeline execution early when a stage returns false', async () => {
43
+ const executedStages = [];
44
+ const stageA = async () => {
45
+ executedStages.push('stageA');
46
+ };
47
+ const stageReject = async (ctx) => {
48
+ executedStages.push('stageReject');
49
+ ctx.responseEnvelope = { rejected: true, reason: 'halt_early' };
50
+ return false; // halt
51
+ };
52
+ const stageB = async () => {
53
+ executedStages.push('stageB');
54
+ };
55
+ const pipeline = new perception_pipeline_1.PerceptionPipeline([stageA, stageReject, stageB]);
56
+ const ctx = createMockContext();
57
+ const result = await pipeline.execute(ctx);
58
+ expect(executedStages).toEqual(['stageA', 'stageReject']);
59
+ expect(result).toEqual({ rejected: true, reason: 'halt_early' });
60
+ });
61
+ test('createDefaultPerceptionPipeline constructs a 12-stage pipeline', () => {
62
+ const defaultPipeline = (0, perception_pipeline_1.createDefaultPerceptionPipeline)();
63
+ expect(defaultPipeline.stages.length).toBe(12);
64
+ });
65
+ });
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const runtime_1 = require("./runtime");
4
- describe('SiduriRuntime Facade Methods & Delegation', () => {
5
- test('delegates vision and observation methods to configured organs', async () => {
3
+ const container_1 = require("./container");
4
+ describe('CompanionContainer & Direct Domain Access', () => {
5
+ test('delegates vision and observation methods to configured organs via container', async () => {
6
6
  const mockVision = {
7
7
  analyze: jest.fn().mockResolvedValue('an ancient artifact'),
8
8
  };
@@ -11,23 +11,23 @@ describe('SiduriRuntime Facade Methods & Delegation', () => {
11
11
  current: jest.fn().mockReturnValue([{ observationId: 'obs-1' }]),
12
12
  clearExpired: jest.fn().mockReturnValue(1),
13
13
  };
14
- const runtime = new runtime_1.SiduriRuntime('test-comp', { name: 'Test' }, {
14
+ const container = new container_1.CompanionContainer('test-comp', { name: 'Test' }, {
15
15
  vision: mockVision,
16
16
  observation: mockObservation,
17
17
  });
18
- const visionResult = await runtime.analyzeVision('http://example.com/img.png', 'describe');
18
+ const visionResult = await container.vision.analyze('http://example.com/img.png', 'describe');
19
19
  expect(visionResult).toBe('an ancient artifact');
20
20
  expect(mockVision.analyze).toHaveBeenCalledWith('http://example.com/img.png', 'describe');
21
21
  const frame = new Uint8Array([1, 2, 3]);
22
- const obsResult = await runtime.ingestObservation(frame, 'camera-1', 'provider-x');
22
+ const obsResult = await container.observation.ingest(frame, 'camera-1', 'provider-x');
23
23
  expect(obsResult.duplicate).toBe(false);
24
24
  expect(mockObservation.ingest).toHaveBeenCalledWith(frame, 'camera-1', 'provider-x');
25
- const cur = runtime.getCurrentObservations();
25
+ const cur = container.observation.current();
26
26
  expect(cur).toEqual([{ observationId: 'obs-1' }]);
27
- const cleared = runtime.clearExpiredObservations();
27
+ const cleared = container.observation.clearExpired();
28
28
  expect(cleared).toBe(1);
29
29
  });
30
- test('delegates memory operations cleanly through facade methods', async () => {
30
+ test('delegates memory operations directly on the memory organ', async () => {
31
31
  const mockMemory = {
32
32
  initialize: jest.fn().mockResolvedValue(undefined),
33
33
  getClaims: jest.fn().mockResolvedValue([{ id: 'claim-1' }]),
@@ -42,39 +42,39 @@ describe('SiduriRuntime Facade Methods & Delegation', () => {
42
42
  disableDirective: jest.fn().mockResolvedValue(undefined),
43
43
  resetMemory: jest.fn().mockResolvedValue(undefined),
44
44
  };
45
- const runtime = new runtime_1.SiduriRuntime('test-comp', { name: 'Test' }, {
45
+ const container = new container_1.CompanionContainer('test-comp', { name: 'Test' }, {
46
46
  memory: mockMemory,
47
47
  });
48
- expect(await runtime.getClaims(10)).toEqual([{ id: 'claim-1' }]);
48
+ expect(await container.memory.getClaims(10)).toEqual([{ id: 'claim-1' }]);
49
49
  expect(mockMemory.getClaims).toHaveBeenCalledWith(10);
50
- expect(await runtime.getPendingClaims()).toEqual([{ id: 'claim-pending-1' }]);
51
- expect(await runtime.getDirectives()).toEqual([{ id: 'dir-1' }]);
52
- await runtime.approveClaim('c-1');
50
+ expect(await container.memory.getPendingClaims()).toEqual([{ id: 'claim-pending-1' }]);
51
+ expect(await container.memory.getDirectives()).toEqual([{ id: 'dir-1' }]);
52
+ await container.memory.approveClaim('c-1');
53
53
  expect(mockMemory.approveClaim).toHaveBeenCalledWith('c-1');
54
- await runtime.rejectClaim('c-2');
54
+ await container.memory.rejectClaim('c-2');
55
55
  expect(mockMemory.rejectClaim).toHaveBeenCalledWith('c-2');
56
- await runtime.updateClaim('c-1', { value: 'new' });
57
- expect(mockMemory.updateClaim).toHaveBeenCalledWith('c-1', { value: 'new' });
58
- await runtime.approveDirective('d-1');
56
+ await container.memory.updateClaim('c-1', { value: 'updated' });
57
+ expect(mockMemory.updateClaim).toHaveBeenCalledWith('c-1', { value: 'updated' });
58
+ await container.memory.approveDirective('d-1');
59
59
  expect(mockMemory.approveDirective).toHaveBeenCalledWith('d-1');
60
- await runtime.rejectDirective('d-2');
60
+ await container.memory.rejectDirective('d-2');
61
61
  expect(mockMemory.rejectDirective).toHaveBeenCalledWith('d-2');
62
- await runtime.revokeDirective('d-3');
62
+ await container.memory.revokeDirective('d-3');
63
63
  expect(mockMemory.revokeDirective).toHaveBeenCalledWith('d-3');
64
- await runtime.disableDirective('d-4');
64
+ await container.memory.disableDirective('d-4');
65
65
  expect(mockMemory.disableDirective).toHaveBeenCalledWith('d-4');
66
- await runtime.resetMemory();
66
+ await container.memory.resetMemory();
67
67
  expect(mockMemory.resetMemory).toHaveBeenCalled();
68
68
  });
69
69
  test('configures SqliteActionStore when actionStore is sqlite', () => {
70
- const runtime = new runtime_1.SiduriRuntime('comp-sqlite', {
70
+ const container = new container_1.CompanionContainer('comp-sqlite', {
71
71
  id: 'comp-sqlite',
72
72
  name: 'Sqlite Test',
73
73
  actionStore: 'sqlite',
74
74
  });
75
- expect(runtime.actionPolicy.getStore()).toBeDefined();
75
+ expect(container.actionPolicy.getStore()).toBeDefined();
76
76
  // Verify it is an instance of SqliteActionStore
77
- expect(runtime.actionPolicy.getStore().constructor.name).toBe('SqliteActionStore');
77
+ expect(container.actionPolicy.getStore().constructor.name).toBe('SqliteActionStore');
78
78
  });
79
79
  test('accepts custom actionStore via RuntimeOrgans', () => {
80
80
  const customStore = {
@@ -87,9 +87,9 @@ describe('SiduriRuntime Facade Methods & Delegation', () => {
87
87
  getAuditLog: jest.fn(),
88
88
  verifyAuditChain: jest.fn(),
89
89
  };
90
- const runtime = new runtime_1.SiduriRuntime('comp-custom', { id: 'comp-custom', name: 'Custom' }, {
90
+ const container = new container_1.CompanionContainer('comp-custom', { id: 'comp-custom', name: 'Custom' }, {
91
91
  actionStore: customStore,
92
92
  });
93
- expect(runtime.actionPolicy.getStore()).toBe(customStore);
93
+ expect(container.actionPolicy.getStore()).toBe(customStore);
94
94
  });
95
95
  });
package/dist/runtime.d.ts CHANGED
@@ -1,126 +1,50 @@
1
- import { BrainOrgan, MemoryOrgan, VoiceOrgan, KnowledgeOrgan, VisionOrgan, BehaviorOrgan, BodyOrgan, HandsOrgan, EarOrgan, ObservationOrgan, ObservationResult, Observation, Message, RequestContext, ActionPolicyEngine, ActionStore, ResponseGatingEngine, StageResponseOptions, ApproveResponseOptions, RejectResponseOptions, ExperienceDispatcher, ExperienceAdapter, OrganConfig, Claim, BehaviorDirective, StagedResponsePlan, ResponseGateEvaluation, EvidenceRecord, MouthOrgan, MouthUtterance, MouthMedium, FormattedMouthOutput, MouthStreamChunk, MouthChannel, SelfRepository, EKnowledgeOrgan } from './index';
2
- export interface SiduriRuntimeConfig {
3
- name: string;
4
- brain?: OrganConfig | Record<string, unknown>;
5
- voice?: OrganConfig | Record<string, unknown>;
6
- memory?: OrganConfig | Record<string, unknown>;
7
- knowledge?: OrganConfig | Record<string, unknown>;
8
- behavior?: OrganConfig | Record<string, unknown>;
9
- body?: OrganConfig | Record<string, unknown>;
10
- vision?: OrganConfig | Record<string, unknown>;
11
- hands?: OrganConfig | Record<string, unknown>;
12
- ear?: OrganConfig | Record<string, unknown>;
13
- observation?: OrganConfig | Record<string, unknown>;
14
- mouth?: OrganConfig | Record<string, unknown>;
15
- self?: OrganConfig | Record<string, unknown>;
16
- externalKnowledge?: OrganConfig | Record<string, unknown>;
17
- actionPolicy?: Record<string, unknown>;
18
- actionStore?: 'in-memory' | 'sqlite' | {
19
- type: 'sqlite' | 'in-memory';
20
- dbPath?: string;
21
- };
22
- actionStorePath?: string;
23
- [key: string]: unknown;
24
- }
25
- export interface RuntimeOrgans {
26
- brain?: BrainOrgan;
27
- memory?: MemoryOrgan;
28
- voice?: VoiceOrgan | ExperienceAdapter;
29
- knowledge?: KnowledgeOrgan;
30
- vision?: VisionOrgan;
31
- behavior?: BehaviorOrgan;
32
- body?: BodyOrgan | ExperienceAdapter;
33
- hands?: HandsOrgan;
34
- ear?: EarOrgan;
35
- observation?: ObservationOrgan;
36
- mouth?: MouthOrgan;
37
- self?: SelfRepository;
38
- externalKnowledge?: EKnowledgeOrgan;
39
- actionStore?: ActionStore;
40
- actionPolicy?: ActionPolicyEngine;
41
- }
42
- export interface CompanionPerception {
43
- source: string;
44
- text?: string;
45
- audioBuffer?: Uint8Array;
46
- roleOrContext?: 'OWNER' | 'VIEWER' | 'OPERATOR' | RequestContext | string;
47
- context?: RequestContext;
48
- history?: Message[];
49
- medium?: MouthMedium;
50
- metadata?: Record<string, unknown>;
51
- signal?: AbortSignal;
52
- }
1
+ import { BrainOrgan, MemoryOrgan, VoiceOrgan, KnowledgeOrgan, VisionOrgan, BehaviorOrgan, BodyOrgan, HandsOrgan, EarOrgan, ObservationOrgan, MouthOrgan, SelfRepository, EKnowledgeOrgan, Message, RequestContext, MouthMedium, ResponseGatingEngine, ActionPolicyEngine, ExperienceDispatcher } from './index';
2
+ import { SessionHistoryManager } from './session-history';
3
+ import { CompanionPerception, PerceptionPipeline } from './perception-pipeline';
4
+ import { CompanionContainer, RuntimeOrgans, SiduriRuntimeConfig } from './container';
5
+ export { CompanionPerception, RuntimeOrgans, SiduriRuntimeConfig };
53
6
  /**
54
- * SiduriRuntime coordinates companion lifecycle, sensory perception,
55
- * context retrieval, cognition planning, safety gating, action execution,
56
- * and experience emission across decoupled organs.
7
+ * SiduriRuntime coordinates companion perception and cognition execution.
8
+ * Lifecycle management and organ storage are handled by CompanionContainer.
9
+ * The 12-stage perception cycle is executed via PerceptionPipeline.
57
10
  */
58
11
  export declare class SiduriRuntime {
59
- id: string;
60
- config: SiduriRuntimeConfig;
61
- brain?: BrainOrgan;
62
- memory?: MemoryOrgan;
63
- voice?: VoiceOrgan | ExperienceAdapter;
64
- knowledge?: KnowledgeOrgan;
65
- vision?: VisionOrgan;
66
- behavior?: BehaviorOrgan;
67
- body?: BodyOrgan | ExperienceAdapter;
68
- hands?: HandsOrgan;
69
- ear?: EarOrgan;
70
- observation?: ObservationOrgan;
71
- mouth?: MouthOrgan;
72
- self?: SelfRepository;
73
- externalKnowledge?: EKnowledgeOrgan;
74
- gating: ResponseGatingEngine;
75
- actionPolicy: ActionPolicyEngine;
76
- dispatcher: ExperienceDispatcher;
77
- private readonly sessionHistory;
12
+ readonly id: string;
13
+ readonly config: SiduriRuntimeConfig;
14
+ readonly container: CompanionContainer;
15
+ readonly pipeline: PerceptionPipeline;
16
+ constructor(id: string, config: SiduriRuntimeConfig, containerOrOrgans?: CompanionContainer | RuntimeOrgans, pipeline?: PerceptionPipeline);
17
+ get organs(): RuntimeOrgans;
18
+ get brain(): BrainOrgan | undefined;
19
+ get memory(): MemoryOrgan | undefined;
20
+ get voice(): VoiceOrgan | undefined;
21
+ get knowledge(): KnowledgeOrgan | undefined;
22
+ get vision(): VisionOrgan | undefined;
23
+ get behavior(): BehaviorOrgan | undefined;
24
+ get body(): BodyOrgan | undefined;
25
+ get hands(): HandsOrgan | undefined;
26
+ get ear(): EarOrgan | undefined;
27
+ get observation(): ObservationOrgan | undefined;
28
+ set observation(org: ObservationOrgan | undefined);
29
+ get mouth(): MouthOrgan | undefined;
30
+ get self(): SelfRepository | undefined;
31
+ get externalKnowledge(): EKnowledgeOrgan | undefined;
32
+ get gating(): ResponseGatingEngine;
33
+ get actionPolicy(): ActionPolicyEngine;
34
+ get dispatcher(): ExperienceDispatcher;
35
+ get sessionHistory(): SessionHistoryManager;
78
36
  get conversationHistory(): Message[];
79
37
  set conversationHistory(messages: Message[]);
80
- constructor(id: string, config: SiduriRuntimeConfig, organs?: RuntimeOrgans);
81
38
  initialize(): Promise<void>;
82
39
  getSessionHistory(sessionKey: string): Message[];
83
40
  clearHistory(sessionKey?: string): void;
84
- analyzeVision(imageUrl: string, prompt: string): Promise<string>;
85
- ingestObservation(frame: Uint8Array, sourceName: string, providerId?: string): Promise<ObservationResult>;
86
- getCurrentObservations(now?: Date): Observation[];
87
- clearExpiredObservations(now?: Date): number;
88
- getClaims(limit?: number): Promise<Claim[]>;
89
- getPendingClaims(limit?: number): Promise<Claim[]>;
90
- getDirectives(): Promise<BehaviorDirective[]>;
91
- approveClaim(id: string): Promise<void>;
92
- rejectClaim(id: string): Promise<void>;
93
- updateClaim(id: string, updates: Partial<Pick<Claim, 'subject' | 'predicate' | 'value' | 'scope' | 'sensitivity' | 'confidence' | 'validFrom' | 'validUntil'>>): Promise<Claim>;
94
- approveDirective(id: string): Promise<void>;
95
- rejectDirective(id: string): Promise<void>;
96
- revokeDirective(id: string): Promise<void>;
97
- disableDirective(id: string): Promise<void>;
98
- resetMemory(): Promise<void>;
99
- stageResponse(options: StageResponseOptions): StagedResponsePlan;
100
- evaluateGate(plan: StagedResponsePlan, evidenceRecords?: EvidenceRecord[]): ResponseGateEvaluation;
101
- approveResponse(options: ApproveResponseOptions): {
102
- success: boolean;
103
- reason?: string;
104
- plan?: StagedResponsePlan;
105
- };
106
- rejectResponse(options: RejectResponseOptions): {
107
- success: boolean;
108
- reason?: string;
109
- plan?: StagedResponsePlan;
110
- };
111
- getStagedPlan(responseId: string): StagedResponsePlan | undefined;
112
- findStagedPlanByCorrelation(companionId: string, correlationId: string): StagedResponsePlan | undefined;
113
41
  /**
114
42
  * Processes an incoming perception (sensory audio, text, platform event, or observation alert)
115
- * through the perception -> retrieval -> cognition -> gating -> action -> experience cycle.
43
+ * through the decoupled PerceptionPipeline.
116
44
  */
117
45
  processPerception(perception: CompanionPerception): Promise<any>;
118
- speakMouth(utterance: MouthUtterance): Promise<FormattedMouthOutput | undefined>;
119
- formatMouth(utterance: MouthUtterance, medium?: MouthMedium): FormattedMouthOutput | undefined;
120
- registerMouthChannel(channel: MouthChannel): void;
121
- unregisterMouthChannel(channelId: string): void;
122
- broadcastMouth(utterance: MouthUtterance): Promise<FormattedMouthOutput[]>;
123
- interruptMouth(reason?: string): void;
124
- streamMouth(utterance: MouthUtterance): AsyncIterable<MouthStreamChunk>;
46
+ /**
47
+ * Primary entrypoint for text chat messages.
48
+ */
125
49
  handleUserMessage(message: string, roleOrContext?: 'OWNER' | 'VIEWER' | 'OPERATOR' | RequestContext | string, history?: Message[], medium?: MouthMedium, signal?: AbortSignal): Promise<any>;
126
50
  }