@siduri-x/core 2.0.1 → 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/dist/action-policy.d.ts +8 -1
- package/dist/action-policy.js +84 -24
- package/dist/action-policy.test.js +67 -1
- package/dist/action.d.ts +1 -1
- package/dist/capability.d.ts +6 -2
- package/dist/capability.js +5 -1
- package/dist/capability.test.js +1 -0
- package/dist/chat-contract.d.ts +3 -1
- package/dist/chat-contract.js +5 -2
- package/dist/container.d.ts +74 -0
- package/dist/container.js +81 -0
- package/dist/context.d.ts +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/input-normalizer.js +3 -1
- package/dist/perception-pipeline.d.ts +76 -0
- package/dist/perception-pipeline.js +255 -0
- package/dist/perception-pipeline.test.d.ts +1 -0
- package/dist/perception-pipeline.test.js +65 -0
- package/dist/runtime-facades.test.js +27 -27
- package/dist/runtime.d.ts +36 -113
- package/dist/runtime.js +58 -403
- package/dist/siduri-db.test.js +4 -3
- package/dist/sqlite-action-store.d.ts +1 -1
- package/dist/sqlite-action-store.js +37 -6
- package/dist/sqlite-action-store.test.js +9 -0
- package/package.json +1 -1
package/dist/runtime.js
CHANGED
|
@@ -1,431 +1,86 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SiduriRuntime = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
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");
|
|
4
|
+
const perception_pipeline_1 = require("./perception-pipeline");
|
|
5
|
+
const container_1 = require("./container");
|
|
15
6
|
/**
|
|
16
|
-
* SiduriRuntime coordinates companion
|
|
17
|
-
*
|
|
18
|
-
*
|
|
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.
|
|
19
10
|
*/
|
|
20
11
|
class SiduriRuntime {
|
|
21
12
|
id;
|
|
22
13
|
config;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
14
|
+
container;
|
|
15
|
+
pipeline;
|
|
16
|
+
constructor(id, config, containerOrOrgans = {}, pipeline) {
|
|
17
|
+
this.id = id;
|
|
18
|
+
this.config = config;
|
|
19
|
+
if (containerOrOrgans instanceof container_1.CompanionContainer) {
|
|
20
|
+
this.container = containerOrOrgans;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
this.container = new container_1.CompanionContainer(id, config, containerOrOrgans);
|
|
24
|
+
}
|
|
25
|
+
this.pipeline = pipeline || (0, perception_pipeline_1.createDefaultPerceptionPipeline)();
|
|
26
|
+
}
|
|
27
|
+
// Direct organ accessors through container
|
|
28
|
+
get organs() { return this.container.organs; }
|
|
29
|
+
get brain() { return this.container.brain; }
|
|
30
|
+
get memory() { return this.container.memory; }
|
|
31
|
+
get voice() { return this.container.voice; }
|
|
32
|
+
get knowledge() { return this.container.knowledge; }
|
|
33
|
+
get vision() { return this.container.vision; }
|
|
34
|
+
get behavior() { return this.container.behavior; }
|
|
35
|
+
get body() { return this.container.body; }
|
|
36
|
+
get hands() { return this.container.hands; }
|
|
37
|
+
get ear() { return this.container.ear; }
|
|
38
|
+
get observation() { return this.container.observation; }
|
|
39
|
+
set observation(org) { this.container.observation = org; }
|
|
40
|
+
get mouth() { return this.container.mouth; }
|
|
41
|
+
get self() { return this.container.self; }
|
|
42
|
+
get externalKnowledge() { return this.container.externalKnowledge; }
|
|
43
|
+
get gating() { return this.container.gating; }
|
|
44
|
+
get actionPolicy() { return this.container.actionPolicy; }
|
|
45
|
+
get dispatcher() { return this.container.dispatcher; }
|
|
46
|
+
get sessionHistory() { return this.container.sessionHistory; }
|
|
47
|
+
// Conversation history accessors
|
|
41
48
|
get conversationHistory() {
|
|
42
|
-
return this.sessionHistory.getHistory('default');
|
|
49
|
+
return this.container.sessionHistory.getHistory('default');
|
|
43
50
|
}
|
|
44
51
|
set conversationHistory(messages) {
|
|
45
|
-
this.sessionHistory.setHistory('default', messages);
|
|
46
|
-
}
|
|
47
|
-
constructor(id, config, organs = {}) {
|
|
48
|
-
this.id = id;
|
|
49
|
-
this.config = config;
|
|
50
|
-
this.brain = organs.brain;
|
|
51
|
-
this.memory = organs.memory;
|
|
52
|
-
this.voice = organs.voice;
|
|
53
|
-
this.knowledge = organs.knowledge;
|
|
54
|
-
this.vision = organs.vision;
|
|
55
|
-
this.behavior = organs.behavior;
|
|
56
|
-
this.body = organs.body;
|
|
57
|
-
this.hands = organs.hands;
|
|
58
|
-
this.ear = organs.ear;
|
|
59
|
-
this.observation = organs.observation;
|
|
60
|
-
this.mouth = organs.mouth;
|
|
61
|
-
this.self = organs.self;
|
|
62
|
-
this.externalKnowledge = organs.externalKnowledge;
|
|
63
|
-
this.gating = new index_1.ResponseGatingEngine();
|
|
64
|
-
let actionStore = organs.actionStore;
|
|
65
|
-
if (!actionStore) {
|
|
66
|
-
const storeOpt = config.actionStore;
|
|
67
|
-
const storePath = config.actionStorePath || (typeof storeOpt === 'object' ? storeOpt.dbPath : undefined);
|
|
68
|
-
if (storeOpt === 'sqlite' || (typeof storeOpt === 'object' && storeOpt.type === 'sqlite') || storePath) {
|
|
69
|
-
actionStore = new index_1.SqliteActionStore({ dbPath: storePath });
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
this.actionPolicy = organs.actionPolicy || new index_1.ActionPolicyEngine({
|
|
73
|
-
store: actionStore,
|
|
74
|
-
});
|
|
75
|
-
this.dispatcher = new index_1.ExperienceDispatcher();
|
|
76
|
-
if (this.voice && typeof this.voice.handleEvent === 'function') {
|
|
77
|
-
this.dispatcher.registerAdapter(this.voice);
|
|
78
|
-
}
|
|
79
|
-
if (this.body && typeof this.body.handleEvent === 'function') {
|
|
80
|
-
this.dispatcher.registerAdapter(this.body);
|
|
81
|
-
}
|
|
82
|
-
if (this.mouth && typeof this.mouth.handleEvent === 'function') {
|
|
83
|
-
this.dispatcher.registerAdapter(this.mouth);
|
|
84
|
-
}
|
|
52
|
+
this.container.sessionHistory.setHistory('default', messages);
|
|
85
53
|
}
|
|
86
54
|
async initialize() {
|
|
87
|
-
|
|
88
|
-
await this.memory.initialize(this.id);
|
|
89
|
-
}
|
|
90
|
-
if (this.hands && typeof this.hands.listTools === 'function') {
|
|
91
|
-
const tools = await this.hands.listTools();
|
|
92
|
-
for (const tool of tools) {
|
|
93
|
-
this.actionPolicy.registerToolDefinition(tool);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
55
|
+
return this.container.initialize();
|
|
96
56
|
}
|
|
97
|
-
// --- Session History Accessors ---
|
|
98
57
|
getSessionHistory(sessionKey) {
|
|
99
|
-
return this.sessionHistory.getHistory(sessionKey);
|
|
58
|
+
return this.container.sessionHistory.getHistory(sessionKey);
|
|
100
59
|
}
|
|
101
60
|
clearHistory(sessionKey) {
|
|
102
|
-
this.sessionHistory.clear(sessionKey);
|
|
103
|
-
}
|
|
104
|
-
// --- Observation & Vision Facades ---
|
|
105
|
-
async analyzeVision(imageUrl, prompt) {
|
|
106
|
-
if (!this.vision || typeof this.vision.analyze !== 'function') {
|
|
107
|
-
throw new Error('Vision organ is not configured on this runtime');
|
|
108
|
-
}
|
|
109
|
-
return this.vision.analyze(imageUrl, prompt);
|
|
110
|
-
}
|
|
111
|
-
async ingestObservation(frame, sourceName, providerId) {
|
|
112
|
-
if (!this.observation || typeof this.observation.ingest !== 'function') {
|
|
113
|
-
return { duplicate: false, reason: 'provider_failure' };
|
|
114
|
-
}
|
|
115
|
-
return this.observation.ingest(frame, sourceName, providerId);
|
|
116
|
-
}
|
|
117
|
-
getCurrentObservations(now) {
|
|
118
|
-
if (!this.observation || typeof this.observation.current !== 'function') {
|
|
119
|
-
return [];
|
|
120
|
-
}
|
|
121
|
-
return this.observation.current(now);
|
|
122
|
-
}
|
|
123
|
-
clearExpiredObservations(now) {
|
|
124
|
-
if (!this.observation || typeof this.observation.clearExpired !== 'function') {
|
|
125
|
-
return 0;
|
|
126
|
-
}
|
|
127
|
-
return this.observation.clearExpired(now);
|
|
61
|
+
this.container.sessionHistory.clear(sessionKey);
|
|
128
62
|
}
|
|
129
|
-
// --- Memory Facades ---
|
|
130
|
-
async getClaims(limit) {
|
|
131
|
-
if (!this.memory || typeof this.memory.getClaims !== 'function') {
|
|
132
|
-
return [];
|
|
133
|
-
}
|
|
134
|
-
return this.memory.getClaims(limit);
|
|
135
|
-
}
|
|
136
|
-
async getPendingClaims(limit) {
|
|
137
|
-
if (!this.memory || typeof this.memory.getPendingClaims !== 'function') {
|
|
138
|
-
return [];
|
|
139
|
-
}
|
|
140
|
-
return this.memory.getPendingClaims(limit);
|
|
141
|
-
}
|
|
142
|
-
async getDirectives() {
|
|
143
|
-
if (!this.memory || typeof this.memory.getDirectives !== 'function') {
|
|
144
|
-
return [];
|
|
145
|
-
}
|
|
146
|
-
return this.memory.getDirectives();
|
|
147
|
-
}
|
|
148
|
-
async approveClaim(id) {
|
|
149
|
-
if (!this.memory || typeof this.memory.approveClaim !== 'function') {
|
|
150
|
-
throw new Error('Memory organ not configured');
|
|
151
|
-
}
|
|
152
|
-
return this.memory.approveClaim(id);
|
|
153
|
-
}
|
|
154
|
-
async rejectClaim(id) {
|
|
155
|
-
if (!this.memory || typeof this.memory.rejectClaim !== 'function') {
|
|
156
|
-
throw new Error('Memory organ not configured');
|
|
157
|
-
}
|
|
158
|
-
return this.memory.rejectClaim(id);
|
|
159
|
-
}
|
|
160
|
-
async updateClaim(id, updates) {
|
|
161
|
-
if (!this.memory || typeof this.memory.updateClaim !== 'function') {
|
|
162
|
-
throw new Error('Memory organ updateClaim not supported');
|
|
163
|
-
}
|
|
164
|
-
return this.memory.updateClaim(id, updates);
|
|
165
|
-
}
|
|
166
|
-
async approveDirective(id, companionId) {
|
|
167
|
-
if (!this.memory || typeof this.memory.approveDirective !== 'function') {
|
|
168
|
-
throw new Error('Memory organ not configured');
|
|
169
|
-
}
|
|
170
|
-
return companionId !== undefined
|
|
171
|
-
? this.memory.approveDirective(id, companionId)
|
|
172
|
-
: this.memory.approveDirective(id);
|
|
173
|
-
}
|
|
174
|
-
async rejectDirective(id, companionId) {
|
|
175
|
-
if (!this.memory || typeof this.memory.rejectDirective !== 'function') {
|
|
176
|
-
throw new Error('Memory organ not configured');
|
|
177
|
-
}
|
|
178
|
-
return companionId !== undefined
|
|
179
|
-
? this.memory.rejectDirective(id, companionId)
|
|
180
|
-
: this.memory.rejectDirective(id);
|
|
181
|
-
}
|
|
182
|
-
async revokeDirective(id, companionId) {
|
|
183
|
-
if (!this.memory || typeof this.memory.revokeDirective !== 'function') {
|
|
184
|
-
throw new Error('Memory organ not configured');
|
|
185
|
-
}
|
|
186
|
-
return companionId !== undefined
|
|
187
|
-
? this.memory.revokeDirective(id, companionId)
|
|
188
|
-
: this.memory.revokeDirective(id);
|
|
189
|
-
}
|
|
190
|
-
async disableDirective(id, companionId) {
|
|
191
|
-
if (!this.memory || typeof this.memory.disableDirective !== 'function') {
|
|
192
|
-
throw new Error('Memory organ not configured');
|
|
193
|
-
}
|
|
194
|
-
return companionId !== undefined
|
|
195
|
-
? this.memory.disableDirective(id, companionId)
|
|
196
|
-
: this.memory.disableDirective(id);
|
|
197
|
-
}
|
|
198
|
-
async resetMemory() {
|
|
199
|
-
if (!this.memory || typeof this.memory.resetMemory !== 'function') {
|
|
200
|
-
throw new Error('Memory organ does not support reset');
|
|
201
|
-
}
|
|
202
|
-
return this.memory.resetMemory();
|
|
203
|
-
}
|
|
204
|
-
// --- Response Gating Facades ---
|
|
205
|
-
stageResponse(options) {
|
|
206
|
-
return this.gating.stageResponse(options);
|
|
207
|
-
}
|
|
208
|
-
evaluateGate(plan, evidenceRecords) {
|
|
209
|
-
return this.gating.evaluateGate(plan, evidenceRecords);
|
|
210
|
-
}
|
|
211
|
-
approveResponse(options) {
|
|
212
|
-
return this.gating.approveResponse(options);
|
|
213
|
-
}
|
|
214
|
-
rejectResponse(options) {
|
|
215
|
-
return this.gating.rejectResponse(options);
|
|
216
|
-
}
|
|
217
|
-
async approveAction(options) {
|
|
218
|
-
return this.actionPolicy.approveAction(options);
|
|
219
|
-
}
|
|
220
|
-
getStagedPlan(responseId) {
|
|
221
|
-
return this.gating.getStagedPlan(responseId);
|
|
222
|
-
}
|
|
223
|
-
findStagedPlanByCorrelation(companionId, correlationId) {
|
|
224
|
-
return this.gating.findStagedPlanByCorrelation(companionId, correlationId);
|
|
225
|
-
}
|
|
226
|
-
// --- Universal Perception & Cognition Cycle ---
|
|
227
63
|
/**
|
|
228
64
|
* Processes an incoming perception (sensory audio, text, platform event, or observation alert)
|
|
229
|
-
* through the
|
|
65
|
+
* through the decoupled PerceptionPipeline.
|
|
230
66
|
*/
|
|
231
67
|
async processPerception(perception) {
|
|
232
|
-
|
|
233
|
-
// If audio buffer is provided and Ear supports transcription, transcribe it
|
|
234
|
-
if (!rawText && perception.audioBuffer && this.ear && typeof this.ear.transcribeAudio === 'function') {
|
|
235
|
-
rawText = await this.ear.transcribeAudio(perception.audioBuffer);
|
|
236
|
-
}
|
|
237
|
-
const roleOrContext = perception.context || perception.roleOrContext || 'OWNER';
|
|
238
|
-
const history = perception.history || [];
|
|
239
|
-
// 1. Input validation, RequestContext synthesis, and Ear perception routing
|
|
240
|
-
const input = await (0, input_normalizer_1.normalizeUserInput)(rawText, roleOrContext, history, this.id, this.ear);
|
|
241
|
-
const sessionKey = input.requestContext.actor.sessionId ||
|
|
242
|
-
input.requestContext.conversation.correlationId ||
|
|
243
|
-
'default';
|
|
244
|
-
const currentMessage = { role: 'user', content: input.perceivedText };
|
|
245
|
-
const boundedSessionHistory = [...input.boundedHistory, currentMessage].slice(-20);
|
|
246
|
-
this.sessionHistory.setHistory(sessionKey, boundedSessionHistory);
|
|
247
|
-
this.sessionHistory.setHistory('default', boundedSessionHistory);
|
|
248
|
-
// 2. Intent classification (delegating to Ear if available, else heuristics)
|
|
249
|
-
const intent = await (0, intent_classifier_1.classifyInputIntentAsync)(input.perceivedText, input.requestContext, this.ear?.classifyIntent
|
|
250
|
-
? (t, c) => this.ear.classifyIntent(t, c)
|
|
251
|
-
: undefined);
|
|
252
|
-
// 3. Concurrent Knowledge & Memory retrieval with diagnostics & evidence handling
|
|
253
|
-
const contextRetrieval = await (0, context_retriever_1.retrieveRuntimeContext)({
|
|
68
|
+
const context = {
|
|
254
69
|
companionId: this.id,
|
|
255
|
-
perceivedText: input.perceivedText,
|
|
256
|
-
requestContext: input.requestContext,
|
|
257
|
-
role: input.role,
|
|
258
|
-
isContextObject: input.isContextObject,
|
|
259
|
-
shouldQueryKnowledge: intent.shouldQueryKnowledge,
|
|
260
|
-
knowledge: this.knowledge,
|
|
261
|
-
memory: this.memory,
|
|
262
|
-
self: this.self,
|
|
263
|
-
externalKnowledge: this.externalKnowledge,
|
|
264
|
-
});
|
|
265
|
-
// 4. Neutral system prompt and contextual prompt compilation
|
|
266
|
-
const prompts = await (0, prompt_compiler_1.compilePrompts)({
|
|
267
|
-
companionName: this.config.name,
|
|
268
|
-
companionId: this.id,
|
|
269
|
-
role: input.role,
|
|
270
|
-
requestContext: input.requestContext,
|
|
271
|
-
behavior: this.behavior,
|
|
272
|
-
activeDirectives: contextRetrieval.activeDirectives,
|
|
273
|
-
subsystemDiagnostics: contextRetrieval.subsystemDiagnostics,
|
|
274
|
-
knowledgeData: contextRetrieval.knowledgeData,
|
|
275
|
-
memoryData: contextRetrieval.memoryData,
|
|
276
|
-
lifeContext: contextRetrieval.lifeContext,
|
|
277
|
-
});
|
|
278
|
-
// 5. Cognition planning via BrainOrgan
|
|
279
|
-
const plan = await (0, cognition_planner_1.generateCognitionPlan)({
|
|
280
70
|
companionName: this.config.name,
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
requestContext: input.requestContext,
|
|
291
|
-
candidateSpeech: plan.speech,
|
|
292
|
-
candidateLanguage: plan.language || 'ja',
|
|
293
|
-
internalMonologue: plan.internalMonologue,
|
|
294
|
-
memoryProposals: plan.memoryProposals,
|
|
295
|
-
behaviorProposals: plan.behaviorProposals,
|
|
296
|
-
evidenceRecords: contextRetrieval.collectedEvidence,
|
|
297
|
-
citations: contextRetrieval.citations,
|
|
298
|
-
});
|
|
299
|
-
const gateEval = this.gating.evaluateGate(stagedPlan, contextRetrieval.collectedEvidence);
|
|
300
|
-
if (!gateEval.admissible) {
|
|
301
|
-
return (0, response_envelope_1.createGateRejectionEnvelope)(stagedPlan, gateEval);
|
|
302
|
-
}
|
|
303
|
-
this.sessionHistory.append(sessionKey, { role: 'assistant', content: plan.speech });
|
|
304
|
-
this.sessionHistory.append('default', { role: 'assistant', content: plan.speech });
|
|
305
|
-
// 7. Settle memory proposals and persist source events
|
|
306
|
-
const memorySettlement = await (0, memory_settler_1.settleMemoryProposals)({
|
|
307
|
-
companionId: this.id,
|
|
308
|
-
perceivedText: input.perceivedText,
|
|
309
|
-
role: input.role,
|
|
310
|
-
requestContext: input.requestContext,
|
|
311
|
-
memory: this.memory,
|
|
312
|
-
explicitTeaching: intent.explicitTeaching,
|
|
313
|
-
plan,
|
|
314
|
-
});
|
|
315
|
-
// 8. Authorize and execute action intents under Primary Security Invariant
|
|
316
|
-
const actionResults = await (0, action_executor_1.executeActionIntents)({
|
|
317
|
-
actionIntents: plan.actionIntents,
|
|
318
|
-
requestContext: input.requestContext,
|
|
319
|
-
actionPolicy: this.actionPolicy,
|
|
320
|
-
hands: this.hands,
|
|
321
|
-
});
|
|
322
|
-
// 9. Dispatch ExperienceEvents to registered adapters
|
|
323
|
-
const experienceEmission = await (0, experience_emitter_1.emitExperienceEvents)({
|
|
324
|
-
companionId: this.id,
|
|
325
|
-
requestContext: input.requestContext,
|
|
326
|
-
stagedPlan,
|
|
327
|
-
gateEval,
|
|
328
|
-
speech: plan.speech,
|
|
329
|
-
language: plan.language || 'ja',
|
|
330
|
-
dispatcher: this.dispatcher,
|
|
331
|
-
voice: this.voice,
|
|
332
|
-
body: this.body,
|
|
333
|
-
});
|
|
334
|
-
// 10. Deliver utterance via Mouth organ (UI / Output Channel Decoupling)
|
|
335
|
-
let mouthDelivery;
|
|
336
|
-
if (this.mouth && typeof this.mouth.speak === 'function') {
|
|
337
|
-
try {
|
|
338
|
-
const avatarEvent = experienceEmission.experienceEvents.find((e) => e.kind === 'avatar');
|
|
339
|
-
mouthDelivery = await this.mouth.speak({
|
|
340
|
-
utteranceId: stagedPlan.responseId,
|
|
341
|
-
companionId: this.id,
|
|
342
|
-
responseId: stagedPlan.responseId,
|
|
343
|
-
correlationId: stagedPlan.correlationId,
|
|
344
|
-
text: plan.speech,
|
|
345
|
-
language: plan.language || 'ja',
|
|
346
|
-
subtitleJa: plan.speech,
|
|
347
|
-
subtitleEn: plan.speech,
|
|
348
|
-
spokenJa: plan.speech,
|
|
349
|
-
expression: avatarEvent?.expression,
|
|
350
|
-
medium: perception.medium,
|
|
351
|
-
signal: perception.signal,
|
|
352
|
-
audioUrl: experienceEmission.speechId
|
|
353
|
-
? `/voice/stream?id=${experienceEmission.speechId}`
|
|
354
|
-
: undefined,
|
|
355
|
-
metadata: {
|
|
356
|
-
subsystemDiagnostics: contextRetrieval.subsystemDiagnostics,
|
|
357
|
-
internalMonologue: plan.internalMonologue,
|
|
358
|
-
},
|
|
359
|
-
citations: gateEval.filteredCitations,
|
|
360
|
-
evidenceIds: gateEval.filteredEvidenceIds,
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
catch (e) {
|
|
364
|
-
console.error('[SiduriRuntime] Mouth delivery failed:', e?.message || e);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
// 11. Assemble and return response envelope
|
|
368
|
-
return (0, response_envelope_1.assembleResponseEnvelope)({
|
|
369
|
-
stagedPlan,
|
|
370
|
-
speech: plan.speech,
|
|
371
|
-
language: plan.language,
|
|
372
|
-
speechId: experienceEmission.speechId,
|
|
373
|
-
createdMemoryProposals: memorySettlement.createdMemoryProposals,
|
|
374
|
-
memoryProposalReceipts: memorySettlement.memoryProposalReceipts,
|
|
375
|
-
actionResults,
|
|
376
|
-
filteredEvidenceIds: gateEval.filteredEvidenceIds,
|
|
377
|
-
filteredCitations: gateEval.filteredCitations,
|
|
378
|
-
subsystemDiagnostics: contextRetrieval.subsystemDiagnostics,
|
|
379
|
-
experienceEvents: experienceEmission.experienceEvents,
|
|
380
|
-
mouthDelivery,
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
// --- Mouth Facades ---
|
|
384
|
-
async speakMouth(utterance) {
|
|
385
|
-
if (!this.mouth || typeof this.mouth.speak !== 'function')
|
|
386
|
-
return undefined;
|
|
387
|
-
return this.mouth.speak(utterance);
|
|
388
|
-
}
|
|
389
|
-
formatMouth(utterance, medium) {
|
|
390
|
-
if (!this.mouth || typeof this.mouth.format !== 'function')
|
|
391
|
-
return undefined;
|
|
392
|
-
return this.mouth.format(utterance, medium);
|
|
393
|
-
}
|
|
394
|
-
registerMouthChannel(channel) {
|
|
395
|
-
if (this.mouth && typeof this.mouth.registerChannel === 'function') {
|
|
396
|
-
this.mouth.registerChannel(channel);
|
|
397
|
-
}
|
|
71
|
+
perception,
|
|
72
|
+
organs: this.container.organs,
|
|
73
|
+
gating: this.container.gating,
|
|
74
|
+
actionPolicy: this.container.actionPolicy,
|
|
75
|
+
dispatcher: this.container.dispatcher,
|
|
76
|
+
sessionHistory: this.container.sessionHistory,
|
|
77
|
+
rawText: perception.text || '',
|
|
78
|
+
};
|
|
79
|
+
return this.pipeline.execute(context);
|
|
398
80
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
async broadcastMouth(utterance) {
|
|
405
|
-
if (!this.mouth || typeof this.mouth.broadcast !== 'function')
|
|
406
|
-
return [];
|
|
407
|
-
return this.mouth.broadcast(utterance);
|
|
408
|
-
}
|
|
409
|
-
interruptMouth(reason) {
|
|
410
|
-
if (this.mouth && typeof this.mouth.interrupt === 'function') {
|
|
411
|
-
this.mouth.interrupt(reason);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
streamMouth(utterance) {
|
|
415
|
-
if (!this.mouth || typeof this.mouth.stream !== 'function') {
|
|
416
|
-
return (async function* () {
|
|
417
|
-
yield {
|
|
418
|
-
utteranceId: utterance.utteranceId,
|
|
419
|
-
index: 0,
|
|
420
|
-
deltaText: utterance.text,
|
|
421
|
-
isComplete: true,
|
|
422
|
-
medium: 'web',
|
|
423
|
-
};
|
|
424
|
-
})();
|
|
425
|
-
}
|
|
426
|
-
return this.mouth.stream(utterance);
|
|
427
|
-
}
|
|
428
|
-
// --- Backward-Compatible Chat Adapter ---
|
|
81
|
+
/**
|
|
82
|
+
* Primary entrypoint for text chat messages.
|
|
83
|
+
*/
|
|
429
84
|
async handleUserMessage(message, roleOrContext = 'OWNER', history = [], medium, signal) {
|
|
430
85
|
return this.processPerception({
|
|
431
86
|
source: 'text_chat',
|
package/dist/siduri-db.test.js
CHANGED
|
@@ -72,14 +72,15 @@ describe('SiduriDatabase', () => {
|
|
|
72
72
|
memDb.close();
|
|
73
73
|
}).not.toThrow();
|
|
74
74
|
});
|
|
75
|
-
it('initializes schema and WAL mode within the startup latency budget (<
|
|
75
|
+
it('initializes schema and WAL mode within the startup latency budget (<1000ms in CI, typical <20ms locally)', () => {
|
|
76
76
|
const start = performance.now();
|
|
77
77
|
const benchDb = new siduri_db_1.SiduriDatabase({ dbPath });
|
|
78
78
|
const duration = performance.now() - start;
|
|
79
79
|
benchDb.close();
|
|
80
80
|
// In bare-metal local development, SQLite cold init is ~2-5ms.
|
|
81
|
-
// Under virtualized CI runners with concurrent Turbo tasks, allow
|
|
82
|
-
|
|
81
|
+
// Under virtualized CI runners with concurrent Turbo tasks and shared I/O, allow up to 1000ms.
|
|
82
|
+
const budgetMs = process.env.CI ? 1000 : 250;
|
|
83
|
+
expect(duration).toBeLessThan(budgetMs);
|
|
83
84
|
});
|
|
84
85
|
it('stores and retrieves companion identity', () => {
|
|
85
86
|
db = new siduri_db_1.SiduriDatabase({ dbPath });
|
|
@@ -12,7 +12,7 @@ export declare class SqliteActionStore implements ActionStore {
|
|
|
12
12
|
reserveExecution(record: PersistentExecutionRecord): Promise<boolean>;
|
|
13
13
|
updateExecution(record: PersistentExecutionRecord): Promise<void>;
|
|
14
14
|
getExecution(executionId: string): Promise<PersistentExecutionRecord | undefined>;
|
|
15
|
-
saveApproval(executionId: string, approverActorId: string, reason?: string, approverRole?: string): Promise<void>;
|
|
15
|
+
saveApproval(executionId: string, approverActorId: string, reason?: string, approverRole?: string, toolName?: string, parametersHash?: string, companionId?: string, actorId?: string): Promise<void>;
|
|
16
16
|
isActionApproved(executionId: string): Promise<boolean>;
|
|
17
17
|
getApproval(executionId: string): Promise<ActionApprovalRecord | undefined>;
|
|
18
18
|
appendAudit(event: ActionAuditEvent): Promise<void>;
|
|
@@ -37,7 +37,11 @@ class SqliteActionStore {
|
|
|
37
37
|
approver_actor_id TEXT NOT NULL,
|
|
38
38
|
reason TEXT,
|
|
39
39
|
approver_role TEXT,
|
|
40
|
-
approved_at TEXT NOT NULL
|
|
40
|
+
approved_at TEXT NOT NULL,
|
|
41
|
+
tool_name TEXT,
|
|
42
|
+
parameters_hash TEXT,
|
|
43
|
+
companion_id TEXT,
|
|
44
|
+
actor_id TEXT
|
|
41
45
|
);
|
|
42
46
|
|
|
43
47
|
CREATE TABLE IF NOT EXISTS action_audit_log (
|
|
@@ -69,6 +73,22 @@ class SqliteActionStore {
|
|
|
69
73
|
catch {
|
|
70
74
|
// Column already exists or table freshly created
|
|
71
75
|
}
|
|
76
|
+
try {
|
|
77
|
+
this.db.exec('ALTER TABLE action_approvals ADD COLUMN tool_name TEXT;');
|
|
78
|
+
}
|
|
79
|
+
catch { }
|
|
80
|
+
try {
|
|
81
|
+
this.db.exec('ALTER TABLE action_approvals ADD COLUMN parameters_hash TEXT;');
|
|
82
|
+
}
|
|
83
|
+
catch { }
|
|
84
|
+
try {
|
|
85
|
+
this.db.exec('ALTER TABLE action_approvals ADD COLUMN companion_id TEXT;');
|
|
86
|
+
}
|
|
87
|
+
catch { }
|
|
88
|
+
try {
|
|
89
|
+
this.db.exec('ALTER TABLE action_approvals ADD COLUMN actor_id TEXT;');
|
|
90
|
+
}
|
|
91
|
+
catch { }
|
|
72
92
|
}
|
|
73
93
|
initLastAuditHash() {
|
|
74
94
|
const row = this.db.prepare('SELECT event_hash FROM action_audit_log ORDER BY id DESC LIMIT 1').get();
|
|
@@ -134,17 +154,24 @@ class SqliteActionStore {
|
|
|
134
154
|
updatedAt: row.updated_at,
|
|
135
155
|
};
|
|
136
156
|
}
|
|
137
|
-
async saveApproval(executionId, approverActorId, reason, approverRole) {
|
|
157
|
+
async saveApproval(executionId, approverActorId, reason, approverRole, toolName, parametersHash, companionId, actorId) {
|
|
138
158
|
const stmt = this.db.prepare(`
|
|
139
|
-
INSERT INTO action_approvals (
|
|
140
|
-
|
|
159
|
+
INSERT INTO action_approvals (
|
|
160
|
+
execution_id, approver_actor_id, reason, approver_role, approved_at,
|
|
161
|
+
tool_name, parameters_hash, companion_id, actor_id
|
|
162
|
+
)
|
|
163
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
141
164
|
ON CONFLICT(execution_id) DO UPDATE SET
|
|
142
165
|
approver_actor_id = excluded.approver_actor_id,
|
|
143
166
|
reason = excluded.reason,
|
|
144
167
|
approver_role = excluded.approver_role,
|
|
145
|
-
approved_at = excluded.approved_at
|
|
168
|
+
approved_at = excluded.approved_at,
|
|
169
|
+
tool_name = excluded.tool_name,
|
|
170
|
+
parameters_hash = excluded.parameters_hash,
|
|
171
|
+
companion_id = excluded.companion_id,
|
|
172
|
+
actor_id = excluded.actor_id
|
|
146
173
|
`);
|
|
147
|
-
stmt.run(executionId, approverActorId, reason ?? null, approverRole ?? null, new Date().toISOString());
|
|
174
|
+
stmt.run(executionId, approverActorId, reason ?? null, approverRole ?? null, new Date().toISOString(), toolName ?? null, parametersHash ?? null, companionId ?? null, actorId ?? null);
|
|
148
175
|
}
|
|
149
176
|
async isActionApproved(executionId) {
|
|
150
177
|
const stmt = this.db.prepare('SELECT 1 FROM action_approvals WHERE execution_id = ?');
|
|
@@ -163,6 +190,10 @@ class SqliteActionStore {
|
|
|
163
190
|
reason: row.reason ?? undefined,
|
|
164
191
|
approverRole: row.approver_role ?? undefined,
|
|
165
192
|
approvedAt: row.approved_at,
|
|
193
|
+
toolName: row.tool_name ?? undefined,
|
|
194
|
+
parametersHash: row.parameters_hash ?? undefined,
|
|
195
|
+
companionId: row.companion_id ?? undefined,
|
|
196
|
+
actorId: row.actor_id ?? undefined,
|
|
166
197
|
};
|
|
167
198
|
}
|
|
168
199
|
async appendAudit(event) {
|
|
@@ -229,6 +229,7 @@ describe('SqliteActionStore Implementation & Durability', () => {
|
|
|
229
229
|
await engine1.approveAction({
|
|
230
230
|
executionId: 'exec-danger-1',
|
|
231
231
|
approverActorId: 'local-owner',
|
|
232
|
+
approverRole: 'owner',
|
|
232
233
|
reason: 'Owner confirmed cleanup',
|
|
233
234
|
});
|
|
234
235
|
store1.close();
|
|
@@ -247,6 +248,14 @@ describe('SqliteActionStore Implementation & Durability', () => {
|
|
|
247
248
|
expect(eval2.decision.decisionCode).toBe('ALLOWED_POLICY');
|
|
248
249
|
expect(eval2.capability).toBeDefined();
|
|
249
250
|
expect((0, capability_1.verifyCapabilitySignature)(eval2.capability, secretKey)).toBe(true);
|
|
251
|
+
// 5. Tampered action after restart -> rejected due to approval parameter mismatch
|
|
252
|
+
const tamperedAction = {
|
|
253
|
+
...action,
|
|
254
|
+
parameters: { force: true, dropDatabase: true },
|
|
255
|
+
};
|
|
256
|
+
const evalTampered = await engine2.evaluateAction(tamperedAction);
|
|
257
|
+
expect(evalTampered.decision.allowed).toBe(false);
|
|
258
|
+
expect(evalTampered.decision.decisionCode).toBe('REJECTED_APPROVAL_MISMATCH');
|
|
250
259
|
store2.close();
|
|
251
260
|
});
|
|
252
261
|
});
|
package/package.json
CHANGED