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.
- package/README.md +42 -0
- package/package.json +1 -1
- package/src/README.md +4 -11
- package/src/cli/README.md +4 -5
- package/src/cli/retrieval.ts +3 -25
- package/src/cli.ts +3 -7
- package/src/core/AGENTS.md +1 -1
- package/src/core/constants/built-in-facts.ts +49 -0
- package/src/core/constants/index.ts +1 -0
- package/src/core/context-utils.ts +0 -1
- package/src/core/embedding-service.ts +8 -0
- package/src/core/handlers/dedup.ts +10 -16
- package/src/core/handlers/heartbeat.ts +2 -3
- package/src/core/handlers/human-extraction.ts +95 -30
- package/src/core/handlers/human-matching.ts +326 -248
- package/src/core/handlers/index.ts +8 -6
- package/src/core/handlers/persona-generation.ts +8 -8
- package/src/core/handlers/rewrite.ts +4 -29
- package/src/core/handlers/utils.ts +23 -1
- package/src/core/heartbeat-manager.ts +2 -4
- package/src/core/human-data-manager.ts +5 -27
- package/src/core/message-manager.ts +10 -10
- package/src/core/orchestrators/ceremony.ts +50 -39
- package/src/core/orchestrators/dedup-phase.ts +0 -1
- package/src/core/orchestrators/human-extraction.ts +351 -207
- package/src/core/orchestrators/index.ts +6 -4
- package/src/core/orchestrators/persona-generation.ts +3 -3
- package/src/core/processor.ts +99 -17
- package/src/core/prompt-context-builder.ts +4 -6
- package/src/core/state/human.ts +1 -26
- package/src/core/state/personas.ts +2 -2
- package/src/core/state-manager.ts +107 -14
- package/src/core/tools/builtin/read-memory.ts +7 -8
- package/src/core/types/data-items.ts +2 -4
- package/src/core/types/entities.ts +6 -4
- package/src/core/types/enums.ts +6 -9
- package/src/core/types/llm.ts +2 -2
- package/src/core/utils/crossFind.ts +2 -5
- package/src/core/utils/event-windows.ts +31 -0
- package/src/integrations/claude-code/importer.ts +8 -4
- package/src/integrations/claude-code/types.ts +2 -0
- package/src/integrations/opencode/importer.ts +7 -3
- package/src/prompts/AGENTS.md +73 -1
- package/src/prompts/ceremony/rewrite.ts +3 -22
- package/src/prompts/ceremony/types.ts +3 -3
- package/src/prompts/generation/descriptions.ts +2 -2
- package/src/prompts/generation/types.ts +2 -2
- package/src/prompts/heartbeat/types.ts +2 -2
- package/src/prompts/human/event-scan.ts +122 -0
- package/src/prompts/human/fact-find.ts +106 -0
- package/src/prompts/human/fact-scan.ts +0 -2
- package/src/prompts/human/index.ts +17 -10
- package/src/prompts/human/person-match.ts +65 -0
- package/src/prompts/human/person-scan.ts +52 -59
- package/src/prompts/human/person-update.ts +241 -0
- package/src/prompts/human/topic-match.ts +65 -0
- package/src/prompts/human/topic-scan.ts +51 -71
- package/src/prompts/human/topic-update.ts +295 -0
- package/src/prompts/human/types.ts +63 -40
- package/src/prompts/index.ts +4 -8
- package/src/prompts/persona/topics-update.ts +2 -2
- package/src/prompts/persona/traits.ts +2 -2
- package/src/prompts/persona/types.ts +3 -3
- package/src/prompts/response/index.ts +1 -1
- package/src/prompts/response/sections.ts +9 -12
- package/src/prompts/response/types.ts +2 -3
- package/src/storage/embeddings.ts +1 -1
- package/src/storage/index.ts +1 -0
- package/src/storage/indexed.ts +174 -0
- package/src/storage/merge.ts +67 -2
- package/tui/src/commands/me.tsx +5 -14
- package/tui/src/commands/settings.tsx +15 -0
- package/tui/src/context/ei.tsx +5 -14
- package/tui/src/util/yaml-serializers.ts +48 -33
- package/src/cli/commands/traits.ts +0 -25
- package/src/prompts/human/item-match.ts +0 -74
- package/src/prompts/human/item-update.ts +0 -364
- 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
|
|
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
|
-
|
|
26
|
+
You are scanning a conversation to quickly identify TOPICS of interest to the HUMAN USER.
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
Detect and flag. Do NOT analyze deeply — that happens later.
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
## What to Capture
|
|
32
31
|
|
|
33
|
-
|
|
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
|
-
|
|
34
|
+
Be **conservative**: only flag topics that are genuinely relevant to the human user long-term. Noise is worse than gaps.
|
|
46
35
|
|
|
47
|
-
|
|
36
|
+
## What a TOPIC Is
|
|
48
37
|
|
|
49
|
-
|
|
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
|
-
|
|
56
|
-
|
|
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
|
-
##
|
|
59
|
-
* Personality Patterns, Communication style, Behavioral tendencies
|
|
60
|
-
* Cognitive Style, Emotional Traits, Work Ethic, Social Orientation
|
|
45
|
+
## Category
|
|
61
46
|
|
|
62
|
-
|
|
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
|
-
|
|
66
|
-
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
79
|
+
ONLY ANALYZE the "Most Recent Messages". The "Earlier Conversation" is provided for context only — it has already been processed.
|
|
101
80
|
|
|
102
|
-
${
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
|
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
|
-
|
|
36
|
-
|
|
45
|
+
name: string;
|
|
46
|
+
description: string;
|
|
47
|
+
category: string;
|
|
37
48
|
reason: string;
|
|
38
49
|
}
|
|
39
50
|
|
|
40
51
|
export interface PersonScanCandidate {
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
name: string;
|
|
53
|
+
description: string;
|
|
54
|
+
relationship: string;
|
|
43
55
|
reason: string;
|
|
44
56
|
}
|
|
45
57
|
|
|
46
|
-
export interface
|
|
47
|
-
|
|
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
|
|
51
|
-
|
|
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
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
+
}
|
package/src/prompts/index.ts
CHANGED
|
@@ -46,28 +46,24 @@ export type {
|
|
|
46
46
|
|
|
47
47
|
export {
|
|
48
48
|
buildHumanFactScanPrompt,
|
|
49
|
-
buildHumanTraitScanPrompt,
|
|
50
49
|
buildHumanTopicScanPrompt,
|
|
51
50
|
buildHumanPersonScanPrompt,
|
|
52
|
-
|
|
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,
|
|
2
|
+
import type { PersonaTopic, PersonaTrait } from "../../core/types.js";
|
|
3
3
|
import { formatMessagesAsPlaceholders } from "../message-utils.js";
|
|
4
4
|
|
|
5
|
-
function formatTraitsForPrompt(traits:
|
|
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
|
}
|