ei-tui 0.1.8 → 0.1.10
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/package.json
CHANGED
|
@@ -233,6 +233,7 @@ function handleEiHeartbeat(response: LLMResponse, state: StateManager): void {
|
|
|
233
233
|
timestamp: now,
|
|
234
234
|
read: false,
|
|
235
235
|
context_status: ContextStatus.Default,
|
|
236
|
+
f: true, r: true, p: true, o: true,
|
|
236
237
|
});
|
|
237
238
|
|
|
238
239
|
if (found.type === "fact") {
|
|
@@ -672,7 +673,7 @@ async function handleHumanItemUpdate(response: LLMResponse, state: StateManager)
|
|
|
672
673
|
const isEi = personaDisplayName.toLowerCase() === "ei";
|
|
673
674
|
|
|
674
675
|
const human = state.getHuman();
|
|
675
|
-
const getExistingItem = (): { learned_by?: string; persona_groups?: string[] } | undefined => {
|
|
676
|
+
const getExistingItem = (): { learned_by?: string; last_changed_by?: string; persona_groups?: string[] } | undefined => {
|
|
676
677
|
if (isNewItem) return undefined;
|
|
677
678
|
switch (candidateType) {
|
|
678
679
|
case "fact": return human.facts.find(f => f.id === existingItemId);
|
|
@@ -710,7 +711,8 @@ async function handleHumanItemUpdate(response: LLMResponse, state: StateManager)
|
|
|
710
711
|
validated: ValidationLevel.None,
|
|
711
712
|
validated_date: now,
|
|
712
713
|
last_updated: now,
|
|
713
|
-
learned_by: isNewItem ?
|
|
714
|
+
learned_by: isNewItem ? personaId : existingItem?.learned_by,
|
|
715
|
+
last_changed_by: personaId,
|
|
714
716
|
persona_groups: mergeGroups(existingItem?.persona_groups),
|
|
715
717
|
embedding,
|
|
716
718
|
};
|
|
@@ -725,7 +727,8 @@ async function handleHumanItemUpdate(response: LLMResponse, state: StateManager)
|
|
|
725
727
|
sentiment: result.sentiment,
|
|
726
728
|
strength: (result as any).strength ?? 0.5,
|
|
727
729
|
last_updated: now,
|
|
728
|
-
learned_by: isNewItem ?
|
|
730
|
+
learned_by: isNewItem ? personaId : existingItem?.learned_by,
|
|
731
|
+
last_changed_by: personaId,
|
|
729
732
|
persona_groups: mergeGroups(existingItem?.persona_groups),
|
|
730
733
|
embedding,
|
|
731
734
|
};
|
|
@@ -745,7 +748,8 @@ async function handleHumanItemUpdate(response: LLMResponse, state: StateManager)
|
|
|
745
748
|
exposure_current: calculateExposureCurrent(exposureImpact),
|
|
746
749
|
exposure_desired: (result as any).exposure_desired ?? 0.5,
|
|
747
750
|
last_updated: now,
|
|
748
|
-
learned_by: isNewItem ?
|
|
751
|
+
learned_by: isNewItem ? personaId : existingItem?.learned_by,
|
|
752
|
+
last_changed_by: personaId,
|
|
749
753
|
persona_groups: mergeGroups(existingItem?.persona_groups),
|
|
750
754
|
embedding,
|
|
751
755
|
};
|
|
@@ -763,7 +767,8 @@ async function handleHumanItemUpdate(response: LLMResponse, state: StateManager)
|
|
|
763
767
|
exposure_current: calculateExposureCurrent(exposureImpact),
|
|
764
768
|
exposure_desired: (result as any).exposure_desired ?? 0.5,
|
|
765
769
|
last_updated: now,
|
|
766
|
-
learned_by: isNewItem ?
|
|
770
|
+
learned_by: isNewItem ? personaId : existingItem?.learned_by,
|
|
771
|
+
last_changed_by: personaId,
|
|
767
772
|
persona_groups: mergeGroups(existingItem?.persona_groups),
|
|
768
773
|
embedding,
|
|
769
774
|
};
|
package/src/core/processor.ts
CHANGED
|
@@ -565,7 +565,7 @@ export class Processor {
|
|
|
565
565
|
const items: EiHeartbeatItem[] = [];
|
|
566
566
|
|
|
567
567
|
const unverifiedFacts = human.facts
|
|
568
|
-
.filter(f => f.validated === ValidationLevel.None && f.learned_by !== "
|
|
568
|
+
.filter(f => f.validated === ValidationLevel.None && f.learned_by !== "ei" && (f.last_changed_by === undefined || f.last_changed_by !== "ei"))
|
|
569
569
|
.slice(0, 5);
|
|
570
570
|
for (const fact of unverifiedFacts) {
|
|
571
571
|
const quote = human.quotes.find(q => q.data_item_ids.includes(fact.id));
|
|
@@ -36,11 +36,60 @@ export class StateManager {
|
|
|
36
36
|
this.humanState.load(state.human);
|
|
37
37
|
this.personaState.load(state.personas);
|
|
38
38
|
this.queueState.load(state.queue);
|
|
39
|
+
this.migrateLearnedByToIds();
|
|
39
40
|
} else {
|
|
40
41
|
this.humanState.load(createDefaultHumanEntity());
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Migration: learned_by used to store display names; now stores persona IDs.
|
|
47
|
+
* On load, attempt to resolve display names -> IDs using current persona map.
|
|
48
|
+
* Unresolvable values (renamed/deleted personas) are cleared to avoid stale display.
|
|
49
|
+
* No-op for already-migrated data (UUIDs or "ei" won't match display names).
|
|
50
|
+
*/
|
|
51
|
+
private migrateLearnedByToIds(): void {
|
|
52
|
+
const personas = this.personaState.getAll();
|
|
53
|
+
const nameToId = new Map<string, string>();
|
|
54
|
+
for (const p of personas) {
|
|
55
|
+
nameToId.set(p.display_name.toLowerCase(), p.id);
|
|
56
|
+
for (const alias of p.aliases ?? []) {
|
|
57
|
+
nameToId.set(alias.toLowerCase(), p.id);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// "Ei" display name -> "ei" id (hardcoded, always valid)
|
|
61
|
+
nameToId.set("ei", "ei");
|
|
62
|
+
|
|
63
|
+
const human = this.humanState.get();
|
|
64
|
+
let dirty = false;
|
|
65
|
+
const migrateItem = (item: { learned_by?: string; last_changed_by?: string }) => {
|
|
66
|
+
if (item.learned_by && !this.isPersonaId(item.learned_by)) {
|
|
67
|
+
const resolved = nameToId.get(item.learned_by.toLowerCase());
|
|
68
|
+
item.learned_by = resolved ?? undefined; // clear if unresolvable
|
|
69
|
+
dirty = true;
|
|
70
|
+
}
|
|
71
|
+
if (item.last_changed_by && !this.isPersonaId(item.last_changed_by)) {
|
|
72
|
+
const resolved = nameToId.get(item.last_changed_by.toLowerCase());
|
|
73
|
+
item.last_changed_by = resolved ?? undefined;
|
|
74
|
+
dirty = true;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
[...human.facts, ...human.traits, ...human.topics, ...human.people].forEach(migrateItem);
|
|
78
|
+
if (dirty) {
|
|
79
|
+
this.humanState.set(human);
|
|
80
|
+
console.log("[StateManager] Migrated learned_by fields from display names to persona IDs");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Returns true if value looks like a persona ID (UUID or the special "ei" id).
|
|
86
|
+
* Display names are free-form strings that won't match UUID format.
|
|
87
|
+
*/
|
|
88
|
+
private isPersonaId(value: string): boolean {
|
|
89
|
+
if (value === "ei") return true;
|
|
90
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
91
|
+
}
|
|
92
|
+
|
|
44
93
|
private buildStorageState(): StorageState {
|
|
45
94
|
return {
|
|
46
95
|
version: 1,
|
package/src/core/types.ts
CHANGED
|
@@ -62,7 +62,8 @@ export interface DataItemBase {
|
|
|
62
62
|
description: string;
|
|
63
63
|
sentiment: number;
|
|
64
64
|
last_updated: string;
|
|
65
|
-
learned_by?: string; // Persona ID that learned this item
|
|
65
|
+
learned_by?: string; // Persona ID that originally learned this item (stable UUID)
|
|
66
|
+
last_changed_by?: string; // Persona ID that most recently updated this item (stable UUID)
|
|
66
67
|
persona_groups?: string[];
|
|
67
68
|
embedding?: number[];
|
|
68
69
|
}
|
|
@@ -272,7 +272,8 @@ async function ensureSessionTopic(
|
|
|
272
272
|
const existingTopic = human.topics.find((t) => t.id === session.id);
|
|
273
273
|
|
|
274
274
|
const firstAgent = await reader.getFirstAgent(session.id);
|
|
275
|
-
const
|
|
275
|
+
const firstPersona = firstAgent ? stateManager.persona_getByName(firstAgent) : null;
|
|
276
|
+
const learnedBy = firstPersona?.id ?? firstAgent ?? "build";
|
|
276
277
|
|
|
277
278
|
if (existingTopic) {
|
|
278
279
|
if (existingTopic.name !== session.title) {
|
|
@@ -350,7 +350,10 @@ export function humanToYAML(human: HumanEntity): string {
|
|
|
350
350
|
|
|
351
351
|
return YAML.stringify(data, {
|
|
352
352
|
lineWidth: 0,
|
|
353
|
-
})
|
|
353
|
+
})
|
|
354
|
+
.replace(/^(\s+validated:\s+\S+)$/mg, '$1 # none | ei | human')
|
|
355
|
+
.replace(/^(\s+)(learned_by: .+)$/mg, '$1# [read-only] $2')
|
|
356
|
+
.replace(/^(\s+)(last_changed_by: .+)$/mg, '$1# [read-only] $2');
|
|
354
357
|
}
|
|
355
358
|
|
|
356
359
|
export interface HumanYAMLResult {
|
|
@@ -365,7 +368,12 @@ export interface HumanYAMLResult {
|
|
|
365
368
|
}
|
|
366
369
|
|
|
367
370
|
export function humanFromYAML(yamlContent: string): HumanYAMLResult {
|
|
368
|
-
|
|
371
|
+
// Strip read-only comment lines before parsing so users can't accidentally corrupt them
|
|
372
|
+
const stripped = yamlContent
|
|
373
|
+
.split('\n')
|
|
374
|
+
.filter(line => !/^\s*#\s*\[read-only\]/.test(line))
|
|
375
|
+
.join('\n');
|
|
376
|
+
const data = YAML.parse(stripped) as EditableHumanData;
|
|
369
377
|
|
|
370
378
|
const deletedFactIds: string[] = [];
|
|
371
379
|
const deletedTraitIds: string[] = [];
|