ei-tui 0.1.25 → 0.3.1
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 +2 -1
- package/src/README.md +4 -11
- package/src/cli/README.md +87 -7
- package/src/cli/commands/facts.ts +2 -2
- package/src/cli/commands/people.ts +2 -2
- package/src/cli/commands/quotes.ts +2 -2
- package/src/cli/commands/topics.ts +2 -2
- package/src/cli/mcp.ts +94 -0
- package/src/cli/retrieval.ts +67 -31
- package/src/cli.ts +64 -23
- 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 +11 -23
- package/src/core/handlers/heartbeat.ts +2 -3
- package/src/core/handlers/human-extraction.ts +96 -30
- package/src/core/handlers/human-matching.ts +328 -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 -51
- package/src/core/handlers/utils.ts +23 -1
- package/src/core/heartbeat-manager.ts +2 -4
- package/src/core/human-data-manager.ts +38 -36
- package/src/core/message-manager.ts +10 -10
- package/src/core/orchestrators/ceremony.ts +49 -44
- package/src/core/orchestrators/dedup-phase.ts +2 -4
- 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 +167 -20
- 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 +13 -18
- package/src/core/types/data-items.ts +3 -4
- package/src/core/types/entities.ts +7 -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 +14 -5
- package/src/integrations/claude-code/types.ts +3 -0
- package/src/integrations/cursor/importer.ts +282 -0
- package/src/integrations/cursor/index.ts +10 -0
- package/src/integrations/cursor/reader.ts +209 -0
- package/src/integrations/cursor/types.ts +140 -0
- package/src/integrations/opencode/importer.ts +14 -4
- package/src/prompts/AGENTS.md +73 -1
- package/src/prompts/ceremony/dedup.ts +0 -33
- package/src/prompts/ceremony/rewrite.ts +6 -41
- package/src/prompts/ceremony/types.ts +4 -4
- 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 +76 -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
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import type { PromptOutput } from "./types.js";
|
|
2
|
+
import type { Person, Message } from "../../core/types.js";
|
|
3
|
+
import { formatMessagesAsPlaceholders } from "../message-utils.js";
|
|
4
|
+
|
|
5
|
+
export interface PersonUpdatePromptData {
|
|
6
|
+
existing_item: Person | null;
|
|
7
|
+
new_person_name?: string;
|
|
8
|
+
new_person_description?: string;
|
|
9
|
+
new_person_relationship?: string;
|
|
10
|
+
messages_context: Message[];
|
|
11
|
+
messages_analyze: Message[];
|
|
12
|
+
persona_name: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function formatExistingPerson(person: Person): string {
|
|
16
|
+
return JSON.stringify({
|
|
17
|
+
name: person.name,
|
|
18
|
+
description: person.description,
|
|
19
|
+
sentiment: person.sentiment,
|
|
20
|
+
relationship: person.relationship,
|
|
21
|
+
exposure_current: person.exposure_current,
|
|
22
|
+
exposure_desired: person.exposure_desired,
|
|
23
|
+
}, null, 2);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function buildPersonUpdatePrompt(data: PersonUpdatePromptData): PromptOutput {
|
|
27
|
+
if (!data.persona_name) {
|
|
28
|
+
throw new Error("buildPersonUpdatePrompt: persona_name is required");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const personaName = data.persona_name;
|
|
32
|
+
|
|
33
|
+
const nameSection = `The person's actual name, or the clearest available identifier.
|
|
34
|
+
|
|
35
|
+
Only update when you learn something more specific.
|
|
36
|
+
|
|
37
|
+
Examples: "Unknown woman" → "Carol", "Mom" → "Carol (Mom)", "David" → "David Kim"`;
|
|
38
|
+
|
|
39
|
+
const descriptionSection = `A concise summary of who this person is and how they relate to the HUMAN USER. Personas use this to recognize this person and engage meaningfully when they come up.
|
|
40
|
+
|
|
41
|
+
## CRITICAL: Synthesize, don't accumulate
|
|
42
|
+
|
|
43
|
+
Every update must **rewrite** the description as a current-state summary. Never append to it.
|
|
44
|
+
|
|
45
|
+
**Good description**: "Borfinda, partner of 12 years. Former marine biologist, now stay-at-home parent. Tends to ground the user when they spiral; dry sense of humor. Two kids together."
|
|
46
|
+
|
|
47
|
+
**Bad description**: "Borfinda was mentioned when the user talked about moving. In a later conversation she came up again during the work stress discussion. Most recently the user said she was supportive."
|
|
48
|
+
|
|
49
|
+
The description should:
|
|
50
|
+
- Capture who this person IS — their role, characteristics, relationship texture
|
|
51
|
+
- Include what the HUMAN USER has revealed about them over time
|
|
52
|
+
- Be useful to a persona who's never heard this person's name before
|
|
53
|
+
- Read as a brief, confident summary — not a log of when they were mentioned
|
|
54
|
+
|
|
55
|
+
The description should NOT:
|
|
56
|
+
- Append "Most recently:", "Latest mention:", or any temporal marker
|
|
57
|
+
- Accumulate a session-by-session history of every time this person came up
|
|
58
|
+
- Speculate about the person based on thin evidence
|
|
59
|
+
- Exceed 3-4 sentences under any circumstances
|
|
60
|
+
|
|
61
|
+
**ABSOLUTELY VITAL**: Do **NOT** embellish — personas use their own voice. Record what the user actually said or demonstrated, not your interpretation of its emotional significance.`;
|
|
62
|
+
|
|
63
|
+
const relationshipSection = `## Relationship (\`relationship\`)
|
|
64
|
+
|
|
65
|
+
How the HUMAN USER is currently related to this PERSON.
|
|
66
|
+
|
|
67
|
+
Once known, this field changes infrequently — a "Father" may later be clarified to "Step-Father", but is unlikely to become "Uncle".
|
|
68
|
+
|
|
69
|
+
Keep it concise and specific. Avoid vague labels.
|
|
70
|
+
|
|
71
|
+
Examples: "Unknown" → "Coworker", "Mother" → "Step-Mother", "Fiance" → "Spouse", "AI Persona" → "AI Companion"`;
|
|
72
|
+
|
|
73
|
+
const exposureSection = `## Desired Exposure (\`exposure_desired\`)
|
|
74
|
+
|
|
75
|
+
How much the HUMAN USER wants to talk about this PERSON.
|
|
76
|
+
|
|
77
|
+
Scale of 0.0 to 1.0:
|
|
78
|
+
- 0.0: Never wants to hear about this PERSON again
|
|
79
|
+
- 0.5: Average amount of engagement
|
|
80
|
+
- 1.0: This PERSON is the sole focus of their existence
|
|
81
|
+
|
|
82
|
+
Do not make micro-adjustments. Close enough is OK.
|
|
83
|
+
|
|
84
|
+
## Exposure Impact (\`exposure_impact\`)
|
|
85
|
+
|
|
86
|
+
Not in the current data — but include it in your response.
|
|
87
|
+
|
|
88
|
+
How much this conversation should count toward exposure tracking:
|
|
89
|
+
- "high": Long, detailed conversation exclusively about this PERSON
|
|
90
|
+
- "medium": Long OR detailed conversation about this PERSON
|
|
91
|
+
- "low": The conversation touched on this PERSON briefly
|
|
92
|
+
- "none": Only alluded to or hinted at`;
|
|
93
|
+
|
|
94
|
+
const currentDetailsSection = data.existing_item
|
|
95
|
+
? `\`\`\`json
|
|
96
|
+
${formatExistingPerson(data.existing_item)}
|
|
97
|
+
\`\`\`
|
|
98
|
+
|
|
99
|
+
You are UPDATING an existing PERSON.`
|
|
100
|
+
: `**NEW PERSON — NOT YET IN SYSTEM**
|
|
101
|
+
|
|
102
|
+
You are CREATING a new PERSON from what was discovered:
|
|
103
|
+
\`\`\`json
|
|
104
|
+
{
|
|
105
|
+
"name": "${data.new_person_name ?? "Unknown"}",
|
|
106
|
+
"description": "${data.new_person_description ?? "Details unknown"}",
|
|
107
|
+
"relationship": "${data.new_person_relationship ?? "Unknown"}"
|
|
108
|
+
}
|
|
109
|
+
\`\`\`
|
|
110
|
+
|
|
111
|
+
Return all fields based on what you find in the conversation.`;
|
|
112
|
+
|
|
113
|
+
const jsonTemplate = `{
|
|
114
|
+
"name": "...",
|
|
115
|
+
"description": "...",
|
|
116
|
+
"sentiment": 0.0,
|
|
117
|
+
"relationship": "Mother|Friend|Coworker|AI Companion|etc.",
|
|
118
|
+
"exposure_desired": 0.5,
|
|
119
|
+
"exposure_impact": "high|medium|low|none",
|
|
120
|
+
"quotes": [
|
|
121
|
+
{
|
|
122
|
+
"text": "exact phrase from message",
|
|
123
|
+
"reason": "why this matters"
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
}`;
|
|
127
|
+
|
|
128
|
+
const system = `# Task
|
|
129
|
+
|
|
130
|
+
You are scanning a conversation to deeply understand a PERSON in the HUMAN USER's life.
|
|
131
|
+
|
|
132
|
+
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**.
|
|
133
|
+
|
|
134
|
+
This means detail you add should:
|
|
135
|
+
1. Be meaningful, accurate, or still true to the HUMAN USER in six months or more
|
|
136
|
+
2. **NOT** already be present in the description or name of the PERSON
|
|
137
|
+
|
|
138
|
+
This PERSON will be recorded in the HUMAN USER's profile for agents and personas to later reference.
|
|
139
|
+
|
|
140
|
+
# Field Definitions
|
|
141
|
+
|
|
142
|
+
## Name (\`name\`)
|
|
143
|
+
${nameSection}
|
|
144
|
+
|
|
145
|
+
## Description (\`description\`)
|
|
146
|
+
${descriptionSection}
|
|
147
|
+
|
|
148
|
+
## Sentiment (\`sentiment\`)
|
|
149
|
+
|
|
150
|
+
How the HUMAN USER feels about this PERSON overall.
|
|
151
|
+
|
|
152
|
+
Scale of -1.0 to 1.0:
|
|
153
|
+
- -1.0: No PERSON is more despised
|
|
154
|
+
- -0.5: Disliked or complicated relationship, but not without value
|
|
155
|
+
- 0: Neutral or unknown
|
|
156
|
+
- 0.5: Liked and valued
|
|
157
|
+
- 1.0: The most important person in their life
|
|
158
|
+
|
|
159
|
+
Do not make micro-adjustments. Close enough is OK.
|
|
160
|
+
|
|
161
|
+
${relationshipSection}
|
|
162
|
+
|
|
163
|
+
${exposureSection}
|
|
164
|
+
|
|
165
|
+
## Quotes
|
|
166
|
+
|
|
167
|
+
In addition to updating the PERSON, identify any **memorable, funny, important, or stand-out phrases** from the Most Recent Messages that relate to this PERSON.
|
|
168
|
+
|
|
169
|
+
### What Makes a Quote Worth Preserving
|
|
170
|
+
|
|
171
|
+
**Prioritize:**
|
|
172
|
+
- Humor, wit, colorful language, creative profanity
|
|
173
|
+
- Emotional outbursts (positive or negative) — the raw stuff
|
|
174
|
+
- Phrases that reveal how the HUMAN USER feels about this PERSON
|
|
175
|
+
- Things you'd quote back to them later to make them laugh or think
|
|
176
|
+
- Unique expressions or turns of phrase about or from this PERSON
|
|
177
|
+
- Quotable moments from EITHER speaker — humans AND AI personas both say memorable things
|
|
178
|
+
|
|
179
|
+
**NEVER extract these — they are NOT quotes:**
|
|
180
|
+
- Technical identifiers: ARNs, URLs, file paths, UUIDs, config keys
|
|
181
|
+
- AI agent self-talk: "I notice I'm in Plan Mode", "I'll start by...", status updates
|
|
182
|
+
- AI apologies or acknowledgments: "You're absolutely right", "I apologize for that"
|
|
183
|
+
- Generic statements that could apply to anyone
|
|
184
|
+
- Credentials, secrets, connection strings, or anything that looks like an access token
|
|
185
|
+
|
|
186
|
+
**The litmus test**: Would you bring this up at a bar with a friend? Would it make someone laugh, think, or feel something?
|
|
187
|
+
- "She's the only person who can make me feel simultaneously stupid and brilliant" → YES.
|
|
188
|
+
- "Borfinda was mentioned in the context of the Minnesota discussion" → NO. That's a note, not a quote.
|
|
189
|
+
|
|
190
|
+
**When in doubt, leave it out.** An empty quotes array is always acceptable.
|
|
191
|
+
|
|
192
|
+
**CRITICAL**: Return the EXACT text as it appears in the message. **WE CAN ONLY USE IT IF WE FIND IT IN THE TEXT.**
|
|
193
|
+
|
|
194
|
+
# CRITICAL INSTRUCTIONS
|
|
195
|
+
|
|
196
|
+
ONLY ANALYZE the "Most Recent Messages". The "Earlier Conversation" is provided for context only — it has already been processed.
|
|
197
|
+
|
|
198
|
+
\`\`\`json
|
|
199
|
+
${jsonTemplate}
|
|
200
|
+
\`\`\`
|
|
201
|
+
|
|
202
|
+
When returning a record, **ALWAYS** include \`name\`, \`description\`, and \`sentiment\`.
|
|
203
|
+
|
|
204
|
+
If you find **NO EVIDENCE** of this PERSON in the "Most Recent Messages", respond with: \`{}\`
|
|
205
|
+
|
|
206
|
+
If **NO CHANGES** are required, respond with: \`{}\`
|
|
207
|
+
|
|
208
|
+
An empty object is the MOST COMMON expected response.
|
|
209
|
+
|
|
210
|
+
# Current Details of PERSON
|
|
211
|
+
|
|
212
|
+
${currentDetailsSection}
|
|
213
|
+
`;
|
|
214
|
+
|
|
215
|
+
const earlierSection =
|
|
216
|
+
data.messages_context.length > 0
|
|
217
|
+
? `## Earlier Conversation
|
|
218
|
+
${formatMessagesAsPlaceholders(data.messages_context, personaName)}
|
|
219
|
+
|
|
220
|
+
`
|
|
221
|
+
: "";
|
|
222
|
+
|
|
223
|
+
const recentSection = `## Most Recent Messages
|
|
224
|
+
${formatMessagesAsPlaceholders(data.messages_analyze, personaName)}`;
|
|
225
|
+
|
|
226
|
+
const user = `# Conversation
|
|
227
|
+
${earlierSection}${recentSection}
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
Analyze the Most Recent Messages and update the PERSON if warranted.
|
|
232
|
+
|
|
233
|
+
**Return JSON:**
|
|
234
|
+
\`\`\`json
|
|
235
|
+
${jsonTemplate}
|
|
236
|
+
\`\`\`
|
|
237
|
+
|
|
238
|
+
If no changes are needed, respond with: \`{}\``;
|
|
239
|
+
|
|
240
|
+
return { system, user };
|
|
241
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { PromptOutput } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export interface TopicMatchPromptData {
|
|
4
|
+
candidate_name: string;
|
|
5
|
+
candidate_description: string;
|
|
6
|
+
candidate_category: string;
|
|
7
|
+
existing_topics: Array<{
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
category?: string;
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function buildTopicMatchPrompt(data: TopicMatchPromptData): PromptOutput {
|
|
16
|
+
if (!data.candidate_name) {
|
|
17
|
+
throw new Error("buildTopicMatchPrompt: candidate_name is required");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const system = `# Task
|
|
21
|
+
|
|
22
|
+
You are checking if a TOPIC already exists in our database.
|
|
23
|
+
|
|
24
|
+
## Matching Rules
|
|
25
|
+
|
|
26
|
+
1. **Exact match**: Same name or concept → return its ID
|
|
27
|
+
2. **Similar match**: Clearly the same topic with different wording → return its ID
|
|
28
|
+
3. **No match**: Genuinely new information → return "new"
|
|
29
|
+
|
|
30
|
+
Be conservative. If you're unsure, return "new" — a duplicate is worse than a gap.
|
|
31
|
+
|
|
32
|
+
# Existing Topics
|
|
33
|
+
|
|
34
|
+
\`\`\`json
|
|
35
|
+
${JSON.stringify(data.existing_topics, null, 2)}
|
|
36
|
+
\`\`\`
|
|
37
|
+
|
|
38
|
+
# Response Format
|
|
39
|
+
|
|
40
|
+
Return ONLY the ID of the matching entry, or "new".
|
|
41
|
+
|
|
42
|
+
\`\`\`json
|
|
43
|
+
{
|
|
44
|
+
"matched_guid": "uuid-of-matching-entry" | "new"
|
|
45
|
+
}
|
|
46
|
+
\`\`\`
|
|
47
|
+
|
|
48
|
+
**Return JSON only.**`;
|
|
49
|
+
|
|
50
|
+
const user = `# Candidate Topic
|
|
51
|
+
|
|
52
|
+
Name: ${data.candidate_name}
|
|
53
|
+
Description: ${data.candidate_description}
|
|
54
|
+
Category: ${data.candidate_category}
|
|
55
|
+
|
|
56
|
+
Find the best match in existing topics, or return "new" if this is genuinely new.
|
|
57
|
+
|
|
58
|
+
\`\`\`json
|
|
59
|
+
{
|
|
60
|
+
"matched_guid": "..." | "new"
|
|
61
|
+
}
|
|
62
|
+
\`\`\``;
|
|
63
|
+
|
|
64
|
+
return { system, user };
|
|
65
|
+
}
|
|
@@ -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
|
}
|