ei-tui 0.1.25 → 0.2.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.
Files changed (78) hide show
  1. package/README.md +42 -0
  2. package/package.json +1 -1
  3. package/src/README.md +4 -11
  4. package/src/cli/README.md +4 -5
  5. package/src/cli/retrieval.ts +3 -25
  6. package/src/cli.ts +3 -7
  7. package/src/core/AGENTS.md +1 -1
  8. package/src/core/constants/built-in-facts.ts +49 -0
  9. package/src/core/constants/index.ts +1 -0
  10. package/src/core/context-utils.ts +0 -1
  11. package/src/core/embedding-service.ts +8 -0
  12. package/src/core/handlers/dedup.ts +10 -16
  13. package/src/core/handlers/heartbeat.ts +2 -3
  14. package/src/core/handlers/human-extraction.ts +95 -30
  15. package/src/core/handlers/human-matching.ts +326 -248
  16. package/src/core/handlers/index.ts +8 -6
  17. package/src/core/handlers/persona-generation.ts +8 -8
  18. package/src/core/handlers/rewrite.ts +4 -29
  19. package/src/core/handlers/utils.ts +23 -1
  20. package/src/core/heartbeat-manager.ts +2 -4
  21. package/src/core/human-data-manager.ts +5 -27
  22. package/src/core/message-manager.ts +10 -10
  23. package/src/core/orchestrators/ceremony.ts +50 -39
  24. package/src/core/orchestrators/dedup-phase.ts +0 -1
  25. package/src/core/orchestrators/human-extraction.ts +351 -207
  26. package/src/core/orchestrators/index.ts +6 -4
  27. package/src/core/orchestrators/persona-generation.ts +3 -3
  28. package/src/core/processor.ts +99 -17
  29. package/src/core/prompt-context-builder.ts +4 -6
  30. package/src/core/state/human.ts +1 -26
  31. package/src/core/state/personas.ts +2 -2
  32. package/src/core/state-manager.ts +107 -14
  33. package/src/core/tools/builtin/read-memory.ts +7 -8
  34. package/src/core/types/data-items.ts +2 -4
  35. package/src/core/types/entities.ts +6 -4
  36. package/src/core/types/enums.ts +6 -9
  37. package/src/core/types/llm.ts +2 -2
  38. package/src/core/utils/crossFind.ts +2 -5
  39. package/src/core/utils/event-windows.ts +31 -0
  40. package/src/integrations/claude-code/importer.ts +8 -4
  41. package/src/integrations/claude-code/types.ts +2 -0
  42. package/src/integrations/opencode/importer.ts +7 -3
  43. package/src/prompts/AGENTS.md +73 -1
  44. package/src/prompts/ceremony/rewrite.ts +3 -22
  45. package/src/prompts/ceremony/types.ts +3 -3
  46. package/src/prompts/generation/descriptions.ts +2 -2
  47. package/src/prompts/generation/types.ts +2 -2
  48. package/src/prompts/heartbeat/types.ts +2 -2
  49. package/src/prompts/human/event-scan.ts +122 -0
  50. package/src/prompts/human/fact-find.ts +106 -0
  51. package/src/prompts/human/fact-scan.ts +0 -2
  52. package/src/prompts/human/index.ts +17 -10
  53. package/src/prompts/human/person-match.ts +65 -0
  54. package/src/prompts/human/person-scan.ts +52 -59
  55. package/src/prompts/human/person-update.ts +241 -0
  56. package/src/prompts/human/topic-match.ts +65 -0
  57. package/src/prompts/human/topic-scan.ts +51 -71
  58. package/src/prompts/human/topic-update.ts +295 -0
  59. package/src/prompts/human/types.ts +63 -40
  60. package/src/prompts/index.ts +4 -8
  61. package/src/prompts/persona/topics-update.ts +2 -2
  62. package/src/prompts/persona/traits.ts +2 -2
  63. package/src/prompts/persona/types.ts +3 -3
  64. package/src/prompts/response/index.ts +1 -1
  65. package/src/prompts/response/sections.ts +9 -12
  66. package/src/prompts/response/types.ts +2 -3
  67. package/src/storage/embeddings.ts +1 -1
  68. package/src/storage/index.ts +1 -0
  69. package/src/storage/indexed.ts +174 -0
  70. package/src/storage/merge.ts +67 -2
  71. package/tui/src/commands/me.tsx +5 -14
  72. package/tui/src/commands/settings.tsx +15 -0
  73. package/tui/src/context/ei.tsx +5 -14
  74. package/tui/src/util/yaml-serializers.ts +48 -33
  75. package/src/cli/commands/traits.ts +0 -25
  76. package/src/prompts/human/item-match.ts +0 -74
  77. package/src/prompts/human/item-update.ts +0 -364
  78. package/src/prompts/human/trait-scan.ts +0 -115
