@siduri-x/core 2.0.3 → 2.0.5
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/chat-contract.d.ts +4 -0
- package/dist/chat-contract.js +17 -2
- package/dist/context-retriever.d.ts +3 -0
- package/dist/context-retriever.js +23 -2
- package/dist/conversational-teach.test.d.ts +1 -0
- package/dist/conversational-teach.test.js +513 -0
- package/dist/index.d.ts +11 -3
- package/dist/memory-settler.d.ts +22 -1
- package/dist/memory-settler.js +70 -9
- package/dist/mouth-types.d.ts +7 -2
- package/dist/perception-pipeline.d.ts +1 -0
- package/dist/perception-pipeline.js +20 -2
- package/dist/prompt-compiler.d.ts +4 -0
- package/dist/prompt-compiler.js +8 -1
- package/dist/proposals.d.ts +2 -1
- package/dist/response-envelope.d.ts +4 -0
- package/dist/response-envelope.js +14 -3
- package/dist/runtime.d.ts +47 -1
- package/dist/runtime.js +209 -2
- package/dist/siduri-db.d.ts +14 -2
- package/dist/siduri-db.js +217 -127
- package/dist/siduri-db.test.js +19 -18
- package/dist/teaching.js +201 -12
- package/package.json +1 -1
package/dist/siduri-db.test.js
CHANGED
|
@@ -370,7 +370,7 @@ describe('SiduriDatabase', () => {
|
|
|
370
370
|
evidence: ['lore book chapter 3'],
|
|
371
371
|
assertedAt: new Date().toISOString(),
|
|
372
372
|
});
|
|
373
|
-
expect(claim.status).toBe('
|
|
373
|
+
expect(claim.status).toBe('pending');
|
|
374
374
|
expect(claim.subject).toBe('Kur');
|
|
375
375
|
expect(claim.predicate).toBe('is');
|
|
376
376
|
expect(claim.value).toBe('a dark entity from the underworld');
|
|
@@ -401,7 +401,7 @@ describe('SiduriDatabase', () => {
|
|
|
401
401
|
const approved = db.getApprovedClaims('siduri-test');
|
|
402
402
|
expect(approved).toHaveLength(1);
|
|
403
403
|
expect(approved[0].id).toBe(claim1.id);
|
|
404
|
-
expect(approved[0].status).toBe('
|
|
404
|
+
expect(approved[0].status).toBe('approved');
|
|
405
405
|
});
|
|
406
406
|
it('searches claims using FTS5 full-text search', () => {
|
|
407
407
|
db = new siduri_db_1.SiduriDatabase({ dbPath });
|
|
@@ -617,7 +617,7 @@ describe('SiduriDatabase', () => {
|
|
|
617
617
|
expect(db2.getInventory(cId)).toHaveLength(1);
|
|
618
618
|
expect(db2.getInventory(cId)[0].entityName).toBe('Aged Wine');
|
|
619
619
|
const claims = db2.searchClaims(cId, 'wine');
|
|
620
|
-
expect(claims.some((c) => c.id === claim.id && c.status === '
|
|
620
|
+
expect(claims.some((c) => c.id === claim.id && c.status === 'approved')).toBe(true);
|
|
621
621
|
db2.close();
|
|
622
622
|
// Prevent afterEach from double-closing
|
|
623
623
|
db = null;
|
|
@@ -691,7 +691,7 @@ describe('SiduriDatabase', () => {
|
|
|
691
691
|
const active = db.getActiveDirectives(cId);
|
|
692
692
|
expect(active).toHaveLength(1);
|
|
693
693
|
expect(active[0].id).toBe(dId);
|
|
694
|
-
expect(active[0].status).toBe('
|
|
694
|
+
expect(active[0].status).toBe('active');
|
|
695
695
|
// 3. Revoke directive
|
|
696
696
|
db.revokeDirective(dId);
|
|
697
697
|
expect(db.getActiveDirectives(cId)).toHaveLength(0);
|
|
@@ -702,7 +702,7 @@ describe('SiduriDatabase', () => {
|
|
|
702
702
|
companionId: cId,
|
|
703
703
|
priority: 50,
|
|
704
704
|
directive: 'Unsafe rule',
|
|
705
|
-
status: '
|
|
705
|
+
status: 'pending',
|
|
706
706
|
category: 'behavioral',
|
|
707
707
|
});
|
|
708
708
|
db.rejectDirective(d2Id);
|
|
@@ -717,22 +717,23 @@ describe('SiduriDatabase', () => {
|
|
|
717
717
|
companionId: cId,
|
|
718
718
|
priority: 50,
|
|
719
719
|
directive: 'Rejected directive',
|
|
720
|
-
status: '
|
|
720
|
+
status: 'rejected',
|
|
721
721
|
category: 'behavioral',
|
|
722
722
|
});
|
|
723
|
-
expect(() => db.approveDirective('dir-rejected-1')).toThrow(/invalid transition from status '
|
|
723
|
+
expect(() => db.approveDirective('dir-rejected-1')).toThrow(/invalid transition from status 'rejected' to 'active'/i);
|
|
724
724
|
// 2. Commit directive in ACTIVE state
|
|
725
725
|
db.commitDirective({
|
|
726
726
|
id: 'dir-active-1',
|
|
727
727
|
companionId: cId,
|
|
728
728
|
priority: 50,
|
|
729
729
|
directive: 'Already active directive',
|
|
730
|
-
status: '
|
|
730
|
+
status: 'active',
|
|
731
731
|
category: 'behavioral',
|
|
732
732
|
});
|
|
733
|
-
|
|
733
|
+
// Approving an already-active directive is now idempotent (no-op, does not throw)
|
|
734
|
+
expect(() => db.approveDirective('dir-active-1')).not.toThrow();
|
|
734
735
|
// 3. Rejecting an already ACTIVE directive throws
|
|
735
|
-
expect(() => db.rejectDirective('dir-active-1')).toThrow(/invalid transition from status '
|
|
736
|
+
expect(() => db.rejectDirective('dir-active-1')).toThrow(/invalid transition from status 'active' to 'rejected'/i);
|
|
736
737
|
});
|
|
737
738
|
it('automatically marks prior directive as SUPERSEDED when approving superseding directive', () => {
|
|
738
739
|
db = new siduri_db_1.SiduriDatabase({ dbPath });
|
|
@@ -766,7 +767,7 @@ describe('SiduriDatabase', () => {
|
|
|
766
767
|
expect(active).toHaveLength(1);
|
|
767
768
|
expect(active[0].id).toBe('dir-replacement-1');
|
|
768
769
|
const original = db.getDirective('dir-original-1', cId);
|
|
769
|
-
expect(original?.status).toBe('
|
|
770
|
+
expect(original?.status).toBe('superseded');
|
|
770
771
|
});
|
|
771
772
|
it('enforces companion isolation on directive approval', () => {
|
|
772
773
|
db = new siduri_db_1.SiduriDatabase({ dbPath });
|
|
@@ -777,14 +778,14 @@ describe('SiduriDatabase', () => {
|
|
|
777
778
|
companionId: cIdB,
|
|
778
779
|
priority: 50,
|
|
779
780
|
directive: 'Beta private rule',
|
|
780
|
-
status: '
|
|
781
|
+
status: 'pending',
|
|
781
782
|
category: 'behavioral',
|
|
782
783
|
});
|
|
783
784
|
// Alpha attempts to approve Beta's directive scoped to Alpha
|
|
784
785
|
db.approveDirective('dir-beta-1', cIdA);
|
|
785
|
-
// Beta's directive must remain
|
|
786
|
+
// Beta's directive must remain pending and unapproved
|
|
786
787
|
const betaDirective = db.getDirective('dir-beta-1', cIdB);
|
|
787
|
-
expect(betaDirective?.status).toBe('
|
|
788
|
+
expect(betaDirective?.status).toBe('pending');
|
|
788
789
|
expect(db.getActiveDirectives(cIdB)).toHaveLength(0);
|
|
789
790
|
// Beta approves its own directive successfully
|
|
790
791
|
db.approveDirective('dir-beta-1', cIdB);
|
|
@@ -800,7 +801,7 @@ describe('SiduriDatabase', () => {
|
|
|
800
801
|
predicate: 'likes',
|
|
801
802
|
value: 'matcha',
|
|
802
803
|
});
|
|
803
|
-
expect(claim.status).toBe('
|
|
804
|
+
expect(claim.status).toBe('pending');
|
|
804
805
|
expect(db.getApprovedClaims(cId)).toHaveLength(0);
|
|
805
806
|
// Approve
|
|
806
807
|
db.approveClaim('claim-1');
|
|
@@ -835,7 +836,7 @@ describe('SiduriDatabase', () => {
|
|
|
835
836
|
});
|
|
836
837
|
db.rejectClaim(rejectedClaim.id);
|
|
837
838
|
// Attempting to approve a REJECTED claim must throw
|
|
838
|
-
expect(() => db.approveClaim(rejectedClaim.id)).toThrow(/invalid transition from status '
|
|
839
|
+
expect(() => db.approveClaim(rejectedClaim.id)).toThrow(/invalid transition from status 'rejected' to 'approved'/i);
|
|
839
840
|
// 2. Propose and approve claim, then revoke
|
|
840
841
|
const revokedClaim = db.proposeClaim({
|
|
841
842
|
id: 'claim-revoked',
|
|
@@ -847,7 +848,7 @@ describe('SiduriDatabase', () => {
|
|
|
847
848
|
db.approveClaim(revokedClaim.id);
|
|
848
849
|
db.revokeClaim(revokedClaim.id);
|
|
849
850
|
// Attempting to approve a REVOKED claim must throw
|
|
850
|
-
expect(() => db.approveClaim(revokedClaim.id)).toThrow(/invalid transition from status '
|
|
851
|
+
expect(() => db.approveClaim(revokedClaim.id)).toThrow(/invalid transition from status 'revoked' to 'approved'/i);
|
|
851
852
|
// 3. Propose and expire claim
|
|
852
853
|
const expiredClaim = db.proposeClaim({
|
|
853
854
|
id: 'claim-expired',
|
|
@@ -858,7 +859,7 @@ describe('SiduriDatabase', () => {
|
|
|
858
859
|
});
|
|
859
860
|
db.expireClaim(expiredClaim.id);
|
|
860
861
|
// Attempting to approve an EXPIRED claim must throw
|
|
861
|
-
expect(() => db.approveClaim(expiredClaim.id)).toThrow(/invalid transition from status '
|
|
862
|
+
expect(() => db.approveClaim(expiredClaim.id)).toThrow(/invalid transition from status 'expired' to 'approved'/i);
|
|
862
863
|
});
|
|
863
864
|
});
|
|
864
865
|
});
|
package/dist/teaching.js
CHANGED
|
@@ -20,12 +20,20 @@ function extractDeterministicTeaching(message, context, sourceEventId) {
|
|
|
20
20
|
if (!text) {
|
|
21
21
|
return { claims, behaviorProposals };
|
|
22
22
|
}
|
|
23
|
-
const
|
|
24
|
-
const
|
|
23
|
+
const rawActorId = context?.actor?.actorId;
|
|
24
|
+
const cleanActorId = rawActorId ? rawActorId.replace(/^actor:/, '') : undefined;
|
|
25
|
+
const actorSubject = cleanActorId ? `actor:${cleanActorId}` : 'actor:anonymous';
|
|
25
26
|
const companionId = context?.companionId || 'default';
|
|
26
27
|
const sensitivity = context?.conversation?.channel === 'public' ? 'public' : 'private';
|
|
28
|
+
// Strip leading companion addressing: "Siduri, you are...", "Hey Siduri: ..."
|
|
29
|
+
const cleanText = text.replace(/^(?:(?:hey|hi|hello)\s+)?(?:siduri|companion|assistant)\s*[,:]\s*/i, '');
|
|
30
|
+
const isTeachMode = context?.mode === 'teach' || /^(?:!teach|\/teach|\bteach mode\b|\blearn this rule\b)/i.test(text);
|
|
31
|
+
const hasExplicitTeachingCue = isTeachMode ||
|
|
32
|
+
/\b(?:remember that|remember this rule|remember this|from now on|learn this|rule:)\b/i.test(text);
|
|
33
|
+
// Casual banter / hedged opinion filter to avoid mutating Self from generic conversation
|
|
34
|
+
const isCasualBanter = /\b(?:probably|maybe|such a|so |very |really |just |not |the funniest|the best|the worst|the coolest|great|awesome|funny|nice|kind|crazy|wrong|right|silly)\b/i;
|
|
27
35
|
// 1. Companion's Name: "your name is X" / "you are called X"
|
|
28
|
-
const companionNameMatch =
|
|
36
|
+
const companionNameMatch = cleanText.match(/\b(?:your name is|you are called)\s+(.+?)(?=\s+and\s+(?:i\b|my\b|you\b)|[.;,]|$)/i);
|
|
29
37
|
if (companionNameMatch) {
|
|
30
38
|
const name = cleanValue(companionNameMatch[1], 80);
|
|
31
39
|
claims.push({
|
|
@@ -45,12 +53,124 @@ function extractDeterministicTeaching(message, context, sourceEventId) {
|
|
|
45
53
|
predicate: 'name',
|
|
46
54
|
value: name,
|
|
47
55
|
memoryClass: 'identity',
|
|
56
|
+
category: 'relational',
|
|
48
57
|
sourceEventId,
|
|
49
58
|
});
|
|
50
59
|
}
|
|
51
|
-
// 2.
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
// 2. Identity / Role Teaching:
|
|
61
|
+
// "your role is X", "you work at X", "you were created by X", "you are from X",
|
|
62
|
+
// or explicit "you are [an/a] X [at Y]" when in teach mode or with explicit teaching cues
|
|
63
|
+
const roleMatch = cleanText.match(/\byour role is\s+(.+?)(?=\s+and\s+(?:i\b|my\b|you\b)|[.;,]|$)/i);
|
|
64
|
+
const workMatch = cleanText.match(/\byou work (?:at|for)\s+(.+?)(?=\s+and\s+(?:i\b|my\b|you\b)|[.;,]|$)/i);
|
|
65
|
+
const createdByMatch = cleanText.match(/\byou (?:were|are) created by\s+(.+?)(?=\s+and\s+(?:i\b|my\b|you\b)|[.;,]|$)/i);
|
|
66
|
+
const fromMatch = cleanText.match(/\byou are from\s+(.+?)(?=\s+and\s+(?:i\b|my\b|you\b)|[.;,]|$)/i);
|
|
67
|
+
// Match "you are [an/a/the] X [at Y]" only when in Teach mode or explicit teaching cues, and not casual banter
|
|
68
|
+
const youAreMatch = (isTeachMode || hasExplicitTeachingCue)
|
|
69
|
+
? cleanText.match(/\b(?:remember that\s+)?you are\s+(?:(?:an?|the)\s+)?(.+?)(?=\s+and\s+(?:i\b|my\b|you\b)|[.;,]|$)/i)
|
|
70
|
+
: null;
|
|
71
|
+
const validYouAreRole = youAreMatch &&
|
|
72
|
+
!companionNameMatch &&
|
|
73
|
+
!fromMatch &&
|
|
74
|
+
!isCasualBanter.test(youAreMatch[1]) &&
|
|
75
|
+
!/^(?:called|named)\b/i.test(youAreMatch[1])
|
|
76
|
+
? cleanValue(youAreMatch[1].replace(/^(?:an?|the)\s+/i, ''), 100)
|
|
77
|
+
: undefined;
|
|
78
|
+
const rawEffectiveRole = roleMatch ? cleanValue(roleMatch[1], 100) : validYouAreRole;
|
|
79
|
+
const effectiveRole = rawEffectiveRole ? rawEffectiveRole.replace(/^(?:an?|the)\s+/i, '') : undefined;
|
|
80
|
+
if (effectiveRole) {
|
|
81
|
+
claims.push({
|
|
82
|
+
subject: `companion:${companionId}`,
|
|
83
|
+
predicate: 'role',
|
|
84
|
+
value: effectiveRole,
|
|
85
|
+
content: `The companion's role is ${effectiveRole}.`,
|
|
86
|
+
claimType: 'semantic',
|
|
87
|
+
provenance: 'deterministic_teaching',
|
|
88
|
+
sensitivity: 'public',
|
|
89
|
+
sourceEventId,
|
|
90
|
+
});
|
|
91
|
+
behaviorProposals.push({
|
|
92
|
+
directive: `Acknowledge role as ${effectiveRole}`,
|
|
93
|
+
priority: 70,
|
|
94
|
+
subject: `companion:${companionId}`,
|
|
95
|
+
predicate: 'role',
|
|
96
|
+
value: effectiveRole,
|
|
97
|
+
memoryClass: 'identity',
|
|
98
|
+
category: 'relational',
|
|
99
|
+
sourceEventId,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (workMatch) {
|
|
103
|
+
const workplace = cleanValue(workMatch[1], 80);
|
|
104
|
+
claims.push({
|
|
105
|
+
subject: `companion:${companionId}`,
|
|
106
|
+
predicate: 'origin',
|
|
107
|
+
value: workplace,
|
|
108
|
+
content: `The companion works at ${workplace}.`,
|
|
109
|
+
claimType: 'semantic',
|
|
110
|
+
provenance: 'deterministic_teaching',
|
|
111
|
+
sensitivity: 'public',
|
|
112
|
+
sourceEventId,
|
|
113
|
+
});
|
|
114
|
+
behaviorProposals.push({
|
|
115
|
+
directive: `Acknowledge workplace as ${workplace}`,
|
|
116
|
+
priority: 70,
|
|
117
|
+
subject: `companion:${companionId}`,
|
|
118
|
+
predicate: 'origin',
|
|
119
|
+
value: workplace,
|
|
120
|
+
memoryClass: 'identity',
|
|
121
|
+
category: 'relational',
|
|
122
|
+
sourceEventId,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (createdByMatch) {
|
|
126
|
+
const creator = cleanValue(createdByMatch[1], 80);
|
|
127
|
+
claims.push({
|
|
128
|
+
subject: `companion:${companionId}`,
|
|
129
|
+
predicate: 'created_by',
|
|
130
|
+
value: creator,
|
|
131
|
+
content: `The companion was created by ${creator}.`,
|
|
132
|
+
claimType: 'semantic',
|
|
133
|
+
provenance: 'deterministic_teaching',
|
|
134
|
+
sensitivity: 'public',
|
|
135
|
+
sourceEventId,
|
|
136
|
+
});
|
|
137
|
+
behaviorProposals.push({
|
|
138
|
+
directive: `Acknowledge creator as ${creator}`,
|
|
139
|
+
priority: 75,
|
|
140
|
+
subject: `companion:${companionId}`,
|
|
141
|
+
predicate: 'created_by',
|
|
142
|
+
value: creator,
|
|
143
|
+
memoryClass: 'identity',
|
|
144
|
+
category: 'relational',
|
|
145
|
+
sourceEventId,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
if (fromMatch) {
|
|
149
|
+
const origin = cleanValue(fromMatch[1], 80);
|
|
150
|
+
claims.push({
|
|
151
|
+
subject: `companion:${companionId}`,
|
|
152
|
+
predicate: 'origin',
|
|
153
|
+
value: origin,
|
|
154
|
+
content: `The companion is from ${origin}.`,
|
|
155
|
+
claimType: 'semantic',
|
|
156
|
+
provenance: 'deterministic_teaching',
|
|
157
|
+
sensitivity: 'public',
|
|
158
|
+
sourceEventId,
|
|
159
|
+
});
|
|
160
|
+
behaviorProposals.push({
|
|
161
|
+
directive: `Acknowledge origin as ${origin}`,
|
|
162
|
+
priority: 70,
|
|
163
|
+
subject: `companion:${companionId}`,
|
|
164
|
+
predicate: 'origin',
|
|
165
|
+
value: origin,
|
|
166
|
+
memoryClass: 'identity',
|
|
167
|
+
category: 'relational',
|
|
168
|
+
sourceEventId,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
// 3. Actor's Name: "my name is X"
|
|
172
|
+
const myNameMatch = cleanText.match(/\bmy name is\s+(.+?)(?=\s+and\s+(?:i\b|my\b|you\b)|[.;,]|$)/i);
|
|
173
|
+
if (myNameMatch && !/\b(?:private|public|everywhere)\b/i.test(cleanText)) {
|
|
54
174
|
const name = cleanValue(myNameMatch[1], 80);
|
|
55
175
|
claims.push({
|
|
56
176
|
subject: actorSubject,
|
|
@@ -63,8 +183,8 @@ function extractDeterministicTeaching(message, context, sourceEventId) {
|
|
|
63
183
|
sourceEventId,
|
|
64
184
|
});
|
|
65
185
|
}
|
|
66
|
-
//
|
|
67
|
-
const callMeMatch =
|
|
186
|
+
// 4. Preferred Address / Call me X: "call me X"
|
|
187
|
+
const callMeMatch = cleanText.match(/\b(?:(?:from now on|only),?\s*)?call me\s+(.+?)(?:\s+(?:in private|privately|in public|publicly|everywhere|in direct conversations))?(?=\s+and\s+(?:i\b|my\b|you\b)|[.;,]|$)/i);
|
|
68
188
|
if (callMeMatch) {
|
|
69
189
|
const address = cleanValue(callMeMatch[1], 80);
|
|
70
190
|
const directiveInstruction = `Address ${actorSubject} as ${address}`;
|
|
@@ -85,13 +205,15 @@ function extractDeterministicTeaching(message, context, sourceEventId) {
|
|
|
85
205
|
predicate: 'preferred_address',
|
|
86
206
|
value: address,
|
|
87
207
|
memoryClass: 'behavioral',
|
|
208
|
+
category: 'behavioral',
|
|
88
209
|
sourceEventId,
|
|
89
210
|
});
|
|
90
211
|
}
|
|
91
|
-
//
|
|
92
|
-
const relMatch =
|
|
212
|
+
// 5. Stated relationship: "I am your creator, Kur Zagin" / "I am your X" / "I'm your creator"
|
|
213
|
+
const relMatch = cleanText.match(/\b(?:i am|i'm)\s+your\s+([A-Za-z0-9_-]+)(?:,\s*([A-Za-z0-9_\s-]+?))?(?=\s+and\s+(?:i\b|my\b|you\b)|[.;]|$)/i);
|
|
93
214
|
if (relMatch) {
|
|
94
215
|
const relationship = cleanValue(relMatch[1], 60);
|
|
216
|
+
const actorName = relMatch[2] ? cleanValue(relMatch[2], 80) : undefined;
|
|
95
217
|
claims.push({
|
|
96
218
|
subject: actorSubject,
|
|
97
219
|
predicate: 'stated_relationship',
|
|
@@ -109,11 +231,78 @@ function extractDeterministicTeaching(message, context, sourceEventId) {
|
|
|
109
231
|
predicate: 'stated_relationship',
|
|
110
232
|
value: relationship,
|
|
111
233
|
memoryClass: 'relationship',
|
|
234
|
+
category: 'relational',
|
|
235
|
+
sourceEventId,
|
|
236
|
+
});
|
|
237
|
+
if (actorName) {
|
|
238
|
+
claims.push({
|
|
239
|
+
subject: actorSubject,
|
|
240
|
+
predicate: 'name',
|
|
241
|
+
value: actorName,
|
|
242
|
+
content: `The actor's name is ${actorName}.`,
|
|
243
|
+
claimType: 'preference',
|
|
244
|
+
provenance: 'deterministic_teaching',
|
|
245
|
+
sensitivity,
|
|
246
|
+
sourceEventId,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// 6. Behavioral rule / directive: "Remember this rule: X" / "Remember that rule: X" / "Rule: X" / "From now on, always X"
|
|
251
|
+
const ruleMatch = cleanText.match(/\b(?:remember\s+(?:this|that)\s+rule:\s*|rule:\s*|(?:from now on,?\s+)?always\s+)(.+?)(?=\s+and\s+(?:i\b|my\b|you\b)|[.;]|$)/i);
|
|
252
|
+
if (ruleMatch) {
|
|
253
|
+
let instruction = cleanValue(ruleMatch[1], 160);
|
|
254
|
+
if (instruction) {
|
|
255
|
+
instruction = instruction.charAt(0).toUpperCase() + instruction.slice(1);
|
|
256
|
+
behaviorProposals.push({
|
|
257
|
+
directive: instruction,
|
|
258
|
+
priority: 60,
|
|
259
|
+
subject: actorSubject,
|
|
260
|
+
predicate: 'rule',
|
|
261
|
+
value: instruction,
|
|
262
|
+
category: 'behavioral',
|
|
263
|
+
memoryClass: 'behavioral',
|
|
264
|
+
sourceEventId,
|
|
265
|
+
});
|
|
266
|
+
claims.push({
|
|
267
|
+
subject: actorSubject,
|
|
268
|
+
predicate: 'behavioral_rule',
|
|
269
|
+
value: instruction,
|
|
270
|
+
content: `Behavioral rule: ${instruction}`,
|
|
271
|
+
claimType: 'preference',
|
|
272
|
+
provenance: 'deterministic_teaching',
|
|
273
|
+
sensitivity,
|
|
274
|
+
sourceEventId,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// Preference: "I prefer concise answers"
|
|
279
|
+
const prefAnswersMatch = cleanText.match(/\b(?:i prefer|my preference is)\s+(.+?)(?=\s+and\s+(?:i\b|my\b|you\b)|[.;,]|$)/i);
|
|
280
|
+
if (prefAnswersMatch && !cleanText.match(/\bmy\s+preferred\s+([A-Za-z0-9_]+)\s+is\b/i)) {
|
|
281
|
+
const prefVal = cleanValue(prefAnswersMatch[1], 100);
|
|
282
|
+
const directive = `Prefer ${prefVal}`;
|
|
283
|
+
behaviorProposals.push({
|
|
284
|
+
directive,
|
|
285
|
+
priority: 60,
|
|
286
|
+
subject: actorSubject,
|
|
287
|
+
predicate: 'preference',
|
|
288
|
+
value: prefVal,
|
|
289
|
+
category: 'behavioral',
|
|
290
|
+
memoryClass: 'behavioral',
|
|
291
|
+
sourceEventId,
|
|
292
|
+
});
|
|
293
|
+
claims.push({
|
|
294
|
+
subject: actorSubject,
|
|
295
|
+
predicate: 'preference',
|
|
296
|
+
value: prefVal,
|
|
297
|
+
content: `The actor prefers ${prefVal}.`,
|
|
298
|
+
claimType: 'preference',
|
|
299
|
+
provenance: 'deterministic_teaching',
|
|
300
|
+
sensitivity,
|
|
112
301
|
sourceEventId,
|
|
113
302
|
});
|
|
114
303
|
}
|
|
115
|
-
//
|
|
116
|
-
const prefMatch =
|
|
304
|
+
// 7. Explicit Domain / Preference fact: "my preferred X is Y" / "my X is Y"
|
|
305
|
+
const prefMatch = cleanText.match(/\bmy\s+preferred\s+([A-Za-z0-9_]+)\s+is\s+(.+?)(?=\s+and\s+(?:i\b|my\b|you\b)|[.;,]|$)/i);
|
|
117
306
|
if (prefMatch) {
|
|
118
307
|
const predicate = cleanValue(prefMatch[1], 40);
|
|
119
308
|
const val = cleanValue(prefMatch[2], 100);
|
package/package.json
CHANGED