ei-tui 0.9.2 → 0.9.4
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 +17 -1
- package/src/README.md +1 -1
- package/src/cli/commands/personas.ts +11 -2
- package/src/cli/mcp.ts +2 -2
- package/src/cli/retrieval.ts +40 -7
- package/src/cli.ts +63 -72
- package/src/core/context-utils.ts +2 -2
- package/src/core/handlers/heartbeat.ts +9 -1
- package/src/core/handlers/human-extraction.ts +4 -1
- package/src/core/handlers/human-matching.ts +5 -53
- package/src/core/handlers/index.ts +1 -51
- package/src/core/handlers/persona-generation.ts +1 -28
- package/src/core/handlers/utils.ts +2 -9
- package/src/core/heartbeat-manager.ts +4 -4
- package/src/core/message-manager.ts +6 -5
- package/src/core/orchestrators/ceremony.ts +15 -13
- package/src/core/orchestrators/extraction-chunker.ts +3 -3
- package/src/core/orchestrators/human-extraction.ts +27 -39
- package/src/core/orchestrators/index.ts +0 -1
- package/src/core/orchestrators/persona-topics.ts +1 -1
- package/src/core/orchestrators/room-extraction.ts +45 -7
- package/src/core/processor.ts +8 -21
- package/src/core/prompt-context-builder.ts +68 -36
- package/src/core/state/personas.ts +1 -17
- package/src/core/state-manager.ts +0 -66
- package/src/core/types/entities.ts +2 -3
- package/src/core/types/enums.ts +0 -2
- package/src/core/types/rooms.ts +1 -1
- package/src/integrations/claude-code/importer.ts +1 -1
- package/src/integrations/cursor/importer.ts +1 -1
- package/src/integrations/opencode/importer.ts +1 -1
- package/src/prompts/ceremony/index.ts +0 -10
- package/src/prompts/ceremony/types.ts +1 -42
- package/src/prompts/generation/index.ts +0 -3
- package/src/prompts/generation/types.ts +0 -15
- package/src/prompts/heartbeat/check.ts +18 -6
- package/src/prompts/heartbeat/types.ts +2 -1
- package/src/prompts/human/index.ts +0 -2
- package/src/prompts/human/person-update.ts +6 -23
- package/src/prompts/human/types.ts +0 -16
- package/src/prompts/index.ts +0 -19
- package/src/prompts/reflection/index.ts +36 -4
- package/src/prompts/reflection/types.ts +1 -1
- package/src/prompts/response/index.ts +5 -0
- package/src/prompts/response/sections.ts +26 -0
- package/src/prompts/response/types.ts +3 -0
- package/tui/src/commands/registry.test.ts +10 -5
- package/tui/src/globals.d.ts +57 -0
- package/tui/src/util/yaml-persona.ts +8 -4
- package/tui/src/util/yaml-settings.ts +3 -3
- package/src/core/orchestrators/person-migration.ts +0 -55
- package/src/prompts/ceremony/description-check.ts +0 -54
- package/src/prompts/ceremony/expire.ts +0 -37
- package/src/prompts/ceremony/explore.ts +0 -77
- package/src/prompts/ceremony/person-migration.ts +0 -77
- package/src/prompts/generation/descriptions.ts +0 -91
- package/src/prompts/human/fact-scan.ts +0 -150
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import type { Person } from "../../core/types/data-items.js";
|
|
2
|
-
import type { PromptOutput } from "../persona/types.js";
|
|
3
|
-
|
|
4
|
-
export interface PersonMigrationPromptData {
|
|
5
|
-
person: Pick<Person, "name" | "description" | "relationship">;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function buildPersonMigrationPrompt(data: PersonMigrationPromptData): PromptOutput {
|
|
9
|
-
const { person } = data;
|
|
10
|
-
|
|
11
|
-
const system = `You are extracting identifiers for a Person record in a personal knowledge system.
|
|
12
|
-
|
|
13
|
-
A Person record has a \`name\` and \`description\` that describe who they are. Your job is to extract ALL identifiers for this person — every name, handle, alias, or platform ID that refers to them.
|
|
14
|
-
|
|
15
|
-
## CRITICAL RULES
|
|
16
|
-
|
|
17
|
-
1. The \`name\` field value MUST appear in identifiers — never lose it.
|
|
18
|
-
2. If \`name\` contains a space (e.g. "Jeremy Scherer"), create BOTH a \`Full Name\` identifier AND a \`First Name\` identifier for the given name.
|
|
19
|
-
3. Mark exactly one identifier as \`is_primary: true\` — the most natural display name.
|
|
20
|
-
4. Check \`read_memory\` for additional context before finalizing.
|
|
21
|
-
5. Return ONLY a JSON object with an \`identifiers\` array. No other text.
|
|
22
|
-
6. "none" is never valid — every person has at least one identifier (their name).
|
|
23
|
-
|
|
24
|
-
## DESCRIPTION PRE-PROCESSING
|
|
25
|
-
|
|
26
|
-
If the description begins with a JSON block (e.g. \`{"identifiers": [...]}\` or \`[{...}]\`), those are pre-seeded identifiers from a prior migration step:
|
|
27
|
-
- Parse them out and include them in your output
|
|
28
|
-
- Normalize their \`type\` values to Title Case (e.g. \`nickname\` → \`Nickname\`, \`full_name\` → \`Full Name\`)
|
|
29
|
-
- Treat the remainder of the description (after the JSON block) as the actual description text for \`read_memory\` context
|
|
30
|
-
|
|
31
|
-
## IDENTIFIER TYPES
|
|
32
|
-
|
|
33
|
-
Use Title Case for all types. Built-in types:
|
|
34
|
-
|
|
35
|
-
| Type | Meaning |
|
|
36
|
-
|------|---------|
|
|
37
|
-
| Full Name | Legal or full birth name |
|
|
38
|
-
| First Name | Given/first name only |
|
|
39
|
-
| Nickname | Informal name, diminutive, pet name |
|
|
40
|
-
| Email | Email address |
|
|
41
|
-
| GitHub | GitHub username |
|
|
42
|
-
| Discord | Discord username |
|
|
43
|
-
| Roblox | Roblox username |
|
|
44
|
-
| Reddit | Reddit username |
|
|
45
|
-
| Twitter | Twitter/X handle |
|
|
46
|
-
| FF14 | Final Fantasy XIV character name |
|
|
47
|
-
| Relationship | How the user addresses this person: Dad, Pop, Sis, etc. |
|
|
48
|
-
| Ei Persona | Ei persona UUID (only if explicitly in the data) |
|
|
49
|
-
|
|
50
|
-
Any string is valid as a type — users define their own (e.g. \`Slack-ASU\`, \`Slack-RnP\`, \`sehimu_thinara\`). Use the exact casing the user would recognize.
|
|
51
|
-
|
|
52
|
-
## RESPONSE FORMAT
|
|
53
|
-
|
|
54
|
-
\`\`\`json
|
|
55
|
-
{
|
|
56
|
-
"identifiers": [
|
|
57
|
-
{ "type": "Nickname", "value": "Flare", "is_primary": true },
|
|
58
|
-
{ "type": "Full Name", "value": "Jeremy Scherer" },
|
|
59
|
-
{ "type": "First Name", "value": "Jeremy" },
|
|
60
|
-
{ "type": "GitHub", "value": "Flare576" },
|
|
61
|
-
{ "type": "Slack-RnP", "value": "jeremy.scherer" }
|
|
62
|
-
]
|
|
63
|
-
}
|
|
64
|
-
\`\`\``;
|
|
65
|
-
|
|
66
|
-
const user = `Extract identifiers for this Person record.
|
|
67
|
-
|
|
68
|
-
Name: ${person.name}
|
|
69
|
-
Relationship: ${person.relationship ?? "unknown"}
|
|
70
|
-
Description: ${person.description ?? "(none)"}
|
|
71
|
-
|
|
72
|
-
First, call read_memory to search for any additional context about "${person.name}". Then return the complete identifiers array.
|
|
73
|
-
|
|
74
|
-
Return JSON only.`;
|
|
75
|
-
|
|
76
|
-
return { system, user };
|
|
77
|
-
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import type { PersonaDescriptionsPromptData, PromptOutput } from "./types.js";
|
|
2
|
-
import type { PersonaTrait, PersonaTopic } from "../../core/types.js";
|
|
3
|
-
|
|
4
|
-
function formatTraitsForPrompt(traits: PersonaTrait[]): string {
|
|
5
|
-
if (traits.length === 0) return "(No traits defined)";
|
|
6
|
-
|
|
7
|
-
return traits.map(t => {
|
|
8
|
-
const strength = t.strength !== undefined ? ` (strength: ${t.strength.toFixed(1)})` : "";
|
|
9
|
-
return `- **${t.name}**${strength}: ${t.description}`;
|
|
10
|
-
}).join('\n');
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function formatTopicsForPrompt(topics: PersonaTopic[]): string {
|
|
14
|
-
if (topics.length === 0) return "(No topics defined)";
|
|
15
|
-
|
|
16
|
-
return topics.map(t => {
|
|
17
|
-
const sentiment = t.sentiment > 0.3 ? "enjoys" : t.sentiment < -0.3 ? "dislikes" : "neutral";
|
|
18
|
-
return `- **${t.name}** (${sentiment}): ${t.perspective || t.name}`;
|
|
19
|
-
}).join('\n');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function buildPersonaDescriptionsPrompt(data: PersonaDescriptionsPromptData): PromptOutput {
|
|
23
|
-
if (!data.name) {
|
|
24
|
-
throw new Error("buildPersonaDescriptionsPrompt: name is required");
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const taskFragment = `You are regenerating descriptions for an existing AI persona named "${data.name}".
|
|
28
|
-
|
|
29
|
-
Their traits and topics have evolved, and the descriptions may no longer accurately represent who they are.
|
|
30
|
-
|
|
31
|
-
**Important**: Only change descriptions if there's a significant mismatch. If the current descriptions still fit, return \`{ "no_change": true }\`.`;
|
|
32
|
-
|
|
33
|
-
const currentStateFragment = `## Current State
|
|
34
|
-
|
|
35
|
-
### Aliases
|
|
36
|
-
${data.aliases.length > 0 ? data.aliases.join(", ") : "(None)"}
|
|
37
|
-
|
|
38
|
-
### Traits
|
|
39
|
-
${formatTraitsForPrompt(data.traits)}
|
|
40
|
-
|
|
41
|
-
### Topics
|
|
42
|
-
${formatTopicsForPrompt(data.topics)}`;
|
|
43
|
-
|
|
44
|
-
const guidelinesFragment = `## Guidelines
|
|
45
|
-
|
|
46
|
-
**When to change:**
|
|
47
|
-
- Traits/topics have shifted significantly from the original concept
|
|
48
|
-
- Current descriptions mention things that are no longer true
|
|
49
|
-
- The persona's core identity has evolved
|
|
50
|
-
|
|
51
|
-
**When NOT to change:**
|
|
52
|
-
- Descriptions are still accurate even if incomplete
|
|
53
|
-
- Changes are minor refinements
|
|
54
|
-
- Original descriptions capture the essence well
|
|
55
|
-
|
|
56
|
-
**If changing:**
|
|
57
|
-
- short_description: 10-15 words capturing the essence
|
|
58
|
-
- long_description: 2-3 sentences describing personality, interests, approach
|
|
59
|
-
- Preserve the persona's core identity while reflecting evolution`;
|
|
60
|
-
|
|
61
|
-
const schemaFragment = `## Response Format
|
|
62
|
-
|
|
63
|
-
If descriptions should change:
|
|
64
|
-
\`\`\`json
|
|
65
|
-
{
|
|
66
|
-
"short_description": "New short description here",
|
|
67
|
-
"long_description": "New long description here."
|
|
68
|
-
}
|
|
69
|
-
\`\`\`
|
|
70
|
-
|
|
71
|
-
If descriptions are still accurate:
|
|
72
|
-
\`\`\`json
|
|
73
|
-
{
|
|
74
|
-
"no_change": true
|
|
75
|
-
}
|
|
76
|
-
\`\`\``;
|
|
77
|
-
|
|
78
|
-
const system = `${taskFragment}
|
|
79
|
-
|
|
80
|
-
${currentStateFragment}
|
|
81
|
-
|
|
82
|
-
${guidelinesFragment}
|
|
83
|
-
|
|
84
|
-
${schemaFragment}`;
|
|
85
|
-
|
|
86
|
-
const user = `Based on the traits and topics above, should ${data.name}'s descriptions be updated?
|
|
87
|
-
|
|
88
|
-
Remember: Only change if there's a significant mismatch. Stability is preferred.`;
|
|
89
|
-
|
|
90
|
-
return { system, user };
|
|
91
|
-
}
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import type { FactScanPromptData, PromptOutput } from "./types.js";
|
|
2
|
-
import { formatMessagesAsPlaceholders } from "../message-utils.js";
|
|
3
|
-
|
|
4
|
-
export function buildHumanFactScanPrompt(data: FactScanPromptData): PromptOutput {
|
|
5
|
-
if (!data.persona_name) {
|
|
6
|
-
throw new Error("buildHumanFactScanPrompt: persona_name is required");
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const personaName = data.persona_name;
|
|
10
|
-
|
|
11
|
-
const taskFragment = `# Task
|
|
12
|
-
|
|
13
|
-
You are scanning a conversation to quickly identify what FACTS were provided or discussed by the HUMAN USER. Your ONLY job is to spot relevant FACTS - do NOT analyze them deeply. Just detect and flag.`;
|
|
14
|
-
|
|
15
|
-
const specificNeedsFragment = `## Specific Needs
|
|
16
|
-
|
|
17
|
-
Your job is to quickly identify:
|
|
18
|
-
1. Which FACTS were mentioned or relevant
|
|
19
|
-
a. Only flag FACTS that were actually discussed, not just tangentially related
|
|
20
|
-
b. Be CONSERVATIVE - only suggest genuinely important, long-term relevant FACTS
|
|
21
|
-
i. Ignore: greetings, small talk, one-off mentions, jokes
|
|
22
|
-
c. Be CLEAR - state your \`reason\` for including this FACT in the record with any evidence you used`;
|
|
23
|
-
|
|
24
|
-
const guidelinesFragment = `# Guidelines
|
|
25
|
-
|
|
26
|
-
1. **Explicitness:**
|
|
27
|
-
* **Focus only on what the user *explicitly states*.** Do not infer, assume, or guess based on context or general knowledge.
|
|
28
|
-
* **Prioritize direct statements.** "I was born in 1985" is a fact. "I feel old now that it's 3030" isn't an explicit statement of their birth year.
|
|
29
|
-
2. **Objectivity and Verifiability:**
|
|
30
|
-
* **Facts are objective and generally verifiable.** They are not subjective opinions, feelings, or temporary states.
|
|
31
|
-
* **Focus on unchangeable or enduring attributes/events.**
|
|
32
|
-
3. **Specificity over Generality:**
|
|
33
|
-
* If the user says "I live in a big city," do not extract "Location: big city." If they say "I live in New York," extract "Location: New York."
|
|
34
|
-
4. **Avoid Inference from Interests/Hobbies:**
|
|
35
|
-
* If a user talks extensively about cooking, it's a Topic or Interest, not a Fact like "Job: Chef" unless they explicitly state they ARE a chef.
|
|
36
|
-
5. **CRITICAL - Entity Attribution:**
|
|
37
|
-
* ONLY extract facts about THE HUMAN USER THEMSELVES, not facts about other people they mention.
|
|
38
|
-
* **Extract**: "I was born in 1984" → User's birthday
|
|
39
|
-
* **Extract**: "I'm a software engineer" → User's job
|
|
40
|
-
* **DO NOT Extract**: "My wife was a theater major" → This is about the wife, NOT the user
|
|
41
|
-
* **DO NOT Extract**: "My daughter is 10 years old" → This is about the daughter, NOT the user
|
|
42
|
-
* **DO NOT Extract**: "My brother lives in Texas" → This is about the brother, NOT the user
|
|
43
|
-
* If the user shares information about someone else, that belongs in PEOPLE tracking, not FACTS.`;
|
|
44
|
-
|
|
45
|
-
const examplesFragment = `# Specific Examples
|
|
46
|
-
|
|
47
|
-
**FACTS are:**
|
|
48
|
-
- Biographical data (Core Identity):
|
|
49
|
-
- type_of_fact: User's Name
|
|
50
|
-
- First Last, Nickname, etc.
|
|
51
|
-
- type_of_fact: Birthday
|
|
52
|
-
- example: "July 15th, 1980"
|
|
53
|
-
- type_of_fact: Birthplace
|
|
54
|
-
- type_of_fact: Hometown
|
|
55
|
-
- type_of_fact: Job
|
|
56
|
-
- current job title, industry, or company
|
|
57
|
-
- type_of_fact: Marital Status
|
|
58
|
-
- married, single, divorced
|
|
59
|
-
- type_of_fact: Gender
|
|
60
|
-
- type_of_fact: Eye Color
|
|
61
|
-
- type_of_fact: Hair Color
|
|
62
|
-
- type_of_fact: Nationality/Citizenship
|
|
63
|
-
- type_of_fact: Languages Spoken
|
|
64
|
-
- type_of_fact: Educational Background
|
|
65
|
-
- Other Important Dates
|
|
66
|
-
- type_of_fact: Wedding Anniversary
|
|
67
|
-
- type_of_fact: Job Anniversary
|
|
68
|
-
- type_of_fact: Pet Ownership
|
|
69
|
-
- Health & Well-being (Objective Conditions):
|
|
70
|
-
- type_of_fact: Allergies
|
|
71
|
-
- type_of_fact: Medical Conditions (if explicitly stated)
|
|
72
|
-
- type_of_fact: Dietary Restrictions
|
|
73
|
-
|
|
74
|
-
> NOTE: Dates themselves are not facts (e.g., "August 15th" is not a fact).
|
|
75
|
-
> They are details OF facts (e.g., { "type_of_fact": "Birthday", "value_of_fact": "August 15th" }).
|
|
76
|
-
|
|
77
|
-
**FACTS ARE NOT**
|
|
78
|
-
- General Topic: Interests, hobbies, general subjects
|
|
79
|
-
- These are tracked separately
|
|
80
|
-
- Relationships: Wife, Husband, Daughter, Son, etc.
|
|
81
|
-
- These are tracked separately
|
|
82
|
-
- People's Names
|
|
83
|
-
- These are tracked separately
|
|
84
|
-
- Personas: AI personas they discuss
|
|
85
|
-
- These are tracked separately
|
|
86
|
-
- Characters: Fictitious entities from books, movies, stories, media, etc.
|
|
87
|
-
- These are tracked separately`;
|
|
88
|
-
|
|
89
|
-
const criticalFragment = `# CRITICAL INSTRUCTIONS
|
|
90
|
-
|
|
91
|
-
ONLY ANALYZE the "Most Recent Messages" in the following conversation. The "Earlier Conversation" is provided for your context and has already been processed!
|
|
92
|
-
|
|
93
|
-
The JSON format is:
|
|
94
|
-
|
|
95
|
-
\`\`\`json
|
|
96
|
-
{
|
|
97
|
-
"facts": [
|
|
98
|
-
{
|
|
99
|
-
"type_of_fact": "The Fact Type from above",
|
|
100
|
-
"value_of_fact": "The exact value of the fact",
|
|
101
|
-
"reason": "The justification of including this specific fact"
|
|
102
|
-
}
|
|
103
|
-
]
|
|
104
|
-
}
|
|
105
|
-
\`\`\`
|
|
106
|
-
|
|
107
|
-
**Return JSON only.**`;
|
|
108
|
-
|
|
109
|
-
const system = `${taskFragment}
|
|
110
|
-
|
|
111
|
-
${specificNeedsFragment}
|
|
112
|
-
|
|
113
|
-
${guidelinesFragment}
|
|
114
|
-
|
|
115
|
-
${examplesFragment}
|
|
116
|
-
|
|
117
|
-
${criticalFragment}`;
|
|
118
|
-
|
|
119
|
-
const earlierSection = data.messages_context.length > 0
|
|
120
|
-
? `## Earlier Conversation
|
|
121
|
-
${formatMessagesAsPlaceholders(data.messages_context, personaName)}
|
|
122
|
-
|
|
123
|
-
`
|
|
124
|
-
: '';
|
|
125
|
-
|
|
126
|
-
const recentSection = `## Most Recent Messages
|
|
127
|
-
${formatMessagesAsPlaceholders(data.messages_analyze, personaName)}`;
|
|
128
|
-
|
|
129
|
-
const user = `# Conversation
|
|
130
|
-
${earlierSection}${recentSection}
|
|
131
|
-
|
|
132
|
-
---
|
|
133
|
-
|
|
134
|
-
Scan the "Most Recent Messages" for FACTS about the human user.
|
|
135
|
-
|
|
136
|
-
**Return JSON:**
|
|
137
|
-
\`\`\`json
|
|
138
|
-
{
|
|
139
|
-
"facts": [
|
|
140
|
-
{
|
|
141
|
-
"type_of_fact": "The Fact Type from above",
|
|
142
|
-
"value_of_fact": "The exact value of the fact",
|
|
143
|
-
"reason": "The justification of including this specific fact"
|
|
144
|
-
}
|
|
145
|
-
]
|
|
146
|
-
}
|
|
147
|
-
\`\`\``;
|
|
148
|
-
|
|
149
|
-
return { system, user };
|
|
150
|
-
}
|