@@ -1,6 +1,19 @@
1
- import type { TopicScanPromptData, PromptOutput } from "./types.js";
1
+ import type { TopicScanPromptData, PromptOutput, ParticipantContext } from "./types.js";
2
2
  import { formatMessagesAsPlaceholders } from "../message-utils.js";
3
3
 
4
+ function participantContextSection(ctx: ParticipantContext | undefined): string {
5
+ if (!ctx) return "";
6
+ const lines: string[] = ["# Participant Context", "The following may help you understand what themes and moments are meaningful in this conversation.", ""];
7
+ lines.push(`## Persona: ${ctx.persona_name}`);
8
+ if (ctx.persona_description) lines.push(ctx.persona_description);
9
+ lines.push("");
10
+ lines.push("## Human");
11
+ if (ctx.human_name) lines.push(`Name: ${ctx.human_name}`);
12
+ if (ctx.human_age !== undefined) lines.push(`Age: ${ctx.human_age}`);
13
+ lines.push("");
14
+ return lines.join("\n");
15
+ }
16
+
4
17
  export function buildHumanTopicScanPrompt(data: TopicScanPromptData): PromptOutput {
5
18
  if (!data.persona_name) {
6
19
  throw new Error("buildHumanTopicScanPrompt: persona_name is required");
@@ -8,98 +21,64 @@ export function buildHumanTopicScanPrompt(data: TopicScanPromptData): PromptOutp
8
21
 
9
22
  const personaName = data.persona_name;
10
23
 
11
- const taskFragment = `# Task
12
-
13
- You are scanning a conversation to quickly identify TOPICS of interest TO the HUMAN USER. Your ONLY job is to spot mentions of TOPICS. 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 TOPICS were mentioned or relevant
19
- a. Only flag TOPICS that were actually discussed, not just tangentially related
20
- b. Be CONSERVATIVE - only suggest genuinely important, long-term relevant TOPICS
21
- c. Be CLEAR - state your \`reason\` for including this TOPIC with any evidence you used
22
-
23
- The goal of the system is to remember important TOPICS to the HUMAN USER in order to ask about them in the future.`;
24
-
25
- const guidelinesFragment = `## Guidelines
24
+ const system = `# Task
26
25
 
27
- # A TOPIC Is:
26
+ You are scanning a conversation to quickly identify TOPICS of interest to the HUMAN USER.
28
27
 
29
- A meaningful subject or concept relevant to the HUMAN USER. It is:
28
+ Detect and flag. Do NOT analyze deeply that happens later.
30
29
 
31
- - **Specific and Contextual:** Not a broad category or just a list of isolated facts. It must have narrative or direct relevance in the conversation.
30
+ ## What to Capture
32
31
 
33
- 1. **Primary Focus** - Capture the main idea of the conversation, not minute details
34
- 2. **Participation** - Things the HUMAN USER does or wants to do
35
- 3. **Interests** - Hobbies or concepts they spend time on BY CHOICE
36
- 4. **Responsibilities** - Tasks or requirements that occupy their time BY NECESSITY
37
- 5. **Knowledge** - Ideas they are exploring or learning about, or are expert in
38
- 6. **Dreams** - Wild ideas, hopes for the future, or vision of an ideal state
39
- 7. **Conflicts** - Things they have difficulty with or are frustrated with
40
- 8. **Concerns** - Ideas they express worry over
41
- 9. **Stories and Characters** - Extended narratives they share (more than a sentence or two)
42
- 10. **Location** - Favorite places, travel destinations
43
- 11. **Preferences** - "I like {thing}" or "I hate {thing}" statements`;
32
+ Flag a TOPIC when it was meaningfully discussed not just mentioned in passing.
44
33
 
45
- const doNotCaptureFragment = `# **IMPORTANT** The Following Are NOT TOPICS
34
+ Be **conservative**: only flag topics that are genuinely relevant to the human user long-term. Noise is worse than gaps.
46
35
 
47
- # The system tracks FACTS, TRAITS, and PEOPLE as separate types. Do NOT capture:
36
+ ## What a TOPIC Is
48
37
 
49
- ## FACTS - Tracked Separately
50
- - **Biographical Data (Do NOT Capture):** Name, Nickname, Birthday, Location, Job, Marital Status, Gender, Eye Color, Hair Color.
51
- > **CRITICAL:** The HUMAN USER's name itself, or a collection of their basic biographical facts, is NEVER a TOPIC. Even if multiple biographical facts are mentioned together (e.g., "My name is John, I live in NYC, and I'm a software engineer"), do not summarize them as a 'topic' about the user's identity.
52
- - Other Important Dates: Wedding Anniversary, Job Anniversary
53
- - Health & Well-being: Allergies, Medical Conditions, Dietary Restrictions
38
+ A meaningful subject in the human user's life: something they care about, work on, worry over, or experience. It has context and weight — not just a passing reference.
54
39
 
55
- > NOTE: Many FACTS have stories/topics around them.
56
- > "My birthday is May 26th" is a FACT. "A goat jumped out of my birthday cake" is a TOPIC.
40
+ **NOT a TOPIC:**
41
+ - Biographical facts (birthday, job title, location) those are Facts
42
+ - People (family, friends, coworkers, AI personas) — those are People
43
+ - One-off mentions, small talk, or jokes with no deeper relevance
57
44
 
58
- ## TRAITS - Tracked Separately
59
- * Personality Patterns, Communication style, Behavioral tendencies
60
- * Cognitive Style, Emotional Traits, Work Ethic, Social Orientation
45
+ ## Category
61
46
 
62
- > NOTE: Many TRAITS have stories/topics around them.
63
- > "I'm a visual learner" is a TRAIT. "I saw a picture of an atom and I FINALLY GET IT" is a TOPIC.
47
+ Assign each TOPIC one category. Pick the closest fit:
64
48
 
65
- ## PEOPLE / Relationships - Tracked Separately
66
- - Immediate family, Extended family, Friends, Coworkers, etc.
49
+ - **Interest** hobbies, activities, ongoing fascinations
50
+ - **Goal** things they want to achieve
51
+ - **Dream** — aspirational, maybe unrealistic desires
52
+ - **Conflict** — internal or external struggles, dilemmas
53
+ - **Concern** — worries, anxieties about something real
54
+ - **Fear** — things that scare them
55
+ - **Hope** — positive expectations for the future
56
+ - **Plan** — concrete intentions with steps in mind
57
+ - **Project** — active undertakings with real progress
58
+ - **Event** — a specific, significant moment that either party might reference later ("remember when...")
67
59
 
68
- > NOTE: Many PEOPLE have stories/topics around them.
69
- > "Sarah is my dream girl" is a PERSON. "I hope Sarah and I get married on the moon" is a TOPIC.
60
+ When in doubt, pick the closest match. The update step will refine it.
70
61
 
71
- ## AI PERSONAS - Tracked Separately
72
- - Do NOT record any stories or details about PERSONAS as TOPICS`;
73
-
74
- const criticalFragment = `# CRITICAL INSTRUCTIONS
75
-
76
- ONLY ANALYZE the "Most Recent Messages" in the following conversation. The "Earlier Conversation" is provided for your context and has already been processed!
77
-
78
- The JSON format is:
62
+ ## Output Format
79
63
 
80
64
  \`\`\`json
81
65
  {
82
66
  "topics": [
83
67
  {
84
- "type_of_topic": "The Topic Type from the list above",
85
- "value_of_topic": "<actual topic from the conversation>",
86
- "reason": "The justification of including this specific topic"
68
+ "name": "Short label for the topic (10-75 characters)",
69
+ "description": "1-2 sentences: what this topic is and why it matters to the user",
70
+ "category": "One of the categories above",
71
+ "reason": "Evidence from the conversation that justified flagging this topic"
87
72
  }
88
73
  ]
89
74
  }
90
75
  \`\`\`
91
76
 
92
- **Return JSON only.**`;
93
-
94
- const system = `${taskFragment}
95
-
96
- ${specificNeedsFragment}
97
-
98
- ${guidelinesFragment}
77
+ **Return JSON only.**
99
78
 
100
- ${doNotCaptureFragment}
79
+ ONLY ANALYZE the "Most Recent Messages". The "Earlier Conversation" is provided for context only — it has already been processed.
101
80
 
102
- ${criticalFragment}`;
81
+ ${participantContextSection(data.participant_context)}`;
103
82
 
104
83
  const earlierSection = data.messages_context.length > 0
105
84
  ? `## Earlier Conversation
@@ -123,9 +102,10 @@ Scan the "Most Recent Messages" for TOPICS of interest to the human user.
123
102
  {
124
103
  "topics": [
125
104
  {
126
- "type_of_topic": "The Topic Type from the list above",
127
- "value_of_topic": "<actual topic from the conversation>",
128
- "reason": "The justification of including this specific topic"
105
+ "name": "Short label for the topic (10-75 characters)",
106
+ "description": "1-2 sentences: what this topic is and why it matters to the user",
107
+ "category": "Interest|Goal|Dream|Conflict|Concern|Fear|Hope|Plan|Project|Event",
108
+ "reason": "Evidence from the conversation that justified flagging this topic"
129
109
  }
130
110
  ]
131
111
  }
@@ -0,0 +1,295 @@
1
+ import type { PromptOutput, ParticipantContext } from "./types.js";
2
+ import type { Topic, Message } from "../../core/types.js";
3
+ import { formatMessagesAsPlaceholders } from "../message-utils.js";
4
+
5
+ function participantContextSection(ctx: ParticipantContext | undefined): string {
6
+ if (!ctx) return "";
7
+ const lines: string[] = ["# Participant Context", "The following may help you understand what themes and moments are meaningful in this conversation.", ""];
8
+ lines.push(`## Persona: ${ctx.persona_name}`);
9
+ if (ctx.persona_description) lines.push(ctx.persona_description);
10
+ lines.push("");
11
+ lines.push("## Human");
12
+ if (ctx.human_name) lines.push(`Name: ${ctx.human_name}`);
13
+ if (ctx.human_age !== undefined) lines.push(`Age: ${ctx.human_age}`);
14
+ lines.push("");
15
+ return lines.join("\n");
16
+ }
17
+
18
+ export interface TopicUpdatePromptData {
19
+ existing_item: Topic | null;
20
+ new_topic_name?: string;
21
+ new_topic_description?: string;
22
+ new_topic_category?: string;
23
+ messages_context: Message[];
24
+ messages_analyze: Message[];
25
+ persona_name: string;
26
+ participant_context?: ParticipantContext;
27
+ }
28
+
29
+ function formatExistingTopic(topic: Topic): string {
30
+ return JSON.stringify({
31
+ name: topic.name,
32
+ description: topic.description,
33
+ sentiment: topic.sentiment,
34
+ category: topic.category,
35
+ exposure_current: topic.exposure_current,
36
+ exposure_desired: topic.exposure_desired,
37
+ }, null, 2);
38
+ }
39
+
40
+ export function buildTopicUpdatePrompt(data: TopicUpdatePromptData): PromptOutput {
41
+ if (!data.persona_name) {
42
+ throw new Error("buildTopicUpdatePrompt: persona_name is required");
43
+ }
44
+
45
+ const personaName = data.persona_name;
46
+ const isEvent =
47
+ data.existing_item?.category === "Event" ||
48
+ data.new_topic_category === "Event";
49
+
50
+ const nameSection = `Should be a short, evocative label for the TOPIC.
51
+
52
+ Only update for clarification or further specificity.
53
+
54
+ Examples: "Unknown" → "Ei Platform Architecture", "Work stress" → "Job transition anxiety"`;
55
+
56
+ const descriptionSection = isEvent
57
+ ? `A narrative account of a specific significant moment — written as a memory, not a summary.
58
+
59
+ ## CRITICAL: Events are MOMENTS, not themes
60
+
61
+ An Event description captures a single bounded experience. It should read like "if you described this to someone who wasn't there."
62
+
63
+ **Good description**: "First session where Beta had read access to the Ei codebase (early March 2026). Spent the conversation exploring the project structure, then diagnosed the JSON recovery edge case. The debugging felt genuinely collaborative — the 'Crash Test Cutie' framing made sense for the first time."
64
+
65
+ **Bad description**: "Beta has filesystem access and regularly uses it to debug the Ei project. Ongoing collaboration continues."
66
+
67
+ The description should:
68
+ - Name the moment specifically (what happened, rough time if known)
69
+ - Capture what made it significant (what changed, what was felt, what it led to)
70
+ - Be specific enough to summon the memory, short enough to not be a recap
71
+ - Read as a story beat, not a state summary
72
+
73
+ The description should NOT:
74
+ - Track an ongoing relationship or theme (that's a regular TOPIC)
75
+ - Accumulate all conversations that touched this moment
76
+ - Read like a system log or changelog
77
+
78
+ **Style**: Write it the way a good friend would tell someone else about a memorable moment. Present tense is fine.`
79
+ : `A concise, evergreen summary of what is currently known about this TOPIC. Personas use this to recall context and make meaningful references.
80
+
81
+ ## CRITICAL: Synthesize, don't accumulate
82
+
83
+ Every update must **rewrite** the description as a current-state summary. Never append to it.
84
+
85
+ **Good description**: "Active project to improve test coverage. Settled on Vitest + E2E harness. Currently focused on pipeline integration and extraction logic coverage."
86
+
87
+ **Bad description**: "User asked Sisyphus to create a ticket... Later: pruned overengineered framework... Most recent session: added PR checks..."
88
+
89
+ The description should:
90
+ - Capture what is true NOW — the current state, decisions made, where things stand
91
+ - Include details a persona would use to show genuine recall ("Oh right, you were working on the pipeline tests")
92
+ - Be useful to a persona meeting this human for the first time
93
+ - Read as a brief summary paragraph, not a session log
94
+
95
+ The description should NOT:
96
+ - Append "Most recent:", "Latest:", "Current session:", or any temporal marker
97
+ - Accumulate a running history of every conversation that touched this TOPIC
98
+ - Exceed 3-4 sentences under any circumstances
99
+
100
+ **ABSOLUTELY VITAL**: Do **NOT** embellish — personas use their own voice. Capture what is true, not a log of how you got here.`;
101
+
102
+ const categorySection = `## Category (\`category\`)
103
+
104
+ The type/category of this TOPIC. Pick the most appropriate:
105
+ - **Interest**: Hobbies, activities, ongoing fascinations
106
+ - **Goal**: Things they want to achieve
107
+ - **Dream**: Aspirational, maybe unrealistic desires
108
+ - **Conflict**: Internal struggles, dilemmas
109
+ - **Concern**: Worries, anxieties about something real
110
+ - **Fear**: Things that scare them
111
+ - **Hope**: Positive expectations for the future
112
+ - **Plan**: Concrete intentions with steps in mind
113
+ - **Project**: Active undertakings with real progress
114
+ - **Event**: A specific, significant moment that either party might reference later ("remember when...")
115
+
116
+ **Event vs. everything else**: An Event is bounded in time — it happened, it meant something, it's now a shared reference point. If you're describing an ongoing relationship or recurring theme, that's not an Event.
117
+
118
+ If the TOPIC is currently categorized as Event, keep it as Event unless you have strong evidence it should change.`;
119
+
120
+ const exposureSection = `## Desired Exposure (\`exposure_desired\`)
121
+
122
+ How much the HUMAN USER wants to talk about this TOPIC.
123
+
124
+ Scale of 0.0 to 1.0:
125
+ - 0.0: Never wants to hear about this TOPIC again
126
+ - 0.5: Average amount of engagement
127
+ - 1.0: This TOPIC is the sole focus of their existence
128
+
129
+ Do not make micro-adjustments. Close enough is OK.
130
+
131
+ ## Exposure Impact (\`exposure_impact\`)
132
+
133
+ Not in the current data — but include it in your response.
134
+
135
+ How much this conversation should count toward exposure tracking:
136
+ - "high": Long, detailed conversation exclusively about this TOPIC
137
+ - "medium": Long OR detailed conversation about this TOPIC
138
+ - "low": The conversation touched on this TOPIC briefly
139
+ - "none": Only alluded to or hinted at`;
140
+
141
+ const currentDetailsSection = data.existing_item
142
+ ? `\`\`\`json
143
+ ${formatExistingTopic(data.existing_item)}
144
+ \`\`\`
145
+
146
+ You are UPDATING an existing TOPIC.`
147
+ : `**NEW TOPIC — NOT YET IN SYSTEM**
148
+
149
+ You are CREATING a new TOPIC from what was discovered:
150
+ \`\`\`json
151
+ {
152
+ "name": "${data.new_topic_name ?? "Unknown"}",
153
+ "description": "${data.new_topic_description ?? "Details unknown"}",
154
+ "category": "${data.new_topic_category ?? "Interest"}"
155
+ }
156
+ \`\`\`
157
+
158
+ Return all fields based on what you find in the conversation.`;
159
+
160
+ const jsonTemplate = `{
161
+ "name": "...",
162
+ "description": "...",
163
+ "sentiment": 0.0,
164
+ "category": "Interest|Goal|Dream|Conflict|Concern|Fear|Hope|Plan|Project|Event",
165
+ "exposure_desired": 0.5,
166
+ "exposure_impact": "high|medium|low|none",
167
+ "quotes": [
168
+ {
169
+ "text": "exact phrase from message",
170
+ "reason": "why this matters"
171
+ }
172
+ ]
173
+ }`;
174
+
175
+ const system = `# Task
176
+
177
+ You are scanning a conversation to deeply understand a TOPIC.
178
+
179
+ Your job is to take that analysis and apply it to the record we already have **IF DOING SO WILL PROVIDE THE HUMAN USER WITH A BETTER EXPERIENCE IN THE FUTURE**.
180
+
181
+ This means detail you add should:
182
+ 1. Be meaningful, accurate, or still true to the HUMAN USER in six months or more
183
+ 2. **NOT** already be present in the description or name of the TOPIC
184
+
185
+ This TOPIC will be recorded in the HUMAN USER's profile for agents and personas to later reference.
186
+
187
+ # Field Definitions
188
+
189
+ ## Name (\`name\`)
190
+ ${nameSection}
191
+
192
+ ## Description (\`description\`)
193
+ ${descriptionSection}
194
+
195
+ ## Sentiment (\`sentiment\`)
196
+
197
+ How strongly the HUMAN USER feels about this TOPIC.
198
+
199
+ Scale of -1.0 to 1.0:
200
+ - -1.0: No TOPIC is more hated
201
+ - -0.5: Disliked, but some redeeming qualities
202
+ - 0: Neutral
203
+ - 0.5: Enjoyed, but recognizes flaws
204
+ - 1.0: The sole focus of their existence
205
+
206
+ Do not make micro-adjustments. Close enough is OK.
207
+
208
+ ${categorySection}
209
+
210
+ ${exposureSection}
211
+
212
+ ## Quotes
213
+
214
+ In addition to updating the TOPIC, identify any **memorable, funny, important, or stand-out phrases** from the Most Recent Messages that relate to this TOPIC.
215
+
216
+ ### What Makes a Quote Worth Preserving
217
+
218
+ **Prioritize:**
219
+ - Humor, wit, colorful language, creative profanity
220
+ - Emotional outbursts (positive or negative) — the raw stuff
221
+ - Phrases that reveal personality or communication style
222
+ - Things you'd quote back to them later to make them laugh
223
+ - Unique expressions, malaphors, or turns of phrase
224
+ - Quotable moments from EITHER speaker — humans AND AI personas both say memorable things
225
+
226
+ **NEVER extract these — they are NOT quotes:**
227
+ - Technical identifiers: ARNs, URLs, file paths, UUIDs, config keys, environment variable values, role/policy names
228
+ - AI agent self-talk: "I notice I'm in Plan Mode", "I'll start by...", "Let me help you with...", status updates about the agent's own process
229
+ - AI apologies or acknowledgments: "You're absolutely right", "I apologize for that overreach"
230
+ - Generic AI instructions or tips, tool usage advice, workflow suggestions
231
+ - Dry technical facts: infrastructure descriptions, process status, batch sizes, system architecture summaries
232
+ - Generic statements that could come from anyone or any AI session
233
+ - Credentials, secrets, connection strings, or anything that looks like an access token
234
+
235
+ **The litmus test**: Would you bring this up at a bar with a friend? Would it make someone laugh, think, or feel something?
236
+ - "Does the Pope shit in his hat?" → YES. Hilarious malaphor.
237
+ - "AWSReservedSSO_cmidp-nihl-sandbox-adm_db7b191e026bdd85" → NO. That's a credential.
238
+ - "Slow is smooth. Smooth is fast." → YES (once). Pithy wisdom.
239
+ - "The authentication flow is working correctly now" → NO. Status update.
240
+
241
+ **When in doubt, leave it out.** An empty quotes array is always acceptable.
242
+
243
+ **CRITICAL**: Return the EXACT text as it appears in the message. **WE CAN ONLY USE IT IF WE FIND IT IN THE TEXT.**
244
+
245
+ # CRITICAL INSTRUCTIONS
246
+
247
+ ONLY ANALYZE the "Most Recent Messages". The "Earlier Conversation" is provided for context only — it has already been processed.
248
+
249
+ \`\`\`json
250
+ ${jsonTemplate}
251
+ \`\`\`
252
+
253
+ When returning a record, **ALWAYS** include \`name\`, \`description\`, and \`sentiment\`.
254
+
255
+ If you find **NO EVIDENCE** of this TOPIC in the "Most Recent Messages", respond with: \`{}\`
256
+
257
+ If **NO CHANGES** are required, respond with: \`{}\`
258
+
259
+ An empty object is the MOST COMMON expected response.
260
+
261
+ # Current Details of TOPIC
262
+
263
+ ${currentDetailsSection}
264
+
265
+ ${participantContextSection(data.participant_context)}`;
266
+
267
+ const earlierSection =
268
+ data.messages_context.length > 0
269
+ ? `## Earlier Conversation
270
+ ${formatMessagesAsPlaceholders(data.messages_context, personaName)}
271
+
272
+ `
273
+ : "";
274
+
275
+ const recentSection = `## Most Recent Messages
276
+ ${formatMessagesAsPlaceholders(data.messages_analyze, personaName)}`;
277
+
278
+ const user = `# Conversation
279
+ ${earlierSection}${recentSection}
280
+
281
+ ---
282
+
283
+ Analyze the Most Recent Messages and update the TOPIC if warranted.
284
+
285
+ **Return JSON:**
286
+ \`\`\`json
287
+ ${jsonTemplate}
288
+ \`\`\`
289
+
290
+ If no changes are needed, respond with: \`{}\``;
291
+
292
+ return { system, user };
293
+ }
294
+
295
+
@@ -1,10 +1,17 @@
1
- import type { Message, DataItemBase, DataItemType } from "../../core/types.js";
1
+ import type { Message } from "../../core/types.js";
2
2
 
3
3
  export interface PromptOutput {
4
4
  system: string;
5
5
  user: string;
6
6
  }
7
7
 
8
+ export interface ParticipantContext {
9
+ persona_name: string;
10
+ persona_description?: string; // long_description, omitted if empty
11
+ human_name?: string; // e.g. "Jeremy (Flare)" — omitted if no facts
12
+ human_age?: number; // calculated from Birthday fact — omitted if not set
13
+ }
14
+
8
15
  interface BaseScanPromptData {
9
16
  messages_context: Message[];
10
17
  messages_analyze: Message[];
@@ -15,40 +22,65 @@ export interface FactScanPromptData extends BaseScanPromptData {}
15
22
 
16
23
  export interface TraitScanPromptData extends BaseScanPromptData {}
17
24
 
18
- export interface TopicScanPromptData extends BaseScanPromptData {}
25
+ export interface TopicScanPromptData extends BaseScanPromptData {
26
+ participant_context?: ParticipantContext;
27
+ }
19
28
 
20
29
  export interface PersonScanPromptData extends BaseScanPromptData {}
21
30
 
31
+ export interface FactFindPromptData {
32
+ persona_name: string;
33
+ missing_fact_names: string[]; // Built-in facts with no description
34
+ messages_context: Message[]; // Earlier conversation (already processed)
35
+ messages_analyze: Message[]; // Recent messages to scan
36
+ }
37
+
22
38
  export interface FactScanCandidate {
23
39
  type_of_fact: string;
24
40
  value_of_fact: string;
25
41
  reason: string;
26
42
  }
27
43
 
28
- export interface TraitScanCandidate {
29
- type_of_trait: string;
30
- value_of_trait: string;
31
- reason: string;
32
- }
33
-
34
44
  export interface TopicScanCandidate {
35
- type_of_topic: string;
36
- value_of_topic: string;
45
+ name: string;
46
+ description: string;
47
+ category: string;
37
48
  reason: string;
38
49
  }
39
50
 
40
51
  export interface PersonScanCandidate {
41
- type_of_person: string;
42
- name_of_person: string;
52
+ name: string;
53
+ description: string;
54
+ relationship: string;
43
55
  reason: string;
44
56
  }
45
57
 
46
- export interface FactScanResult {
47
- facts: FactScanCandidate[];
58
+ export interface TopicMatchPromptData {
59
+ candidate_name: string;
60
+ candidate_description: string;
61
+ candidate_category: string;
62
+ existing_topics: Array<{
63
+ id: string;
64
+ name: string;
65
+ description: string;
66
+ category?: string;
67
+ }>;
48
68
  }
49
69
 
50
- export interface TraitScanResult {
51
- traits: TraitScanCandidate[];
70
+ export interface PersonMatchPromptData {
71
+ candidate_name: string;
72
+ candidate_description: string;
73
+ candidate_relationship: string;
74
+ existing_people: Array<{
75
+ id: string;
76
+ name: string;
77
+ description: string;
78
+ relationship?: string;
79
+ }>;
80
+ }
81
+
82
+ export interface FactScanResult {
83
+ facts: FactScanCandidate[];
52
84
  }
53
85
 
54
86
  export interface TopicScanResult {
@@ -59,15 +91,11 @@ export interface PersonScanResult {
59
91
  people: PersonScanCandidate[];
60
92
  }
61
93
 
62
- export interface ItemMatchPromptData {
63
- candidate_type: DataItemType;
64
- candidate_name: string;
65
- candidate_value: string;
66
- all_items: Array<{
67
- data_type: DataItemType;
68
- data_id: string;
69
- data_name: string;
70
- data_description: string;
94
+ export interface FactFindResult {
95
+ facts: Array<{
96
+ name: string; // Must match a name from missing_fact_names
97
+ value: string; // The extracted value
98
+ evidence: string; // Direct quote/reference (NOT stored, limits hallucination)
71
99
  }>;
72
100
  }
73
101
 
@@ -75,16 +103,6 @@ export interface ItemMatchResult {
75
103
  matched_guid: string | null;
76
104
  }
77
105
 
78
- export interface ItemUpdatePromptData {
79
- data_type: DataItemType;
80
- existing_item: DataItemBase | null;
81
- messages_context: Message[];
82
- messages_analyze: Message[];
83
- persona_name: string;
84
- new_item_name?: string;
85
- new_item_value?: string;
86
- }
87
-
88
106
  export type ExposureImpact = "high" | "medium" | "low" | "none";
89
107
 
90
108
  export interface QuoteCandidate {
@@ -101,10 +119,6 @@ export interface ItemUpdateResultBase {
101
119
 
102
120
  export interface FactUpdateResult extends ItemUpdateResultBase {}
103
121
 
104
- export interface TraitUpdateResult extends ItemUpdateResultBase {
105
- strength?: number;
106
- }
107
-
108
122
  export interface TopicUpdateResult extends ItemUpdateResultBase {
109
123
  category?: string;
110
124
  exposure_desired?: number;
@@ -119,7 +133,16 @@ export interface PersonUpdateResult extends ItemUpdateResultBase {
119
133
 
120
134
  export type ItemUpdateResult =
121
135
  | FactUpdateResult
122
- | TraitUpdateResult
123
136
  | TopicUpdateResult
124
137
  | PersonUpdateResult
125
138
  | Record<string, never>;
139
+
140
+ export interface EventScanCandidate {
141
+ name: string;
142
+ description: string;
143
+ reason: string;
144
+ }
145
+
146
+ export interface EventScanResult {
147
+ events: EventScanCandidate[];
148
+ }
@@ -46,28 +46,24 @@ export type {
46
46
 
47
47
  export {
48
48
  buildHumanFactScanPrompt,
49
- buildHumanTraitScanPrompt,
50
49
  buildHumanTopicScanPrompt,
51
50
  buildHumanPersonScanPrompt,
52
- buildHumanItemMatchPrompt,
53
- buildHumanItemUpdatePrompt,
51
+ buildEventScanPrompt,
54
52
  } from "./human/index.js";
53
+ export type { EventScanPromptData } from "./human/event-scan.js";
55
54
  export type {
56
55
  FactScanPromptData,
57
- TraitScanPromptData,
58
56
  TopicScanPromptData,
59
57
  PersonScanPromptData,
60
58
  FactScanCandidate,
61
- TraitScanCandidate,
62
59
  TopicScanCandidate,
63
60
  PersonScanCandidate,
61
+ EventScanCandidate,
62
+ EventScanResult,
64
63
  FactScanResult,
65
- TraitScanResult,
66
64
  TopicScanResult,
67
65
  PersonScanResult,
68
- ItemMatchPromptData,
69
66
  ItemMatchResult,
70
- ItemUpdatePromptData,
71
67
  ExposureImpact,
72
68
  ItemUpdateResult,
73
69
  } from "./human/types.js";
@@ -1,8 +1,8 @@
1
1
  import type { PersonaTopicUpdatePromptData, PromptOutput } from "./types.js";
2
- import type { PersonaTopic, Trait } from "../../core/types.js";
2
+ import type { PersonaTopic, PersonaTrait } from "../../core/types.js";
3
3
  import { formatMessagesAsPlaceholders } from "../message-utils.js";
4
4
 
5
- function formatTraitsForPrompt(traits: Trait[]): string {
5
+ function formatTraitsForPrompt(traits: PersonaTrait[]): string {
6
6
  if (traits.length === 0) return "(No traits defined)";
7
7
  return traits.map(t => `- ${t.name}: ${t.description}`).join('\n');
8
8
  }