@sidurijs/core 1.0.0
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 +54 -0
- package/dist/action-policy.d.ts +79 -0
- package/dist/action-policy.js +439 -0
- package/dist/action-policy.test.d.ts +1 -0
- package/dist/action-policy.test.js +363 -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 +491 -0
- package/dist/architecture-boundary.test.d.ts +1 -0
- package/dist/architecture-boundary.test.js +108 -0
- package/dist/capability.d.ts +78 -0
- package/dist/capability.js +168 -0
- package/dist/capability.test.d.ts +1 -0
- package/dist/capability.test.js +311 -0
- package/dist/chat-contract.d.ts +94 -0
- package/dist/chat-contract.js +104 -0
- package/dist/cognition-planner.d.ts +15 -0
- package/dist/cognition-planner.js +23 -0
- package/dist/container.d.ts +74 -0
- package/dist/container.js +81 -0
- package/dist/context-retriever.d.ts +33 -0
- package/dist/context-retriever.js +260 -0
- package/dist/context.d.ts +46 -0
- package/dist/context.js +85 -0
- package/dist/context.test.d.ts +1 -0
- package/dist/context.test.js +76 -0
- package/dist/conversational-teach.test.d.ts +1 -0
- package/dist/conversational-teach.test.js +941 -0
- package/dist/database.d.ts +1 -0
- package/dist/database.js +17 -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 +58 -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 +27 -0
- package/dist/evidence.test.d.ts +1 -0
- package/dist/evidence.test.js +75 -0
- package/dist/experience-emitter.d.ts +21 -0
- package/dist/experience-emitter.js +47 -0
- package/dist/experience.d.ts +55 -0
- package/dist/experience.js +74 -0
- package/dist/experience.test.d.ts +1 -0
- package/dist/experience.test.js +55 -0
- package/dist/gating.d.ts +45 -0
- package/dist/gating.js +185 -0
- package/dist/gating.test.d.ts +1 -0
- package/dist/gating.test.js +186 -0
- package/dist/index.d.ts +367 -0
- package/dist/index.js +56 -0
- package/dist/input-normalizer.d.ts +14 -0
- package/dist/input-normalizer.js +76 -0
- package/dist/input-normalizer.test.d.ts +1 -0
- package/dist/input-normalizer.test.js +48 -0
- package/dist/intent-classifier.d.ts +37 -0
- package/dist/intent-classifier.js +150 -0
- package/dist/intent-classifier.test.d.ts +1 -0
- package/dist/intent-classifier.test.js +118 -0
- package/dist/memory-settler.d.ts +49 -0
- package/dist/memory-settler.js +161 -0
- package/dist/mouth-types.d.ts +90 -0
- package/dist/mouth-types.js +2 -0
- package/dist/perception-cycle.test.d.ts +1 -0
- package/dist/perception-cycle.test.js +301 -0
- package/dist/perception-pipeline.d.ts +77 -0
- package/dist/perception-pipeline.js +304 -0
- package/dist/perception-pipeline.test.d.ts +1 -0
- package/dist/perception-pipeline.test.js +65 -0
- package/dist/prompt-compiler.d.ts +26 -0
- package/dist/prompt-compiler.js +81 -0
- package/dist/prompt-compiler.test.d.ts +1 -0
- package/dist/prompt-compiler.test.js +90 -0
- package/dist/proposals.d.ts +33 -0
- package/dist/proposals.js +2 -0
- package/dist/response-envelope.d.ts +30 -0
- package/dist/response-envelope.js +90 -0
- package/dist/runtime-facades.test.d.ts +1 -0
- package/dist/runtime-facades.test.js +95 -0
- package/dist/runtime.d.ts +116 -0
- package/dist/runtime.js +778 -0
- package/dist/schema-validator.d.ts +9 -0
- package/dist/schema-validator.js +107 -0
- package/dist/schema-validator.test.d.ts +1 -0
- package/dist/schema-validator.test.js +124 -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/siduri-db.d.ts +235 -0
- package/dist/siduri-db.js +1405 -0
- package/dist/siduri-db.test.d.ts +1 -0
- package/dist/siduri-db.test.js +1021 -0
- package/dist/sqlite-action-store.d.ts +21 -0
- package/dist/sqlite-action-store.js +281 -0
- package/dist/sqlite-action-store.test.d.ts +1 -0
- package/dist/sqlite-action-store.test.js +261 -0
- package/dist/teaching.d.ts +14 -0
- package/dist/teaching.js +14 -0
- package/package.json +43 -0
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SiduriRuntime = void 0;
|
|
4
|
+
exports.promoteApprovedClaimToSelf = promoteApprovedClaimToSelf;
|
|
5
|
+
exports.isSelfAffectingClaim = isSelfAffectingClaim;
|
|
6
|
+
exports.isKnowledgeAffectingClaim = isKnowledgeAffectingClaim;
|
|
7
|
+
exports.promoteApprovedClaimToKnowledge = promoteApprovedClaimToKnowledge;
|
|
8
|
+
const perception_pipeline_1 = require("./perception-pipeline");
|
|
9
|
+
const container_1 = require("./container");
|
|
10
|
+
/**
|
|
11
|
+
* SiduriRuntime coordinates companion perception and cognition execution.
|
|
12
|
+
* Lifecycle management and organ storage are handled by CompanionContainer.
|
|
13
|
+
* The 12-stage perception cycle is executed via PerceptionPipeline.
|
|
14
|
+
*/
|
|
15
|
+
class SiduriRuntime {
|
|
16
|
+
id;
|
|
17
|
+
config;
|
|
18
|
+
container;
|
|
19
|
+
pipeline;
|
|
20
|
+
constructor(id, config, containerOrOrgans = {}, pipeline) {
|
|
21
|
+
this.id = id;
|
|
22
|
+
this.config = config;
|
|
23
|
+
if (containerOrOrgans instanceof container_1.CompanionContainer) {
|
|
24
|
+
this.container = containerOrOrgans;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
this.container = new container_1.CompanionContainer(id, config, containerOrOrgans);
|
|
28
|
+
}
|
|
29
|
+
this.pipeline = pipeline || (0, perception_pipeline_1.createDefaultPerceptionPipeline)();
|
|
30
|
+
}
|
|
31
|
+
// Direct organ accessors through container
|
|
32
|
+
get organs() { return this.container.organs; }
|
|
33
|
+
get brain() { return this.container.brain; }
|
|
34
|
+
get memory() { return this.container.memory; }
|
|
35
|
+
get voice() { return this.container.voice; }
|
|
36
|
+
get knowledge() { return this.container.knowledge; }
|
|
37
|
+
get vision() { return this.container.vision; }
|
|
38
|
+
get behavior() { return this.container.behavior; }
|
|
39
|
+
get body() { return this.container.body; }
|
|
40
|
+
get hands() { return this.container.hands; }
|
|
41
|
+
get ear() { return this.container.ear; }
|
|
42
|
+
get observation() { return this.container.observation; }
|
|
43
|
+
set observation(org) { this.container.observation = org; }
|
|
44
|
+
get mouth() { return this.container.mouth; }
|
|
45
|
+
get self() { return this.container.self; }
|
|
46
|
+
get externalKnowledge() { return this.container.externalKnowledge; }
|
|
47
|
+
get gating() { return this.container.gating; }
|
|
48
|
+
get actionPolicy() { return this.container.actionPolicy; }
|
|
49
|
+
get dispatcher() { return this.container.dispatcher; }
|
|
50
|
+
get sessionHistory() { return this.container.sessionHistory; }
|
|
51
|
+
// Conversation history accessors
|
|
52
|
+
get conversationHistory() {
|
|
53
|
+
return this.container.sessionHistory.getHistory('default');
|
|
54
|
+
}
|
|
55
|
+
set conversationHistory(messages) {
|
|
56
|
+
this.container.sessionHistory.setHistory('default', messages);
|
|
57
|
+
}
|
|
58
|
+
async initialize() {
|
|
59
|
+
await this.container.initialize();
|
|
60
|
+
if (this.memory && this.self && typeof this.memory.approveClaim === 'function') {
|
|
61
|
+
const originalApproveClaim = this.memory.approveClaim.bind(this.memory);
|
|
62
|
+
this.memory.approveClaim = async (id) => {
|
|
63
|
+
await originalApproveClaim(id);
|
|
64
|
+
const targetCompId = this.id;
|
|
65
|
+
const claims = typeof this.memory.getAllClaims === 'function'
|
|
66
|
+
? await this.memory.getAllClaims(500)
|
|
67
|
+
: await this.memory.getClaims(500);
|
|
68
|
+
let found = claims.find((c) => c.id === id);
|
|
69
|
+
if (!found && typeof this.memory.getPendingClaims === 'function') {
|
|
70
|
+
const pending = await this.memory.getPendingClaims(500);
|
|
71
|
+
found = pending.find((c) => c.id === id);
|
|
72
|
+
}
|
|
73
|
+
if (found && this.self) {
|
|
74
|
+
await promoteApprovedClaimToSelf(found, this.self, targetCompId);
|
|
75
|
+
}
|
|
76
|
+
if (found && this.container.knowledge) {
|
|
77
|
+
await promoteApprovedClaimToKnowledge(found, this.container.knowledge, targetCompId);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// Sync any pre-existing approved claims to Knowledge / Life DB
|
|
82
|
+
if (this.memory && this.container.knowledge) {
|
|
83
|
+
try {
|
|
84
|
+
const claims = typeof this.memory.getAllClaims === 'function'
|
|
85
|
+
? await this.memory.getAllClaims(500)
|
|
86
|
+
: await this.memory.getClaims(500);
|
|
87
|
+
const approvedClaims = (claims || []).filter((c) => (c.status || '').toLowerCase() === 'approved');
|
|
88
|
+
for (const c of approvedClaims) {
|
|
89
|
+
await promoteApprovedClaimToKnowledge(c, this.container.knowledge, this.id);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// non-blocking
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
get db() {
|
|
98
|
+
return this.container.db || this.memory?.db || this.self?.db;
|
|
99
|
+
}
|
|
100
|
+
log(level, subsystem, message, metadata) {
|
|
101
|
+
if (this.db && typeof this.db.insertLog === 'function') {
|
|
102
|
+
return this.db.insertLog({
|
|
103
|
+
companionId: this.id,
|
|
104
|
+
level,
|
|
105
|
+
subsystem,
|
|
106
|
+
message,
|
|
107
|
+
metadata,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
queryLogs(options) {
|
|
112
|
+
if (this.db && typeof this.db.queryLogs === 'function') {
|
|
113
|
+
return this.db.queryLogs({
|
|
114
|
+
companionId: options?.companionId || this.id,
|
|
115
|
+
...options,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
clearLogs(companionId) {
|
|
121
|
+
if (this.db && typeof this.db.clearLogs === 'function') {
|
|
122
|
+
this.db.clearLogs(companionId || this.id);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
getSessionHistory(sessionKey) {
|
|
126
|
+
return this.container.sessionHistory.getHistory(sessionKey);
|
|
127
|
+
}
|
|
128
|
+
clearHistory(sessionKey) {
|
|
129
|
+
this.container.sessionHistory.clear(sessionKey);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Approves a memory proposal or behavior proposal and canonically promotes
|
|
133
|
+
* Self-affecting mutations to SelfRepository.
|
|
134
|
+
*/
|
|
135
|
+
async approveProposal(proposalId, options) {
|
|
136
|
+
const companionId = options?.companionId || this.id;
|
|
137
|
+
// 1. Approve claim in memory if present
|
|
138
|
+
if (this.memory && typeof this.memory.approveClaim === 'function') {
|
|
139
|
+
await this.memory.approveClaim(proposalId);
|
|
140
|
+
}
|
|
141
|
+
// 2. Fetch claim
|
|
142
|
+
let claim;
|
|
143
|
+
if (this.memory) {
|
|
144
|
+
const claims = typeof this.memory.getAllClaims === 'function'
|
|
145
|
+
? await this.memory.getAllClaims(500)
|
|
146
|
+
: await this.memory.getClaims(500);
|
|
147
|
+
claim = claims.find((c) => c.id === proposalId);
|
|
148
|
+
if (!claim && typeof this.memory.getPendingClaims === 'function') {
|
|
149
|
+
const pending = await this.memory.getPendingClaims(500);
|
|
150
|
+
claim = pending.find((c) => c.id === proposalId);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// 3. Promote to Knowledge / Life DB if claim is Knowledge-affecting
|
|
154
|
+
if (claim && this.container.knowledge) {
|
|
155
|
+
const promotedToKnowledge = await promoteApprovedClaimToKnowledge(claim, this.container.knowledge, companionId);
|
|
156
|
+
if (promotedToKnowledge) {
|
|
157
|
+
this.log('info', 'truth_gate', `Approved and promoted claim '${proposalId}' to Knowledge / Life DB`, {
|
|
158
|
+
proposalId,
|
|
159
|
+
companionId,
|
|
160
|
+
target: 'knowledge',
|
|
161
|
+
subject: claim.subject,
|
|
162
|
+
predicate: claim.predicate,
|
|
163
|
+
});
|
|
164
|
+
return { success: true, target: 'knowledge' };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// 4. Promote to SelfRepository if claim is Self-affecting
|
|
168
|
+
if (claim && this.self) {
|
|
169
|
+
await promoteApprovedClaimToSelf(claim, this.self, companionId);
|
|
170
|
+
const identity = typeof this.self.getIdentity === 'function' ? await this.self.getIdentity(companionId) : null;
|
|
171
|
+
this.log('info', 'truth_gate', `Approved and promoted claim '${proposalId}' to Self`, {
|
|
172
|
+
proposalId,
|
|
173
|
+
companionId,
|
|
174
|
+
target: 'self',
|
|
175
|
+
subject: claim.subject,
|
|
176
|
+
predicate: claim.predicate,
|
|
177
|
+
});
|
|
178
|
+
return { success: true, target: 'self', name: identity?.name };
|
|
179
|
+
}
|
|
180
|
+
// 5. Also check if this proposal ID is a behavioral directive in Self
|
|
181
|
+
if (this.self && typeof this.self.approveDirective === 'function') {
|
|
182
|
+
try {
|
|
183
|
+
await this.self.approveDirective(proposalId, companionId);
|
|
184
|
+
const identity = typeof this.self.getIdentity === 'function' ? await this.self.getIdentity(companionId) : null;
|
|
185
|
+
this.log('info', 'truth_gate', `Approved behavioral directive proposal '${proposalId}'`, {
|
|
186
|
+
proposalId,
|
|
187
|
+
companionId,
|
|
188
|
+
target: 'self',
|
|
189
|
+
});
|
|
190
|
+
return { success: true, target: 'self', name: identity?.name };
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// Not a pending directive or already active
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const identity = this.self && typeof this.self.getIdentity === 'function' ? await this.self.getIdentity(companionId) : null;
|
|
197
|
+
this.log('info', 'truth_gate', `Approved memory proposal '${proposalId}'`, {
|
|
198
|
+
proposalId,
|
|
199
|
+
companionId,
|
|
200
|
+
target: 'memory',
|
|
201
|
+
});
|
|
202
|
+
return { success: true, target: 'memory', name: identity?.name };
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Rejects a memory or behavior proposal.
|
|
206
|
+
*/
|
|
207
|
+
async rejectProposal(proposalId, options) {
|
|
208
|
+
const companionId = options?.companionId || this.id;
|
|
209
|
+
if (this.memory && typeof this.memory.rejectClaim === 'function') {
|
|
210
|
+
await this.memory.rejectClaim(proposalId);
|
|
211
|
+
}
|
|
212
|
+
if (this.self && typeof this.self.rejectDirective === 'function') {
|
|
213
|
+
try {
|
|
214
|
+
await this.self.rejectDirective(proposalId, companionId);
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// ignore
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
this.log('info', 'truth_gate', `Rejected proposal '${proposalId}'`, {
|
|
221
|
+
proposalId,
|
|
222
|
+
companionId,
|
|
223
|
+
});
|
|
224
|
+
return { success: true };
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Approves a behavioral directive in Self and Memory.
|
|
228
|
+
*/
|
|
229
|
+
async approveDirective(directiveId, options) {
|
|
230
|
+
const companionId = options?.companionId || this.id;
|
|
231
|
+
if (this.self && typeof this.self.approveDirective === 'function') {
|
|
232
|
+
await this.self.approveDirective(directiveId, companionId);
|
|
233
|
+
}
|
|
234
|
+
if (this.memory && typeof this.memory.approveDirective === 'function') {
|
|
235
|
+
await this.memory.approveDirective(directiveId, companionId);
|
|
236
|
+
}
|
|
237
|
+
// If directive pertains to companion name, ensure self identity reflects it
|
|
238
|
+
if (this.self && typeof this.self.getActiveDirectives === 'function') {
|
|
239
|
+
try {
|
|
240
|
+
const activeDirs = await this.self.getActiveDirectives(companionId);
|
|
241
|
+
const dir = activeDirs.find((d) => d.id === directiveId);
|
|
242
|
+
if (dir?.directive) {
|
|
243
|
+
const compNameMatch = dir.directive.match(/^(?:address\s+companion\s+as|your\s+name\s+is|call\s+yourself|acknowledge\s+name\s+as|companion\s+name\s+is)\s+["“']?([^"”'.]+)["”']?/i);
|
|
244
|
+
if (compNameMatch) {
|
|
245
|
+
const newName = compNameMatch[1].trim();
|
|
246
|
+
if (newName) {
|
|
247
|
+
const currentIdentity = (await this.self.getIdentity(companionId)) || {
|
|
248
|
+
companionId,
|
|
249
|
+
name: '',
|
|
250
|
+
version: '1.0.0',
|
|
251
|
+
updatedAt: new Date().toISOString(),
|
|
252
|
+
};
|
|
253
|
+
currentIdentity.name = newName;
|
|
254
|
+
currentIdentity.updatedAt = new Date().toISOString();
|
|
255
|
+
await this.self.setIdentity(currentIdentity);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
// non-blocking
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const identity = this.self && typeof this.self.getIdentity === 'function' ? await this.self.getIdentity(companionId) : null;
|
|
265
|
+
this.log('info', 'truth_gate', `Approved behavioral directive '${directiveId}'`, {
|
|
266
|
+
directiveId,
|
|
267
|
+
companionId,
|
|
268
|
+
});
|
|
269
|
+
return { success: true, name: identity?.name };
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Rejects a behavioral directive in Self and Memory.
|
|
273
|
+
*/
|
|
274
|
+
async rejectDirective(directiveId, options) {
|
|
275
|
+
const companionId = options?.companionId || this.id;
|
|
276
|
+
if (this.self && typeof this.self.rejectDirective === 'function') {
|
|
277
|
+
await this.self.rejectDirective(directiveId, companionId);
|
|
278
|
+
}
|
|
279
|
+
if (this.memory && typeof this.memory.rejectDirective === 'function') {
|
|
280
|
+
await this.memory.rejectDirective(directiveId, companionId);
|
|
281
|
+
}
|
|
282
|
+
this.log('info', 'truth_gate', `Rejected behavioral directive '${directiveId}'`, {
|
|
283
|
+
directiveId,
|
|
284
|
+
companionId,
|
|
285
|
+
});
|
|
286
|
+
return { success: true };
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Revokes a behavioral directive in Self and Memory.
|
|
290
|
+
*/
|
|
291
|
+
async revokeDirective(directiveId, options) {
|
|
292
|
+
const companionId = options?.companionId || this.id;
|
|
293
|
+
if (this.self && typeof this.self.revokeDirective === 'function') {
|
|
294
|
+
await this.self.revokeDirective(directiveId, companionId);
|
|
295
|
+
}
|
|
296
|
+
if (this.memory && typeof this.memory.revokeDirective === 'function') {
|
|
297
|
+
await this.memory.revokeDirective(directiveId, companionId);
|
|
298
|
+
}
|
|
299
|
+
return { success: true };
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Processes an incoming perception (sensory audio, text, platform event, or observation alert)
|
|
303
|
+
* through the decoupled PerceptionPipeline.
|
|
304
|
+
*/
|
|
305
|
+
async processPerception(perception) {
|
|
306
|
+
const context = {
|
|
307
|
+
companionId: this.id,
|
|
308
|
+
companionName: this.config.name,
|
|
309
|
+
perception,
|
|
310
|
+
organs: this.container.organs,
|
|
311
|
+
gating: this.container.gating,
|
|
312
|
+
actionPolicy: this.container.actionPolicy,
|
|
313
|
+
dispatcher: this.container.dispatcher,
|
|
314
|
+
sessionHistory: this.container.sessionHistory,
|
|
315
|
+
rawText: perception.text || '',
|
|
316
|
+
};
|
|
317
|
+
return this.pipeline.execute(context);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Primary entrypoint for text chat messages.
|
|
321
|
+
*/
|
|
322
|
+
async handleUserMessage(message, roleOrContext = 'OWNER', history = [], medium, signal, subtitleLanguage) {
|
|
323
|
+
return this.processPerception({
|
|
324
|
+
source: 'text_chat',
|
|
325
|
+
text: message,
|
|
326
|
+
roleOrContext,
|
|
327
|
+
history,
|
|
328
|
+
medium,
|
|
329
|
+
signal,
|
|
330
|
+
subtitleLanguage,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
exports.SiduriRuntime = SiduriRuntime;
|
|
335
|
+
/**
|
|
336
|
+
* Canonically promotes an approved Claim into SelfRepository state.
|
|
337
|
+
*/
|
|
338
|
+
async function promoteApprovedClaimToSelf(claim, self, companionId) {
|
|
339
|
+
const targetCompanionId = companionId || claim.companionId || 'default';
|
|
340
|
+
const subject = (claim.subject || '').toLowerCase();
|
|
341
|
+
const predicate = (claim.predicate || '').toLowerCase();
|
|
342
|
+
const value = claim.value || '';
|
|
343
|
+
if (!value)
|
|
344
|
+
return;
|
|
345
|
+
// 1. Identity mutations: companion identity/role/origin/name/ethos
|
|
346
|
+
const isCompanionTarget = subject.startsWith('companion') ||
|
|
347
|
+
subject === 'siduri' ||
|
|
348
|
+
subject === 'self' ||
|
|
349
|
+
subject === 'assistant' ||
|
|
350
|
+
subject === 'persona' ||
|
|
351
|
+
subject === 'ai' ||
|
|
352
|
+
subject === 'me' ||
|
|
353
|
+
((predicate === 'name' || predicate === 'role' || predicate === 'archetype' || predicate === 'ethos' || predicate === 'origin') &&
|
|
354
|
+
!subject.startsWith('actor:') &&
|
|
355
|
+
subject !== 'user' &&
|
|
356
|
+
subject !== 'primary_user' &&
|
|
357
|
+
subject !== 'owner' &&
|
|
358
|
+
subject !== 'creator');
|
|
359
|
+
if (isCompanionTarget) {
|
|
360
|
+
const existing = (await self.getIdentity(targetCompanionId)) || {
|
|
361
|
+
companionId: targetCompanionId,
|
|
362
|
+
name: '',
|
|
363
|
+
version: '1.0.0',
|
|
364
|
+
updatedAt: new Date().toISOString(),
|
|
365
|
+
};
|
|
366
|
+
if (predicate === 'role' || predicate === 'archetype') {
|
|
367
|
+
existing.archetype = value;
|
|
368
|
+
existing.role = value;
|
|
369
|
+
existing.updatedAt = new Date().toISOString();
|
|
370
|
+
await self.setIdentity(existing);
|
|
371
|
+
await self.commitDirectives(targetCompanionId, [
|
|
372
|
+
{
|
|
373
|
+
id: `dir-role-${claim.id || Date.now()}`,
|
|
374
|
+
companionId: targetCompanionId,
|
|
375
|
+
priority: 70,
|
|
376
|
+
directive: `Acknowledge role as ${value}`,
|
|
377
|
+
status: 'active',
|
|
378
|
+
category: 'relational',
|
|
379
|
+
createdAt: new Date().toISOString(),
|
|
380
|
+
},
|
|
381
|
+
]);
|
|
382
|
+
}
|
|
383
|
+
else if (predicate === 'origin' || predicate === 'created_by') {
|
|
384
|
+
existing.origin = value;
|
|
385
|
+
existing.updatedAt = new Date().toISOString();
|
|
386
|
+
await self.setIdentity(existing);
|
|
387
|
+
}
|
|
388
|
+
else if (predicate === 'name') {
|
|
389
|
+
existing.name = value;
|
|
390
|
+
existing.updatedAt = new Date().toISOString();
|
|
391
|
+
await self.setIdentity(existing);
|
|
392
|
+
}
|
|
393
|
+
else if (predicate === 'ethos') {
|
|
394
|
+
existing.ethos = value;
|
|
395
|
+
existing.updatedAt = new Date().toISOString();
|
|
396
|
+
await self.setIdentity(existing);
|
|
397
|
+
}
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
// 2. Relationship mutations: creator or user stated relationship, name, or affiliation
|
|
401
|
+
if (claim.claimType === 'relationship' ||
|
|
402
|
+
predicate === 'stated_relationship' ||
|
|
403
|
+
predicate === 'relationship' ||
|
|
404
|
+
predicate === 'relationship_to_companion' ||
|
|
405
|
+
predicate === 'relationship_to_siduri' ||
|
|
406
|
+
(predicate === 'name' && (subject.startsWith('actor:') || subject === 'user' || subject === 'primary_user')) ||
|
|
407
|
+
predicate === 'preferred_address' ||
|
|
408
|
+
predicate === 'affiliation') {
|
|
409
|
+
const rawSubject = (claim.subject || 'actor:user').replace(/^actor:actor:/, 'actor:');
|
|
410
|
+
const isCreator = value.toLowerCase().includes('creator');
|
|
411
|
+
const isName = predicate === 'name' || predicate === 'preferred_address';
|
|
412
|
+
const isAffil = predicate === 'affiliation';
|
|
413
|
+
const existingRel = typeof self.getRelationship === 'function'
|
|
414
|
+
? await self.getRelationship(targetCompanionId, rawSubject)
|
|
415
|
+
: null;
|
|
416
|
+
const isPriorCreator = existingRel?.role === 'creator' || (existingRel?.stance === 'familiar_loyal' && existingRel.trustScore === 1.0);
|
|
417
|
+
const role = isCreator
|
|
418
|
+
? 'creator'
|
|
419
|
+
: (isPriorCreator ? 'creator' : (existingRel?.role && existingRel.role !== 'user' ? existingRel.role : (isName || isAffil ? existingRel?.role || 'user' : value)));
|
|
420
|
+
const name = isName ? value : existingRel?.name;
|
|
421
|
+
const affiliation = isAffil ? value : existingRel?.affiliation;
|
|
422
|
+
const stance = isCreator || isPriorCreator ? 'familiar_loyal' : (existingRel?.stance || 'neutral');
|
|
423
|
+
const trustScore = isCreator || isPriorCreator ? 1.0 : (existingRel?.trustScore ?? 0.8);
|
|
424
|
+
const familiarity = isCreator || isPriorCreator ? 0.9 : (existingRel?.familiarity ?? 0.5);
|
|
425
|
+
const interactionConventions = isCreator || isPriorCreator
|
|
426
|
+
? Array.from(new Set([...(existingRel?.interactionConventions || []), 'Direct communication', 'Highest administrative trust']))
|
|
427
|
+
: (existingRel?.interactionConventions || []);
|
|
428
|
+
await self.updateRelationship(targetCompanionId, {
|
|
429
|
+
companionId: targetCompanionId,
|
|
430
|
+
entityId: rawSubject,
|
|
431
|
+
entityType: 'human',
|
|
432
|
+
name,
|
|
433
|
+
affiliation,
|
|
434
|
+
role,
|
|
435
|
+
stance,
|
|
436
|
+
trustScore,
|
|
437
|
+
familiarity,
|
|
438
|
+
interactionConventions,
|
|
439
|
+
});
|
|
440
|
+
// Dual promotion: If this actor is established as creator, also populate companion's origin in self_identity
|
|
441
|
+
if (isCreator || (isName && isPriorCreator)) {
|
|
442
|
+
const existingIdentity = (await self.getIdentity(targetCompanionId)) || {
|
|
443
|
+
companionId: targetCompanionId,
|
|
444
|
+
name: 'Siduri',
|
|
445
|
+
version: '1.0.0',
|
|
446
|
+
updatedAt: new Date().toISOString(),
|
|
447
|
+
};
|
|
448
|
+
const creatorName = name || existingRel?.name || (rawSubject.startsWith('actor:') && rawSubject !== 'actor:user' && rawSubject !== 'actor:primary' ? rawSubject.slice(6) : value);
|
|
449
|
+
existingIdentity.origin = creatorName !== 'user' && creatorName !== 'primary' ? creatorName : value;
|
|
450
|
+
existingIdentity.updatedAt = new Date().toISOString();
|
|
451
|
+
await self.setIdentity(existingIdentity);
|
|
452
|
+
}
|
|
453
|
+
if (isName) {
|
|
454
|
+
await self.commitDirectives(targetCompanionId, [
|
|
455
|
+
{
|
|
456
|
+
id: `dir-name-${claim.id || Date.now()}`,
|
|
457
|
+
companionId: targetCompanionId,
|
|
458
|
+
priority: 75,
|
|
459
|
+
directive: `Address ${rawSubject} as ${value}`,
|
|
460
|
+
status: 'active',
|
|
461
|
+
category: 'relational',
|
|
462
|
+
createdAt: new Date().toISOString(),
|
|
463
|
+
},
|
|
464
|
+
]);
|
|
465
|
+
}
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
// 3. Behavioral rule claim
|
|
469
|
+
if (predicate === 'behavioral_rule' || predicate === 'rule') {
|
|
470
|
+
await self.commitDirectives(targetCompanionId, [
|
|
471
|
+
{
|
|
472
|
+
id: `dir-rule-${claim.id || Date.now()}`,
|
|
473
|
+
companionId: targetCompanionId,
|
|
474
|
+
priority: 60,
|
|
475
|
+
directive: value,
|
|
476
|
+
status: 'active',
|
|
477
|
+
category: 'behavioral',
|
|
478
|
+
createdAt: new Date().toISOString(),
|
|
479
|
+
},
|
|
480
|
+
]);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
function isSelfAffectingClaim(claim) {
|
|
484
|
+
if (!claim)
|
|
485
|
+
return false;
|
|
486
|
+
const subject = (claim.subject || '').toLowerCase();
|
|
487
|
+
const predicate = (claim.predicate || '').toLowerCase();
|
|
488
|
+
return (subject.startsWith('companion') ||
|
|
489
|
+
subject === 'siduri' ||
|
|
490
|
+
subject === 'self' ||
|
|
491
|
+
subject === 'assistant' ||
|
|
492
|
+
subject === 'persona' ||
|
|
493
|
+
subject === 'ai' ||
|
|
494
|
+
subject === 'me' ||
|
|
495
|
+
claim.claimType === 'relationship' ||
|
|
496
|
+
predicate === 'stated_relationship' ||
|
|
497
|
+
predicate === 'relationship' ||
|
|
498
|
+
predicate === 'relationship_to_companion' ||
|
|
499
|
+
predicate === 'relationship_to_siduri' ||
|
|
500
|
+
predicate === 'name' ||
|
|
501
|
+
predicate === 'preferred_address' ||
|
|
502
|
+
predicate === 'affiliation' ||
|
|
503
|
+
predicate === 'role' ||
|
|
504
|
+
predicate === 'archetype' ||
|
|
505
|
+
predicate === 'origin' ||
|
|
506
|
+
predicate === 'created_by' ||
|
|
507
|
+
predicate === 'ethos' ||
|
|
508
|
+
predicate === 'behavioral_rule' ||
|
|
509
|
+
predicate === 'rule');
|
|
510
|
+
}
|
|
511
|
+
function isKnowledgeAffectingClaim(claim) {
|
|
512
|
+
if (!claim)
|
|
513
|
+
return false;
|
|
514
|
+
const subject = (claim.subject || '').toLowerCase();
|
|
515
|
+
const predicate = (claim.predicate || '').toLowerCase();
|
|
516
|
+
const claimType = (claim.claimType || '').toLowerCase();
|
|
517
|
+
return (claim.target === 'knowledge' ||
|
|
518
|
+
claimType === 'life_entity' ||
|
|
519
|
+
claimType === 'entity' ||
|
|
520
|
+
claimType === 'inventory' ||
|
|
521
|
+
claimType === 'life_event' ||
|
|
522
|
+
claimType === 'event' ||
|
|
523
|
+
claimType === 'finance' ||
|
|
524
|
+
claimType === 'life_task' ||
|
|
525
|
+
claimType === 'task' ||
|
|
526
|
+
claimType === 'life_schedule' ||
|
|
527
|
+
claimType === 'schedule' ||
|
|
528
|
+
claimType === 'preference' ||
|
|
529
|
+
claimType === 'life_preference' ||
|
|
530
|
+
subject.startsWith('inventory:') ||
|
|
531
|
+
subject.startsWith('finance:') ||
|
|
532
|
+
subject.startsWith('task:') ||
|
|
533
|
+
subject.startsWith('todo:') ||
|
|
534
|
+
subject.startsWith('schedule:') ||
|
|
535
|
+
subject.startsWith('preference:') ||
|
|
536
|
+
subject.startsWith('entity:') ||
|
|
537
|
+
subject.startsWith('event:') ||
|
|
538
|
+
predicate === 'owns' ||
|
|
539
|
+
predicate === 'inventory' ||
|
|
540
|
+
predicate === 'expense' ||
|
|
541
|
+
predicate === 'income' ||
|
|
542
|
+
predicate === 'spent' ||
|
|
543
|
+
predicate === 'task' ||
|
|
544
|
+
predicate === 'todo' ||
|
|
545
|
+
predicate === 'goal' ||
|
|
546
|
+
predicate === 'schedule' ||
|
|
547
|
+
predicate === 'appointment' ||
|
|
548
|
+
predicate === 'meeting' ||
|
|
549
|
+
predicate === 'preference' ||
|
|
550
|
+
predicate === 'favorite' ||
|
|
551
|
+
predicate === 'prefers');
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Canonically promotes an approved Claim into Knowledge / LifeDatabase state.
|
|
555
|
+
*/
|
|
556
|
+
async function promoteApprovedClaimToKnowledge(claim, knowledge, companionId) {
|
|
557
|
+
if (!knowledge)
|
|
558
|
+
return false;
|
|
559
|
+
const targetCompanionId = companionId || claim.companionId || 'default';
|
|
560
|
+
const subject = (claim.subject || '').toLowerCase();
|
|
561
|
+
const predicate = (claim.predicate || '').toLowerCase();
|
|
562
|
+
const claimType = (claim.claimType || '').toLowerCase();
|
|
563
|
+
const value = claim.value || '';
|
|
564
|
+
const evidenceObj = typeof claim.evidence === 'object' && claim.evidence ? claim.evidence : {};
|
|
565
|
+
// 1. Entities / Inventory
|
|
566
|
+
const isEntityClaim = claimType === 'life_entity' ||
|
|
567
|
+
claimType === 'entity' ||
|
|
568
|
+
claimType === 'inventory' ||
|
|
569
|
+
subject.startsWith('inventory:') ||
|
|
570
|
+
subject.startsWith('entity:') ||
|
|
571
|
+
predicate === 'owns' ||
|
|
572
|
+
predicate.startsWith('owns_') ||
|
|
573
|
+
predicate.startsWith('owns ') ||
|
|
574
|
+
predicate === 'inventory' ||
|
|
575
|
+
predicate.includes('character') ||
|
|
576
|
+
predicate.includes('account') ||
|
|
577
|
+
predicate.includes('device') ||
|
|
578
|
+
predicate.includes('hardware') ||
|
|
579
|
+
predicate.includes('weapon') ||
|
|
580
|
+
predicate.includes('item');
|
|
581
|
+
if (isEntityClaim) {
|
|
582
|
+
const rawName = subject.startsWith('inventory:')
|
|
583
|
+
? claim.subject.slice(10)
|
|
584
|
+
: (subject.startsWith('entity:')
|
|
585
|
+
? claim.subject.slice(7)
|
|
586
|
+
: ((predicate === 'owns' || predicate.startsWith('owns') || predicate.includes('character') || predicate.includes('account') || predicate.includes('item') || predicate.includes('weapon'))
|
|
587
|
+
? value
|
|
588
|
+
: claim.subject));
|
|
589
|
+
const entityName = rawName || 'Unnamed Entity';
|
|
590
|
+
let inferredDomain = claim.domain || evidenceObj.domain || claim.knowledge_domain;
|
|
591
|
+
if (!inferredDomain) {
|
|
592
|
+
if (predicate.includes('game') ||
|
|
593
|
+
predicate.includes('character') ||
|
|
594
|
+
predicate.includes('weapon') ||
|
|
595
|
+
predicate.includes('roster')) {
|
|
596
|
+
inferredDomain = 'gaming';
|
|
597
|
+
}
|
|
598
|
+
else if (claimType === 'inventory' ||
|
|
599
|
+
subject.startsWith('inventory:') ||
|
|
600
|
+
predicate.includes('device') ||
|
|
601
|
+
predicate.includes('hardware')) {
|
|
602
|
+
inferredDomain = 'hardware';
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
605
|
+
inferredDomain = 'general';
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
let inferredEntityType = claim.entityType || evidenceObj.entityType;
|
|
609
|
+
if (!inferredEntityType) {
|
|
610
|
+
if (predicate.includes('character')) {
|
|
611
|
+
inferredEntityType = 'character';
|
|
612
|
+
}
|
|
613
|
+
else if (predicate.includes('account')) {
|
|
614
|
+
inferredEntityType = 'game_account';
|
|
615
|
+
}
|
|
616
|
+
else if (predicate.includes('weapon')) {
|
|
617
|
+
inferredEntityType = 'weapon';
|
|
618
|
+
}
|
|
619
|
+
else if (claimType === 'inventory' || subject.startsWith('inventory:') || predicate.includes('device') || predicate.includes('hardware')) {
|
|
620
|
+
inferredEntityType = 'hardware';
|
|
621
|
+
}
|
|
622
|
+
else {
|
|
623
|
+
inferredEntityType = 'entity';
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
const properties = {
|
|
627
|
+
description: value,
|
|
628
|
+
predicate: claim.predicate,
|
|
629
|
+
...(typeof claim.properties === 'object' ? claim.properties : {}),
|
|
630
|
+
...(typeof evidenceObj.properties === 'object' ? evidenceObj.properties : {}),
|
|
631
|
+
...(typeof claim.metadata === 'object' ? claim.metadata : {}),
|
|
632
|
+
};
|
|
633
|
+
if (knowledge.entities && typeof knowledge.entities.saveEntity === 'function') {
|
|
634
|
+
await knowledge.entities.saveEntity({
|
|
635
|
+
id: claim.id || `ent-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
|
636
|
+
companionId: targetCompanionId,
|
|
637
|
+
entityType: inferredEntityType,
|
|
638
|
+
domain: inferredDomain,
|
|
639
|
+
name: entityName,
|
|
640
|
+
properties,
|
|
641
|
+
updatedAt: new Date().toISOString(),
|
|
642
|
+
});
|
|
643
|
+
return true;
|
|
644
|
+
}
|
|
645
|
+
else if (knowledge.inventory && typeof knowledge.inventory.saveItem === 'function') {
|
|
646
|
+
await knowledge.inventory.saveItem({
|
|
647
|
+
id: claim.id || `inv-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
|
648
|
+
companionId: targetCompanionId,
|
|
649
|
+
domain: inferredDomain,
|
|
650
|
+
entityName,
|
|
651
|
+
properties,
|
|
652
|
+
updatedAt: new Date().toISOString(),
|
|
653
|
+
});
|
|
654
|
+
return true;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
// 2. Events / Finance / Telemetry
|
|
658
|
+
if (claimType === 'life_event' ||
|
|
659
|
+
claimType === 'event' ||
|
|
660
|
+
claimType === 'finance' ||
|
|
661
|
+
subject.startsWith('finance:') ||
|
|
662
|
+
subject.startsWith('event:') ||
|
|
663
|
+
predicate === 'expense' ||
|
|
664
|
+
predicate === 'income' ||
|
|
665
|
+
predicate === 'spent') {
|
|
666
|
+
const stream = claim.stream || evidenceObj.stream || (claimType === 'finance' || subject.startsWith('finance:') || predicate === 'expense' || predicate === 'income' ? 'finance' : 'telemetry');
|
|
667
|
+
const parsedAmount = typeof claim.metricValue === 'number'
|
|
668
|
+
? claim.metricValue
|
|
669
|
+
: (typeof evidenceObj.metricValue === 'number'
|
|
670
|
+
? evidenceObj.metricValue
|
|
671
|
+
: (typeof claim.amount === 'number'
|
|
672
|
+
? claim.amount
|
|
673
|
+
: (typeof evidenceObj.amount === 'number'
|
|
674
|
+
? evidenceObj.amount
|
|
675
|
+
: parseFloat(value) || 0)));
|
|
676
|
+
if (knowledge.events && typeof knowledge.events.addEvent === 'function') {
|
|
677
|
+
await knowledge.events.addEvent({
|
|
678
|
+
id: claim.id || `evt-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
|
679
|
+
companionId: targetCompanionId,
|
|
680
|
+
stream,
|
|
681
|
+
timestamp: claim.timestamp || evidenceObj.timestamp || new Date().toISOString(),
|
|
682
|
+
metricValue: parsedAmount,
|
|
683
|
+
metadata: {
|
|
684
|
+
category: claim.category || evidenceObj.category || predicate,
|
|
685
|
+
value,
|
|
686
|
+
...(typeof claim.metadata === 'object' ? claim.metadata : {}),
|
|
687
|
+
...(typeof evidenceObj.metadata === 'object' ? evidenceObj.metadata : {}),
|
|
688
|
+
},
|
|
689
|
+
});
|
|
690
|
+
return true;
|
|
691
|
+
}
|
|
692
|
+
else if (knowledge.finance && typeof knowledge.finance.addEntry === 'function') {
|
|
693
|
+
await knowledge.finance.addEntry({
|
|
694
|
+
id: claim.id || `fin-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
|
695
|
+
companionId: targetCompanionId,
|
|
696
|
+
category: claim.category || evidenceObj.category || 'general',
|
|
697
|
+
amount: parsedAmount,
|
|
698
|
+
currency: claim.currency || evidenceObj.currency || 'USD',
|
|
699
|
+
timestamp: claim.timestamp || evidenceObj.timestamp || new Date().toISOString(),
|
|
700
|
+
metadata: typeof claim.metadata === 'object' ? claim.metadata : { detail: value },
|
|
701
|
+
});
|
|
702
|
+
return true;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
// 3. Tasks / To-dos / Goals
|
|
706
|
+
if (claimType === 'life_task' ||
|
|
707
|
+
claimType === 'task' ||
|
|
708
|
+
subject.startsWith('task:') ||
|
|
709
|
+
subject.startsWith('todo:') ||
|
|
710
|
+
predicate === 'task' ||
|
|
711
|
+
predicate === 'todo' ||
|
|
712
|
+
predicate === 'goal' ||
|
|
713
|
+
predicate.startsWith('task_') ||
|
|
714
|
+
predicate.startsWith('todo_') ||
|
|
715
|
+
predicate.includes('task') ||
|
|
716
|
+
predicate.includes('todo')) {
|
|
717
|
+
const title = value || (subject.startsWith('task:') ? claim.subject.slice(5) : claim.subject);
|
|
718
|
+
if (knowledge.tasks && typeof knowledge.tasks.saveTask === 'function') {
|
|
719
|
+
await knowledge.tasks.saveTask({
|
|
720
|
+
id: claim.id || `tsk-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
|
721
|
+
companionId: targetCompanionId,
|
|
722
|
+
title,
|
|
723
|
+
status: claim.taskStatus || evidenceObj.status || claim.metadata?.status || (claim.status && claim.status !== 'pending' && claim.status !== 'approved' ? claim.status : 'backlog'),
|
|
724
|
+
priority: claim.priority || evidenceObj.priority || 0,
|
|
725
|
+
targetDate: claim.targetDate || evidenceObj.targetDate || claim.dueDate,
|
|
726
|
+
metadata: typeof claim.metadata === 'object' ? claim.metadata : (typeof evidenceObj.metadata === 'object' ? evidenceObj.metadata : undefined),
|
|
727
|
+
updatedAt: new Date().toISOString(),
|
|
728
|
+
});
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
// 4. Schedule / Deadlines
|
|
733
|
+
if (claimType === 'life_schedule' ||
|
|
734
|
+
claimType === 'schedule' ||
|
|
735
|
+
subject.startsWith('schedule:') ||
|
|
736
|
+
predicate === 'schedule' ||
|
|
737
|
+
predicate === 'appointment' ||
|
|
738
|
+
predicate === 'meeting' ||
|
|
739
|
+
predicate.includes('schedule') ||
|
|
740
|
+
predicate.includes('calendar')) {
|
|
741
|
+
const title = value || (subject.startsWith('schedule:') ? claim.subject.slice(9) : claim.subject);
|
|
742
|
+
if (knowledge.schedule && typeof knowledge.schedule.saveItem === 'function') {
|
|
743
|
+
await knowledge.schedule.saveItem({
|
|
744
|
+
id: claim.id || `sch-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
|
745
|
+
companionId: targetCompanionId,
|
|
746
|
+
title,
|
|
747
|
+
startTime: claim.startTime || claim.start_time || new Date().toISOString(),
|
|
748
|
+
endTime: claim.endTime || claim.end_time,
|
|
749
|
+
isRecurring: Boolean(claim.isRecurring || claim.is_recurring),
|
|
750
|
+
status: claim.status || 'active',
|
|
751
|
+
});
|
|
752
|
+
return true;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
// 5. Preferences (e.g. food preference, theme)
|
|
756
|
+
if (claimType === 'preference' ||
|
|
757
|
+
claimType === 'life_preference' ||
|
|
758
|
+
subject.startsWith('preference:') ||
|
|
759
|
+
predicate === 'preference' ||
|
|
760
|
+
predicate === 'favorite' ||
|
|
761
|
+
predicate === 'prefers') {
|
|
762
|
+
const prefKey = subject.startsWith('preference:')
|
|
763
|
+
? claim.subject.slice(11)
|
|
764
|
+
: (predicate === 'favorite' || predicate === 'prefers' ? claim.subject : (claim.key || claim.preferenceKey || 'user_preference'));
|
|
765
|
+
if (knowledge.preferences && typeof knowledge.preferences.setPreference === 'function') {
|
|
766
|
+
await knowledge.preferences.setPreference({
|
|
767
|
+
id: claim.id || `pref-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
|
768
|
+
companionId: targetCompanionId,
|
|
769
|
+
preferenceKey: prefKey,
|
|
770
|
+
preferenceValue: value,
|
|
771
|
+
category: claim.category || 'general',
|
|
772
|
+
updatedAt: new Date().toISOString(),
|
|
773
|
+
});
|
|
774
|
+
return true;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
return false;
|
|
778
|
+
}
|