@siduri-x/core 1.0.5 → 1.0.7
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/dist/action-executor.d.ts +12 -0
- package/dist/action-executor.js +50 -0
- package/dist/action-policy.d.ts +45 -0
- package/dist/action-policy.js +219 -0
- package/dist/action-policy.test.d.ts +1 -0
- package/dist/action-policy.test.js +194 -0
- package/dist/action.d.ts +72 -0
- package/dist/action.js +2 -0
- package/dist/adversarial.test.d.ts +1 -0
- package/dist/adversarial.test.js +493 -0
- package/dist/architecture-boundary.test.d.ts +1 -0
- package/dist/architecture-boundary.test.js +117 -0
- package/dist/capability.d.ts +65 -0
- package/dist/capability.js +157 -0
- package/dist/capability.test.d.ts +1 -0
- package/dist/capability.test.js +269 -0
- package/dist/chat-contract.d.ts +81 -0
- package/dist/chat-contract.js +68 -0
- package/dist/cognition-planner.d.ts +15 -0
- package/dist/cognition-planner.js +23 -0
- package/dist/context-retriever.d.ts +24 -0
- package/dist/context-retriever.js +92 -0
- package/dist/context.d.ts +45 -0
- package/dist/context.js +72 -0
- package/dist/context.test.d.ts +1 -0
- package/dist/context.test.js +76 -0
- package/dist/dispatcher.d.ts +14 -0
- package/dist/dispatcher.js +40 -0
- package/dist/dispatcher.test.d.ts +1 -0
- package/dist/dispatcher.test.js +60 -0
- package/dist/ear-types.d.ts +33 -0
- package/dist/ear-types.js +2 -0
- package/dist/evidence.d.ts +76 -0
- package/dist/evidence.js +47 -0
- package/dist/evidence.test.d.ts +1 -0
- package/dist/evidence.test.js +101 -0
- package/dist/experience-emitter.d.ts +21 -0
- package/dist/experience-emitter.js +48 -0
- package/dist/experience.d.ts +57 -0
- package/dist/experience.js +79 -0
- package/dist/experience.test.d.ts +1 -0
- package/dist/experience.test.js +59 -0
- package/dist/gating.d.ts +46 -0
- package/dist/gating.js +185 -0
- package/dist/gating.test.d.ts +1 -0
- package/dist/gating.test.js +190 -0
- package/dist/index.d.ts +250 -0
- package/dist/index.js +43 -0
- package/dist/input-normalizer.d.ts +14 -0
- package/dist/input-normalizer.js +58 -0
- package/dist/input-normalizer.test.d.ts +1 -0
- package/dist/input-normalizer.test.js +39 -0
- package/dist/intent-classifier.d.ts +24 -0
- package/dist/intent-classifier.js +53 -0
- package/dist/intent-classifier.test.d.ts +1 -0
- package/dist/intent-classifier.test.js +68 -0
- package/dist/memory-settler.d.ts +27 -0
- package/dist/memory-settler.js +95 -0
- package/dist/mouth-types.d.ts +85 -0
- package/dist/mouth-types.js +2 -0
- package/dist/perception-cycle.test.d.ts +1 -0
- package/dist/perception-cycle.test.js +155 -0
- package/dist/prompt-compiler.d.ts +20 -0
- package/dist/prompt-compiler.js +57 -0
- package/dist/prompt-compiler.test.d.ts +1 -0
- package/dist/prompt-compiler.test.js +76 -0
- package/dist/proposals.d.ts +30 -0
- package/dist/proposals.js +2 -0
- package/dist/response-envelope.d.ts +25 -0
- package/dist/response-envelope.js +64 -0
- package/dist/runtime-facades.test.d.ts +1 -0
- package/dist/runtime-facades.test.js +69 -0
- package/dist/runtime.d.ts +114 -0
- package/dist/runtime.js +412 -0
- package/dist/session-history.d.ts +20 -0
- package/dist/session-history.js +55 -0
- package/dist/session-history.test.d.ts +1 -0
- package/dist/session-history.test.js +38 -0
- package/dist/sqlite-action-store.d.ts +20 -0
- package/dist/sqlite-action-store.js +225 -0
- package/dist/sqlite-action-store.test.d.ts +1 -0
- package/dist/sqlite-action-store.test.js +252 -0
- package/dist/teaching.d.ts +15 -0
- package/dist/teaching.js +159 -0
- package/package.json +1 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createGateRejectionEnvelope = createGateRejectionEnvelope;
|
|
4
|
+
exports.assembleResponseEnvelope = assembleResponseEnvelope;
|
|
5
|
+
/**
|
|
6
|
+
* Creates the standardized rejection envelope for responses that fail gate evaluation.
|
|
7
|
+
*/
|
|
8
|
+
function createGateRejectionEnvelope(stagedPlan, gateEval) {
|
|
9
|
+
return {
|
|
10
|
+
status: gateEval.disposition,
|
|
11
|
+
reasonCode: gateEval.reasonCode,
|
|
12
|
+
response_id: stagedPlan.responseId,
|
|
13
|
+
correlation_id: stagedPlan.correlationId,
|
|
14
|
+
response: {
|
|
15
|
+
subtitle_ja: undefined,
|
|
16
|
+
subtitle_en: undefined,
|
|
17
|
+
},
|
|
18
|
+
metadata: {
|
|
19
|
+
requires_approval: stagedPlan.requiresApproval,
|
|
20
|
+
staged: true,
|
|
21
|
+
confidence: stagedPlan.confidenceSummary,
|
|
22
|
+
uncertainty: stagedPlan.uncertaintySummary,
|
|
23
|
+
proposals: [],
|
|
24
|
+
memory_proposals: [],
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Assembles the standardized response structure for approved companion responses.
|
|
30
|
+
*/
|
|
31
|
+
function assembleResponseEnvelope(params) {
|
|
32
|
+
const { stagedPlan, speech, language, speechId, createdMemoryProposals, memoryProposalReceipts, actionResults, filteredEvidenceIds, filteredCitations, subsystemDiagnostics, experienceEvents, mouthDelivery, } = params;
|
|
33
|
+
return {
|
|
34
|
+
status: 'APPROVED',
|
|
35
|
+
response_id: stagedPlan.responseId,
|
|
36
|
+
correlation_id: stagedPlan.correlationId,
|
|
37
|
+
response: {
|
|
38
|
+
speech_id: speechId,
|
|
39
|
+
audio_url: mouthDelivery?.audioUrl ?? (speechId ? `/voice/stream?id=${speechId}` : undefined),
|
|
40
|
+
subtitle_ja: mouthDelivery?.subtitles?.ja ?? speech,
|
|
41
|
+
subtitle_en: mouthDelivery?.subtitles?.en ?? speech,
|
|
42
|
+
},
|
|
43
|
+
delivery: mouthDelivery,
|
|
44
|
+
metadata: {
|
|
45
|
+
language,
|
|
46
|
+
proposals: createdMemoryProposals,
|
|
47
|
+
memory_proposals: memoryProposalReceipts,
|
|
48
|
+
action_results: actionResults,
|
|
49
|
+
evidence_ids: filteredEvidenceIds,
|
|
50
|
+
citations: filteredCitations,
|
|
51
|
+
subsystem_diagnostics: Object.keys(subsystemDiagnostics).length > 0
|
|
52
|
+
? subsystemDiagnostics
|
|
53
|
+
: undefined,
|
|
54
|
+
events: experienceEvents.map((e) => ({
|
|
55
|
+
event_id: e.eventId,
|
|
56
|
+
kind: e.kind,
|
|
57
|
+
lifecycle: e.lifecycle,
|
|
58
|
+
approval: e.approval,
|
|
59
|
+
expression: e.expression,
|
|
60
|
+
action: e.action,
|
|
61
|
+
})),
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
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 () => {
|
|
6
|
+
const mockVision = {
|
|
7
|
+
analyze: jest.fn().mockResolvedValue('an ancient artifact'),
|
|
8
|
+
};
|
|
9
|
+
const mockObservation = {
|
|
10
|
+
ingest: jest.fn().mockResolvedValue({ duplicate: false }),
|
|
11
|
+
current: jest.fn().mockReturnValue([{ observationId: 'obs-1' }]),
|
|
12
|
+
clearExpired: jest.fn().mockReturnValue(1),
|
|
13
|
+
};
|
|
14
|
+
const runtime = new runtime_1.SiduriRuntime('test-comp', { name: 'Test' }, {
|
|
15
|
+
vision: mockVision,
|
|
16
|
+
observation: mockObservation,
|
|
17
|
+
});
|
|
18
|
+
const visionResult = await runtime.analyzeVision('http://example.com/img.png', 'describe');
|
|
19
|
+
expect(visionResult).toBe('an ancient artifact');
|
|
20
|
+
expect(mockVision.analyze).toHaveBeenCalledWith('http://example.com/img.png', 'describe');
|
|
21
|
+
const frame = new Uint8Array([1, 2, 3]);
|
|
22
|
+
const obsResult = await runtime.ingestObservation(frame, 'camera-1', 'provider-x');
|
|
23
|
+
expect(obsResult.duplicate).toBe(false);
|
|
24
|
+
expect(mockObservation.ingest).toHaveBeenCalledWith(frame, 'camera-1', 'provider-x');
|
|
25
|
+
const cur = runtime.getCurrentObservations();
|
|
26
|
+
expect(cur).toEqual([{ observationId: 'obs-1' }]);
|
|
27
|
+
const cleared = runtime.clearExpiredObservations();
|
|
28
|
+
expect(cleared).toBe(1);
|
|
29
|
+
});
|
|
30
|
+
test('delegates memory operations cleanly through facade methods', async () => {
|
|
31
|
+
const mockMemory = {
|
|
32
|
+
initialize: jest.fn().mockResolvedValue(undefined),
|
|
33
|
+
getClaims: jest.fn().mockResolvedValue([{ id: 'claim-1' }]),
|
|
34
|
+
getPendingClaims: jest.fn().mockResolvedValue([{ id: 'claim-pending-1' }]),
|
|
35
|
+
getDirectives: jest.fn().mockResolvedValue([{ id: 'dir-1' }]),
|
|
36
|
+
approveClaim: jest.fn().mockResolvedValue(undefined),
|
|
37
|
+
rejectClaim: jest.fn().mockResolvedValue(undefined),
|
|
38
|
+
updateClaim: jest.fn().mockResolvedValue({ id: 'claim-1', value: 'updated' }),
|
|
39
|
+
approveDirective: jest.fn().mockResolvedValue(undefined),
|
|
40
|
+
rejectDirective: jest.fn().mockResolvedValue(undefined),
|
|
41
|
+
revokeDirective: jest.fn().mockResolvedValue(undefined),
|
|
42
|
+
disableDirective: jest.fn().mockResolvedValue(undefined),
|
|
43
|
+
resetMemory: jest.fn().mockResolvedValue(undefined),
|
|
44
|
+
};
|
|
45
|
+
const runtime = new runtime_1.SiduriRuntime('test-comp', { name: 'Test' }, {
|
|
46
|
+
memory: mockMemory,
|
|
47
|
+
});
|
|
48
|
+
expect(await runtime.getClaims(10)).toEqual([{ id: 'claim-1' }]);
|
|
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');
|
|
53
|
+
expect(mockMemory.approveClaim).toHaveBeenCalledWith('c-1');
|
|
54
|
+
await runtime.rejectClaim('c-2');
|
|
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');
|
|
59
|
+
expect(mockMemory.approveDirective).toHaveBeenCalledWith('d-1');
|
|
60
|
+
await runtime.rejectDirective('d-2');
|
|
61
|
+
expect(mockMemory.rejectDirective).toHaveBeenCalledWith('d-2');
|
|
62
|
+
await runtime.revokeDirective('d-3');
|
|
63
|
+
expect(mockMemory.revokeDirective).toHaveBeenCalledWith('d-3');
|
|
64
|
+
await runtime.disableDirective('d-4');
|
|
65
|
+
expect(mockMemory.disableDirective).toHaveBeenCalledWith('d-4');
|
|
66
|
+
await runtime.resetMemory();
|
|
67
|
+
expect(mockMemory.resetMemory).toHaveBeenCalled();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { BrainOrgan, MemoryOrgan, VoiceOrgan, KnowledgeOrgan, VisionOrgan, BehaviorOrgan, BodyOrgan, HandsOrgan, EarOrgan, ObservationOrgan, ObservationResult, Observation, Message, RequestContext, ActionPolicyEngine, ResponseGatingEngine, StageResponseOptions, ApproveResponseOptions, RejectResponseOptions, ExperienceDispatcher, ExperienceAdapter, OrganConfig, Claim, BehaviorDirective, StagedResponsePlan, ResponseGateEvaluation, EvidenceRecord, MouthOrgan, MouthUtterance, MouthMedium, FormattedMouthOutput, MouthStreamChunk, MouthChannel } 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
|
+
actionPolicy?: Record<string, unknown>;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
export interface RuntimeOrgans {
|
|
19
|
+
brain?: BrainOrgan;
|
|
20
|
+
memory?: MemoryOrgan;
|
|
21
|
+
voice?: VoiceOrgan | ExperienceAdapter;
|
|
22
|
+
knowledge?: KnowledgeOrgan;
|
|
23
|
+
vision?: VisionOrgan;
|
|
24
|
+
behavior?: BehaviorOrgan;
|
|
25
|
+
body?: BodyOrgan | ExperienceAdapter;
|
|
26
|
+
hands?: HandsOrgan;
|
|
27
|
+
ear?: EarOrgan;
|
|
28
|
+
observation?: ObservationOrgan;
|
|
29
|
+
mouth?: MouthOrgan;
|
|
30
|
+
actionPolicy?: ActionPolicyEngine;
|
|
31
|
+
}
|
|
32
|
+
export interface CompanionPerception {
|
|
33
|
+
source: string;
|
|
34
|
+
text?: string;
|
|
35
|
+
audioBuffer?: Uint8Array;
|
|
36
|
+
roleOrContext?: 'OWNER' | 'VIEWER' | 'OPERATOR' | RequestContext | string;
|
|
37
|
+
context?: RequestContext;
|
|
38
|
+
history?: Message[];
|
|
39
|
+
medium?: MouthMedium;
|
|
40
|
+
metadata?: Record<string, unknown>;
|
|
41
|
+
signal?: AbortSignal;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* SiduriRuntime coordinates companion lifecycle, sensory perception,
|
|
45
|
+
* context retrieval, cognition planning, safety gating, action execution,
|
|
46
|
+
* and experience emission across decoupled organs.
|
|
47
|
+
*/
|
|
48
|
+
export declare class SiduriRuntime {
|
|
49
|
+
id: string;
|
|
50
|
+
config: SiduriRuntimeConfig;
|
|
51
|
+
brain?: BrainOrgan;
|
|
52
|
+
memory?: MemoryOrgan;
|
|
53
|
+
voice?: VoiceOrgan | ExperienceAdapter;
|
|
54
|
+
knowledge?: KnowledgeOrgan;
|
|
55
|
+
vision?: VisionOrgan;
|
|
56
|
+
behavior?: BehaviorOrgan;
|
|
57
|
+
body?: BodyOrgan | ExperienceAdapter;
|
|
58
|
+
hands?: HandsOrgan;
|
|
59
|
+
ear?: EarOrgan;
|
|
60
|
+
observation?: ObservationOrgan;
|
|
61
|
+
mouth?: MouthOrgan;
|
|
62
|
+
gating: ResponseGatingEngine;
|
|
63
|
+
actionPolicy: ActionPolicyEngine;
|
|
64
|
+
dispatcher: ExperienceDispatcher;
|
|
65
|
+
private readonly sessionHistory;
|
|
66
|
+
get conversationHistory(): Message[];
|
|
67
|
+
set conversationHistory(messages: Message[]);
|
|
68
|
+
constructor(id: string, config: SiduriRuntimeConfig, organs?: RuntimeOrgans);
|
|
69
|
+
initialize(): Promise<void>;
|
|
70
|
+
getSessionHistory(sessionKey: string): Message[];
|
|
71
|
+
clearHistory(sessionKey?: string): void;
|
|
72
|
+
analyzeVision(imageUrl: string, prompt: string): Promise<string>;
|
|
73
|
+
ingestObservation(frame: Uint8Array, sourceName: string, providerId?: string): Promise<ObservationResult>;
|
|
74
|
+
getCurrentObservations(now?: Date): Observation[];
|
|
75
|
+
clearExpiredObservations(now?: Date): number;
|
|
76
|
+
getClaims(limit?: number): Promise<Claim[]>;
|
|
77
|
+
getPendingClaims(limit?: number): Promise<Claim[]>;
|
|
78
|
+
getDirectives(): Promise<BehaviorDirective[]>;
|
|
79
|
+
approveClaim(id: string): Promise<void>;
|
|
80
|
+
rejectClaim(id: string): Promise<void>;
|
|
81
|
+
updateClaim(id: string, updates: Partial<Pick<Claim, 'subject' | 'predicate' | 'value' | 'scope' | 'sensitivity' | 'confidence' | 'validFrom' | 'validUntil'>>): Promise<Claim>;
|
|
82
|
+
approveDirective(id: string): Promise<void>;
|
|
83
|
+
rejectDirective(id: string): Promise<void>;
|
|
84
|
+
revokeDirective(id: string): Promise<void>;
|
|
85
|
+
disableDirective(id: string): Promise<void>;
|
|
86
|
+
resetMemory(): Promise<void>;
|
|
87
|
+
stageResponse(options: StageResponseOptions): StagedResponsePlan;
|
|
88
|
+
evaluateGate(plan: StagedResponsePlan, evidenceRecords?: EvidenceRecord[]): ResponseGateEvaluation;
|
|
89
|
+
approveResponse(options: ApproveResponseOptions): {
|
|
90
|
+
success: boolean;
|
|
91
|
+
reason?: string;
|
|
92
|
+
plan?: StagedResponsePlan;
|
|
93
|
+
};
|
|
94
|
+
rejectResponse(options: RejectResponseOptions): {
|
|
95
|
+
success: boolean;
|
|
96
|
+
reason?: string;
|
|
97
|
+
plan?: StagedResponsePlan;
|
|
98
|
+
};
|
|
99
|
+
getStagedPlan(responseId: string): StagedResponsePlan | undefined;
|
|
100
|
+
findStagedPlanByCorrelation(companionId: string, correlationId: string): StagedResponsePlan | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* Processes an incoming perception (sensory audio, text, platform event, or observation alert)
|
|
103
|
+
* through the perception -> retrieval -> cognition -> gating -> action -> experience cycle.
|
|
104
|
+
*/
|
|
105
|
+
processPerception(perception: CompanionPerception): Promise<any>;
|
|
106
|
+
speakMouth(utterance: MouthUtterance): Promise<FormattedMouthOutput | undefined>;
|
|
107
|
+
formatMouth(utterance: MouthUtterance, medium?: MouthMedium): FormattedMouthOutput | undefined;
|
|
108
|
+
registerMouthChannel(channel: MouthChannel): void;
|
|
109
|
+
unregisterMouthChannel(channelId: string): void;
|
|
110
|
+
broadcastMouth(utterance: MouthUtterance): Promise<FormattedMouthOutput[]>;
|
|
111
|
+
interruptMouth(reason?: string): void;
|
|
112
|
+
streamMouth(utterance: MouthUtterance): AsyncIterable<MouthStreamChunk>;
|
|
113
|
+
handleUserMessage(message: string, roleOrContext?: 'OWNER' | 'VIEWER' | 'OPERATOR' | RequestContext | string, history?: Message[], medium?: MouthMedium, signal?: AbortSignal): Promise<any>;
|
|
114
|
+
}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SiduriRuntime = void 0;
|
|
4
|
+
const index_1 = require("./index");
|
|
5
|
+
const input_normalizer_1 = require("./input-normalizer");
|
|
6
|
+
const intent_classifier_1 = require("./intent-classifier");
|
|
7
|
+
const context_retriever_1 = require("./context-retriever");
|
|
8
|
+
const prompt_compiler_1 = require("./prompt-compiler");
|
|
9
|
+
const cognition_planner_1 = require("./cognition-planner");
|
|
10
|
+
const memory_settler_1 = require("./memory-settler");
|
|
11
|
+
const action_executor_1 = require("./action-executor");
|
|
12
|
+
const experience_emitter_1 = require("./experience-emitter");
|
|
13
|
+
const response_envelope_1 = require("./response-envelope");
|
|
14
|
+
const session_history_1 = require("./session-history");
|
|
15
|
+
/**
|
|
16
|
+
* SiduriRuntime coordinates companion lifecycle, sensory perception,
|
|
17
|
+
* context retrieval, cognition planning, safety gating, action execution,
|
|
18
|
+
* and experience emission across decoupled organs.
|
|
19
|
+
*/
|
|
20
|
+
class SiduriRuntime {
|
|
21
|
+
id;
|
|
22
|
+
config;
|
|
23
|
+
brain;
|
|
24
|
+
memory;
|
|
25
|
+
voice;
|
|
26
|
+
knowledge;
|
|
27
|
+
vision;
|
|
28
|
+
behavior;
|
|
29
|
+
body;
|
|
30
|
+
hands;
|
|
31
|
+
ear;
|
|
32
|
+
observation;
|
|
33
|
+
mouth;
|
|
34
|
+
gating;
|
|
35
|
+
actionPolicy;
|
|
36
|
+
dispatcher;
|
|
37
|
+
sessionHistory = new session_history_1.SessionHistoryManager();
|
|
38
|
+
// Backward-compatible getter/setter for conversation history
|
|
39
|
+
get conversationHistory() {
|
|
40
|
+
return this.sessionHistory.getHistory('default');
|
|
41
|
+
}
|
|
42
|
+
set conversationHistory(messages) {
|
|
43
|
+
this.sessionHistory.setHistory('default', messages);
|
|
44
|
+
}
|
|
45
|
+
constructor(id, config, organs = {}) {
|
|
46
|
+
this.id = id;
|
|
47
|
+
this.config = config;
|
|
48
|
+
this.brain = organs.brain;
|
|
49
|
+
this.memory = organs.memory;
|
|
50
|
+
this.voice = organs.voice;
|
|
51
|
+
this.knowledge = organs.knowledge;
|
|
52
|
+
this.vision = organs.vision;
|
|
53
|
+
this.behavior = organs.behavior;
|
|
54
|
+
this.body = organs.body;
|
|
55
|
+
this.hands = organs.hands;
|
|
56
|
+
this.ear = organs.ear;
|
|
57
|
+
this.observation = organs.observation;
|
|
58
|
+
this.mouth = organs.mouth;
|
|
59
|
+
this.gating = new index_1.ResponseGatingEngine();
|
|
60
|
+
this.actionPolicy = organs.actionPolicy || new index_1.ActionPolicyEngine();
|
|
61
|
+
this.dispatcher = new index_1.ExperienceDispatcher();
|
|
62
|
+
if (this.voice && typeof this.voice.handleEvent === 'function') {
|
|
63
|
+
this.dispatcher.registerAdapter(this.voice);
|
|
64
|
+
}
|
|
65
|
+
if (this.body && typeof this.body.handleEvent === 'function') {
|
|
66
|
+
this.dispatcher.registerAdapter(this.body);
|
|
67
|
+
}
|
|
68
|
+
if (this.mouth && typeof this.mouth.handleEvent === 'function') {
|
|
69
|
+
this.dispatcher.registerAdapter(this.mouth);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async initialize() {
|
|
73
|
+
if (this.memory && typeof this.memory.initialize === 'function') {
|
|
74
|
+
await this.memory.initialize(this.id);
|
|
75
|
+
}
|
|
76
|
+
if (this.hands && typeof this.hands.listTools === 'function') {
|
|
77
|
+
const tools = await this.hands.listTools();
|
|
78
|
+
for (const tool of tools) {
|
|
79
|
+
this.actionPolicy.registerToolDefinition(tool);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// --- Session History Accessors ---
|
|
84
|
+
getSessionHistory(sessionKey) {
|
|
85
|
+
return this.sessionHistory.getHistory(sessionKey);
|
|
86
|
+
}
|
|
87
|
+
clearHistory(sessionKey) {
|
|
88
|
+
this.sessionHistory.clear(sessionKey);
|
|
89
|
+
}
|
|
90
|
+
// --- Observation & Vision Facades ---
|
|
91
|
+
async analyzeVision(imageUrl, prompt) {
|
|
92
|
+
if (!this.vision || typeof this.vision.analyze !== 'function') {
|
|
93
|
+
throw new Error('Vision organ is not configured on this runtime');
|
|
94
|
+
}
|
|
95
|
+
return this.vision.analyze(imageUrl, prompt);
|
|
96
|
+
}
|
|
97
|
+
async ingestObservation(frame, sourceName, providerId) {
|
|
98
|
+
if (!this.observation || typeof this.observation.ingest !== 'function') {
|
|
99
|
+
return { duplicate: false, reason: 'provider_failure' };
|
|
100
|
+
}
|
|
101
|
+
return this.observation.ingest(frame, sourceName, providerId);
|
|
102
|
+
}
|
|
103
|
+
getCurrentObservations(now) {
|
|
104
|
+
if (!this.observation || typeof this.observation.current !== 'function') {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
return this.observation.current(now);
|
|
108
|
+
}
|
|
109
|
+
clearExpiredObservations(now) {
|
|
110
|
+
if (!this.observation || typeof this.observation.clearExpired !== 'function') {
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
return this.observation.clearExpired(now);
|
|
114
|
+
}
|
|
115
|
+
// --- Memory Facades ---
|
|
116
|
+
async getClaims(limit) {
|
|
117
|
+
if (!this.memory || typeof this.memory.getClaims !== 'function') {
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
return this.memory.getClaims(limit);
|
|
121
|
+
}
|
|
122
|
+
async getPendingClaims(limit) {
|
|
123
|
+
if (!this.memory || typeof this.memory.getPendingClaims !== 'function') {
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
return this.memory.getPendingClaims(limit);
|
|
127
|
+
}
|
|
128
|
+
async getDirectives() {
|
|
129
|
+
if (!this.memory || typeof this.memory.getDirectives !== 'function') {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
return this.memory.getDirectives();
|
|
133
|
+
}
|
|
134
|
+
async approveClaim(id) {
|
|
135
|
+
if (!this.memory || typeof this.memory.approveClaim !== 'function') {
|
|
136
|
+
throw new Error('Memory organ not configured');
|
|
137
|
+
}
|
|
138
|
+
return this.memory.approveClaim(id);
|
|
139
|
+
}
|
|
140
|
+
async rejectClaim(id) {
|
|
141
|
+
if (!this.memory || typeof this.memory.rejectClaim !== 'function') {
|
|
142
|
+
throw new Error('Memory organ not configured');
|
|
143
|
+
}
|
|
144
|
+
return this.memory.rejectClaim(id);
|
|
145
|
+
}
|
|
146
|
+
async updateClaim(id, updates) {
|
|
147
|
+
if (!this.memory || typeof this.memory.updateClaim !== 'function') {
|
|
148
|
+
throw new Error('Memory organ updateClaim not supported');
|
|
149
|
+
}
|
|
150
|
+
return this.memory.updateClaim(id, updates);
|
|
151
|
+
}
|
|
152
|
+
async approveDirective(id) {
|
|
153
|
+
if (!this.memory || typeof this.memory.approveDirective !== 'function') {
|
|
154
|
+
throw new Error('Memory organ not configured');
|
|
155
|
+
}
|
|
156
|
+
return this.memory.approveDirective(id);
|
|
157
|
+
}
|
|
158
|
+
async rejectDirective(id) {
|
|
159
|
+
if (!this.memory || typeof this.memory.rejectDirective !== 'function') {
|
|
160
|
+
throw new Error('Memory organ not configured');
|
|
161
|
+
}
|
|
162
|
+
return this.memory.rejectDirective(id);
|
|
163
|
+
}
|
|
164
|
+
async revokeDirective(id) {
|
|
165
|
+
if (!this.memory || typeof this.memory.revokeDirective !== 'function') {
|
|
166
|
+
throw new Error('Memory organ not configured');
|
|
167
|
+
}
|
|
168
|
+
return this.memory.revokeDirective(id);
|
|
169
|
+
}
|
|
170
|
+
async disableDirective(id) {
|
|
171
|
+
if (!this.memory || typeof this.memory.disableDirective !== 'function') {
|
|
172
|
+
throw new Error('Memory organ not configured');
|
|
173
|
+
}
|
|
174
|
+
return this.memory.disableDirective(id);
|
|
175
|
+
}
|
|
176
|
+
async resetMemory() {
|
|
177
|
+
if (!this.memory || typeof this.memory.resetMemory !== 'function') {
|
|
178
|
+
throw new Error('Memory organ does not support reset');
|
|
179
|
+
}
|
|
180
|
+
return this.memory.resetMemory();
|
|
181
|
+
}
|
|
182
|
+
// --- Response Gating Facades ---
|
|
183
|
+
stageResponse(options) {
|
|
184
|
+
return this.gating.stageResponse(options);
|
|
185
|
+
}
|
|
186
|
+
evaluateGate(plan, evidenceRecords) {
|
|
187
|
+
return this.gating.evaluateGate(plan, evidenceRecords);
|
|
188
|
+
}
|
|
189
|
+
approveResponse(options) {
|
|
190
|
+
return this.gating.approveResponse(options);
|
|
191
|
+
}
|
|
192
|
+
rejectResponse(options) {
|
|
193
|
+
return this.gating.rejectResponse(options);
|
|
194
|
+
}
|
|
195
|
+
getStagedPlan(responseId) {
|
|
196
|
+
return this.gating.getStagedPlan(responseId);
|
|
197
|
+
}
|
|
198
|
+
findStagedPlanByCorrelation(companionId, correlationId) {
|
|
199
|
+
return this.gating.findStagedPlanByCorrelation(companionId, correlationId);
|
|
200
|
+
}
|
|
201
|
+
// --- Universal Perception & Cognition Cycle ---
|
|
202
|
+
/**
|
|
203
|
+
* Processes an incoming perception (sensory audio, text, platform event, or observation alert)
|
|
204
|
+
* through the perception -> retrieval -> cognition -> gating -> action -> experience cycle.
|
|
205
|
+
*/
|
|
206
|
+
async processPerception(perception) {
|
|
207
|
+
let rawText = perception.text || '';
|
|
208
|
+
// If audio buffer is provided and Ear supports transcription, transcribe it
|
|
209
|
+
if (!rawText && perception.audioBuffer && this.ear && typeof this.ear.transcribeAudio === 'function') {
|
|
210
|
+
rawText = await this.ear.transcribeAudio(perception.audioBuffer);
|
|
211
|
+
}
|
|
212
|
+
const roleOrContext = perception.context || perception.roleOrContext || 'OWNER';
|
|
213
|
+
const history = perception.history || [];
|
|
214
|
+
// 1. Input validation, RequestContext synthesis, and Ear perception routing
|
|
215
|
+
const input = await (0, input_normalizer_1.normalizeUserInput)(rawText, roleOrContext, history, this.id, this.ear);
|
|
216
|
+
const sessionKey = input.requestContext.actor.sessionId ||
|
|
217
|
+
input.requestContext.conversation.audienceId ||
|
|
218
|
+
'default';
|
|
219
|
+
const currentMessage = { role: 'user', content: input.perceivedText };
|
|
220
|
+
const boundedSessionHistory = [...input.boundedHistory, currentMessage].slice(-20);
|
|
221
|
+
this.sessionHistory.setHistory(sessionKey, boundedSessionHistory);
|
|
222
|
+
this.sessionHistory.setHistory('default', boundedSessionHistory);
|
|
223
|
+
// 2. Intent classification (delegating to Ear if available, else heuristics)
|
|
224
|
+
const intent = await (0, intent_classifier_1.classifyInputIntentAsync)(input.perceivedText, input.requestContext, this.ear?.classifyIntent
|
|
225
|
+
? (t, c) => this.ear.classifyIntent(t, c)
|
|
226
|
+
: undefined);
|
|
227
|
+
// 3. Concurrent Knowledge & Memory retrieval with diagnostics & evidence handling
|
|
228
|
+
const contextRetrieval = await (0, context_retriever_1.retrieveRuntimeContext)({
|
|
229
|
+
companionId: this.id,
|
|
230
|
+
perceivedText: input.perceivedText,
|
|
231
|
+
requestContext: input.requestContext,
|
|
232
|
+
role: input.role,
|
|
233
|
+
isContextObject: input.isContextObject,
|
|
234
|
+
shouldQueryKnowledge: intent.shouldQueryKnowledge,
|
|
235
|
+
knowledge: this.knowledge,
|
|
236
|
+
memory: this.memory,
|
|
237
|
+
});
|
|
238
|
+
// 4. Neutral system prompt and contextual prompt compilation
|
|
239
|
+
const prompts = await (0, prompt_compiler_1.compilePrompts)({
|
|
240
|
+
companionName: this.config.name,
|
|
241
|
+
companionId: this.id,
|
|
242
|
+
role: input.role,
|
|
243
|
+
requestContext: input.requestContext,
|
|
244
|
+
behavior: this.behavior,
|
|
245
|
+
activeDirectives: contextRetrieval.activeDirectives,
|
|
246
|
+
subsystemDiagnostics: contextRetrieval.subsystemDiagnostics,
|
|
247
|
+
knowledgeData: contextRetrieval.knowledgeData,
|
|
248
|
+
memoryData: contextRetrieval.memoryData,
|
|
249
|
+
});
|
|
250
|
+
// 5. Cognition planning via BrainOrgan
|
|
251
|
+
const plan = await (0, cognition_planner_1.generateCognitionPlan)({
|
|
252
|
+
companionName: this.config.name,
|
|
253
|
+
brain: this.brain,
|
|
254
|
+
systemPrompt: prompts.systemPrompt,
|
|
255
|
+
contextPrompt: prompts.contextPrompt,
|
|
256
|
+
recentMessages: this.sessionHistory.getHistory(sessionKey).slice(-10),
|
|
257
|
+
recipient: input.role,
|
|
258
|
+
perceivedText: input.perceivedText,
|
|
259
|
+
});
|
|
260
|
+
// 6. Stage response and evaluate safety gating boundary
|
|
261
|
+
const stagedPlan = this.gating.stageResponse({
|
|
262
|
+
requestContext: input.requestContext,
|
|
263
|
+
candidateSpeech: plan.speech,
|
|
264
|
+
candidateLanguage: plan.language || 'ja',
|
|
265
|
+
internalMonologue: plan.internalMonologue,
|
|
266
|
+
memoryProposals: plan.memoryProposals,
|
|
267
|
+
behaviorProposals: plan.behaviorProposals,
|
|
268
|
+
evidenceRecords: contextRetrieval.collectedEvidence,
|
|
269
|
+
citations: contextRetrieval.citations,
|
|
270
|
+
});
|
|
271
|
+
const gateEval = this.gating.evaluateGate(stagedPlan, contextRetrieval.collectedEvidence);
|
|
272
|
+
if (!gateEval.admissible) {
|
|
273
|
+
return (0, response_envelope_1.createGateRejectionEnvelope)(stagedPlan, gateEval);
|
|
274
|
+
}
|
|
275
|
+
this.sessionHistory.append(sessionKey, { role: 'assistant', content: plan.speech });
|
|
276
|
+
this.sessionHistory.append('default', { role: 'assistant', content: plan.speech });
|
|
277
|
+
// 7. Settle memory proposals and persist source events
|
|
278
|
+
const memorySettlement = await (0, memory_settler_1.settleMemoryProposals)({
|
|
279
|
+
companionId: this.id,
|
|
280
|
+
perceivedText: input.perceivedText,
|
|
281
|
+
role: input.role,
|
|
282
|
+
requestContext: input.requestContext,
|
|
283
|
+
memory: this.memory,
|
|
284
|
+
explicitTeaching: intent.explicitTeaching,
|
|
285
|
+
plan,
|
|
286
|
+
});
|
|
287
|
+
// 8. Authorize and execute action intents under Primary Security Invariant
|
|
288
|
+
const actionResults = await (0, action_executor_1.executeActionIntents)({
|
|
289
|
+
actionIntents: plan.actionIntents,
|
|
290
|
+
requestContext: input.requestContext,
|
|
291
|
+
actionPolicy: this.actionPolicy,
|
|
292
|
+
hands: this.hands,
|
|
293
|
+
});
|
|
294
|
+
// 9. Dispatch ExperienceEvents to registered adapters
|
|
295
|
+
const experienceEmission = await (0, experience_emitter_1.emitExperienceEvents)({
|
|
296
|
+
companionId: this.id,
|
|
297
|
+
requestContext: input.requestContext,
|
|
298
|
+
stagedPlan,
|
|
299
|
+
gateEval,
|
|
300
|
+
speech: plan.speech,
|
|
301
|
+
language: plan.language || 'ja',
|
|
302
|
+
dispatcher: this.dispatcher,
|
|
303
|
+
voice: this.voice,
|
|
304
|
+
body: this.body,
|
|
305
|
+
});
|
|
306
|
+
// 10. Deliver utterance via Mouth organ (UI / Output Channel Decoupling)
|
|
307
|
+
let mouthDelivery;
|
|
308
|
+
if (this.mouth && typeof this.mouth.speak === 'function') {
|
|
309
|
+
try {
|
|
310
|
+
const avatarEvent = experienceEmission.experienceEvents.find((e) => e.kind === 'avatar');
|
|
311
|
+
mouthDelivery = await this.mouth.speak({
|
|
312
|
+
utteranceId: stagedPlan.responseId,
|
|
313
|
+
companionId: this.id,
|
|
314
|
+
responseId: stagedPlan.responseId,
|
|
315
|
+
correlationId: stagedPlan.correlationId,
|
|
316
|
+
text: plan.speech,
|
|
317
|
+
language: plan.language || 'ja',
|
|
318
|
+
subtitleJa: plan.speech,
|
|
319
|
+
subtitleEn: plan.speech,
|
|
320
|
+
spokenJa: plan.speech,
|
|
321
|
+
expression: avatarEvent?.expression,
|
|
322
|
+
medium: perception.medium,
|
|
323
|
+
signal: perception.signal,
|
|
324
|
+
audioUrl: experienceEmission.speechId
|
|
325
|
+
? `/voice/stream?id=${experienceEmission.speechId}`
|
|
326
|
+
: undefined,
|
|
327
|
+
metadata: {
|
|
328
|
+
subsystemDiagnostics: contextRetrieval.subsystemDiagnostics,
|
|
329
|
+
internalMonologue: plan.internalMonologue,
|
|
330
|
+
},
|
|
331
|
+
citations: gateEval.filteredCitations,
|
|
332
|
+
evidenceIds: gateEval.filteredEvidenceIds,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
catch (e) {
|
|
336
|
+
console.error('[SiduriRuntime] Mouth delivery failed:', e?.message || e);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// 11. Assemble and return response envelope
|
|
340
|
+
return (0, response_envelope_1.assembleResponseEnvelope)({
|
|
341
|
+
stagedPlan,
|
|
342
|
+
speech: plan.speech,
|
|
343
|
+
language: plan.language,
|
|
344
|
+
speechId: experienceEmission.speechId,
|
|
345
|
+
createdMemoryProposals: memorySettlement.createdMemoryProposals,
|
|
346
|
+
memoryProposalReceipts: memorySettlement.memoryProposalReceipts,
|
|
347
|
+
actionResults,
|
|
348
|
+
filteredEvidenceIds: gateEval.filteredEvidenceIds,
|
|
349
|
+
filteredCitations: gateEval.filteredCitations,
|
|
350
|
+
subsystemDiagnostics: contextRetrieval.subsystemDiagnostics,
|
|
351
|
+
experienceEvents: experienceEmission.experienceEvents,
|
|
352
|
+
mouthDelivery,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
// --- Mouth Facades ---
|
|
356
|
+
async speakMouth(utterance) {
|
|
357
|
+
if (!this.mouth || typeof this.mouth.speak !== 'function')
|
|
358
|
+
return undefined;
|
|
359
|
+
return this.mouth.speak(utterance);
|
|
360
|
+
}
|
|
361
|
+
formatMouth(utterance, medium) {
|
|
362
|
+
if (!this.mouth || typeof this.mouth.format !== 'function')
|
|
363
|
+
return undefined;
|
|
364
|
+
return this.mouth.format(utterance, medium);
|
|
365
|
+
}
|
|
366
|
+
registerMouthChannel(channel) {
|
|
367
|
+
if (this.mouth && typeof this.mouth.registerChannel === 'function') {
|
|
368
|
+
this.mouth.registerChannel(channel);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
unregisterMouthChannel(channelId) {
|
|
372
|
+
if (this.mouth && typeof this.mouth.unregisterChannel === 'function') {
|
|
373
|
+
this.mouth.unregisterChannel(channelId);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async broadcastMouth(utterance) {
|
|
377
|
+
if (!this.mouth || typeof this.mouth.broadcast !== 'function')
|
|
378
|
+
return [];
|
|
379
|
+
return this.mouth.broadcast(utterance);
|
|
380
|
+
}
|
|
381
|
+
interruptMouth(reason) {
|
|
382
|
+
if (this.mouth && typeof this.mouth.interrupt === 'function') {
|
|
383
|
+
this.mouth.interrupt(reason);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
streamMouth(utterance) {
|
|
387
|
+
if (!this.mouth || typeof this.mouth.stream !== 'function') {
|
|
388
|
+
return (async function* () {
|
|
389
|
+
yield {
|
|
390
|
+
utteranceId: utterance.utteranceId,
|
|
391
|
+
index: 0,
|
|
392
|
+
deltaText: utterance.text,
|
|
393
|
+
isComplete: true,
|
|
394
|
+
medium: 'web',
|
|
395
|
+
};
|
|
396
|
+
})();
|
|
397
|
+
}
|
|
398
|
+
return this.mouth.stream(utterance);
|
|
399
|
+
}
|
|
400
|
+
// --- Backward-Compatible Chat Adapter ---
|
|
401
|
+
async handleUserMessage(message, roleOrContext = 'OWNER', history = [], medium, signal) {
|
|
402
|
+
return this.processPerception({
|
|
403
|
+
source: 'text_chat',
|
|
404
|
+
text: message,
|
|
405
|
+
roleOrContext,
|
|
406
|
+
history,
|
|
407
|
+
medium,
|
|
408
|
+
signal,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
exports.SiduriRuntime = SiduriRuntime;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Message } from './index';
|
|
2
|
+
export interface SessionHistoryOptions {
|
|
3
|
+
maxSessions?: number;
|
|
4
|
+
maxMessagesPerSession?: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Manages conversation history scoped by session key to prevent cross-session
|
|
8
|
+
* and cross-channel memory/conversation leakage.
|
|
9
|
+
*/
|
|
10
|
+
export declare class SessionHistoryManager {
|
|
11
|
+
private readonly sessions;
|
|
12
|
+
private readonly maxSessions;
|
|
13
|
+
private readonly maxMessagesPerSession;
|
|
14
|
+
constructor(options?: SessionHistoryOptions);
|
|
15
|
+
getHistory(sessionKey?: string): Message[];
|
|
16
|
+
setHistory(sessionKey: string | undefined, history: Message[]): void;
|
|
17
|
+
append(sessionKey: string | undefined, message: Message): void;
|
|
18
|
+
clear(sessionKey?: string): void;
|
|
19
|
+
sessionCount(): number;
|
|
20
|
+
}
|