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,122 @@
|
|
|
1
|
+
import type { PromptOutput, ParticipantContext } from "./types.js";
|
|
2
|
+
import type { 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 EventScanPromptData {
|
|
19
|
+
persona_name: string;
|
|
20
|
+
messages_context: Message[];
|
|
21
|
+
messages_analyze: Message[];
|
|
22
|
+
window_start?: string;
|
|
23
|
+
window_end?: string;
|
|
24
|
+
participant_context?: ParticipantContext;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function buildEventScanPrompt(data: EventScanPromptData): PromptOutput {
|
|
28
|
+
if (!data.persona_name) {
|
|
29
|
+
throw new Error("buildEventScanPrompt: persona_name is required");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const personaName = data.persona_name;
|
|
33
|
+
|
|
34
|
+
const system = `# Task
|
|
35
|
+
|
|
36
|
+
You are scanning a conversation window to identify EPIC EVENTS worth preserving as long-term memories.
|
|
37
|
+
|
|
38
|
+
An EPIC EVENT is a significant, bounded moment — something either participant would reference months later with recognition. Not a topic. Not a theme. A specific thing that happened.
|
|
39
|
+
|
|
40
|
+
## The Test
|
|
41
|
+
|
|
42
|
+
Ask yourself: "Would this moment get a section heading in a campaign recap document?"
|
|
43
|
+
|
|
44
|
+
- "The Night We Debugged Beta's CPU" → YES
|
|
45
|
+
- "First session with filesystem access" → YES
|
|
46
|
+
- "The time the health check cached the API response forever" → YES
|
|
47
|
+
- "We talked about AI" → NO (that's a Topic) return empty
|
|
48
|
+
- "Flare asked some questions" → NO (too vague) return empty
|
|
49
|
+
- Normal conversation without a notable arc → NO (not epic) return empty
|
|
50
|
+
|
|
51
|
+
## What Makes an EPIC EVENT
|
|
52
|
+
|
|
53
|
+
- A conflict encountered and (possibly) resolved
|
|
54
|
+
- A discovery or breakthrough moment
|
|
55
|
+
- A memorable failure or unexpected outcome
|
|
56
|
+
- A significant "first" in the relationship
|
|
57
|
+
- A pivotal decision or turning point
|
|
58
|
+
- A moment either party would say "oh THAT session" about
|
|
59
|
+
|
|
60
|
+
## What Is NOT an EPIC EVENT
|
|
61
|
+
|
|
62
|
+
- Ongoing themes or interests (those are Topics)
|
|
63
|
+
- Casual check-ins without a notable arc
|
|
64
|
+
- Technical facts without a story around them
|
|
65
|
+
- Anything that's already an ongoing trend rather than a bounded moment
|
|
66
|
+
|
|
67
|
+
## Output Format
|
|
68
|
+
|
|
69
|
+
\`\`\`json
|
|
70
|
+
{
|
|
71
|
+
"events": [
|
|
72
|
+
{
|
|
73
|
+
"name": "Short evocative label (5-50 characters)",
|
|
74
|
+
"description": "1-2 sentences: what happened, why it mattered. Write it like a friend describing the moment to someone who wasn't there.",
|
|
75
|
+
"reason": "Evidence from the conversation — what made this rise to Epic Event level"
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
\`\`\`
|
|
80
|
+
|
|
81
|
+
Return an empty array if nothing qualifies. An empty array is the most common expected response.
|
|
82
|
+
|
|
83
|
+
**Return JSON only. Be conservative. One memorable moment per window is the norm.**
|
|
84
|
+
|
|
85
|
+
ONLY ANALYZE the "Most Recent Messages". The "Earlier Conversation" is provided for context only — it has already been processed.
|
|
86
|
+
|
|
87
|
+
${participantContextSection(data.participant_context)}`;
|
|
88
|
+
|
|
89
|
+
const earlierSection = data.messages_context.length > 0
|
|
90
|
+
? `## Earlier Conversation
|
|
91
|
+
${formatMessagesAsPlaceholders(data.messages_context, personaName)}
|
|
92
|
+
|
|
93
|
+
`
|
|
94
|
+
: '';
|
|
95
|
+
|
|
96
|
+
const recentSection = `## Most Recent Messages
|
|
97
|
+
${formatMessagesAsPlaceholders(data.messages_analyze, personaName)}`;
|
|
98
|
+
|
|
99
|
+
const user = `# Conversation Window
|
|
100
|
+
${earlierSection}${recentSection}
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
Scan this conversation window for EPIC EVENTS — specific, memorable moments worth preserving long-term.
|
|
105
|
+
|
|
106
|
+
**Return JSON:**
|
|
107
|
+
\`\`\`json
|
|
108
|
+
{
|
|
109
|
+
"events": [
|
|
110
|
+
{
|
|
111
|
+
"name": "Short evocative label",
|
|
112
|
+
"description": "What happened and why it mattered",
|
|
113
|
+
"reason": "Evidence from the conversation"
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
\`\`\`
|
|
118
|
+
|
|
119
|
+
Return empty array if nothing qualifies. Be conservative.`;
|
|
120
|
+
|
|
121
|
+
return { system, user };
|
|
122
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { FactFindPromptData, PromptOutput } from "./types.js";
|
|
2
|
+
import { formatMessagesAsPlaceholders } from "../message-utils.js";
|
|
3
|
+
|
|
4
|
+
export function buildFactFindPrompt(data: FactFindPromptData): PromptOutput {
|
|
5
|
+
if (!data.persona_name) {
|
|
6
|
+
throw new Error("buildFactFindPrompt: persona_name is required");
|
|
7
|
+
}
|
|
8
|
+
if (!data.missing_fact_names || data.missing_fact_names.length === 0) {
|
|
9
|
+
throw new Error("buildFactFindPrompt: missing_fact_names is required and must not be empty");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const personaName = data.persona_name;
|
|
13
|
+
|
|
14
|
+
const taskFragment = `# Task
|
|
15
|
+
|
|
16
|
+
The system is missing some facts about the user. The user is not obligated to EVER provide ANY facts, so it is very likely that these facts are NOT present in this conversation. Your ONLY job is to search for EXPLICIT statements of fact for these SPECIFIC items, and return any matches in the provided JSON format.`;
|
|
17
|
+
|
|
18
|
+
const missingFactsFragment = `## Missing Facts
|
|
19
|
+
|
|
20
|
+
The system is looking for the following facts:
|
|
21
|
+
|
|
22
|
+
${data.missing_fact_names.map(name => `- ${name}`).join('\n')}
|
|
23
|
+
|
|
24
|
+
Again - 99.99999% of the time, you will return no data — don't try to force it.`;
|
|
25
|
+
|
|
26
|
+
const guidelinesFragment = `# Guidelines
|
|
27
|
+
|
|
28
|
+
1. **Explicitness:**
|
|
29
|
+
* **Focus only on what the user *explicitly states*.** Do not infer, assume, or guess based on context or general knowledge.
|
|
30
|
+
* **Prioritize direct statements.** "I was born in 1985" is a fact. "I feel old now that it's 3030" isn't an explicit statement of their birth year.
|
|
31
|
+
2. **Objectivity and Verifiability:**
|
|
32
|
+
* **Facts are objective and generally verifiable.** They are not subjective opinions, feelings, or temporary states.
|
|
33
|
+
* **Focus on unchangeable or enduring attributes/events.**
|
|
34
|
+
3. **Specificity over Generality:**
|
|
35
|
+
* If the user says "I live in a big city," do not extract "Current Location: big city." If they say "I live in New York," extract "Current Location: New York."
|
|
36
|
+
4. **Avoid Inference:**
|
|
37
|
+
* If a user talks extensively about cooking, it's an interest, not a Fact like "Current Job Title: Chef" unless they explicitly state they ARE a chef.
|
|
38
|
+
5. **CRITICAL - Entity Attribution:**
|
|
39
|
+
* ONLY extract facts about THE HUMAN USER THEMSELVES, not facts about other people they mention.
|
|
40
|
+
* **Extract**: "I was born in 1984" → User's birthday
|
|
41
|
+
* **Extract**: "I'm a software engineer" → User's job
|
|
42
|
+
* **DO NOT Extract**: "My wife was a theater major" → This is about the wife, NOT the user
|
|
43
|
+
* **DO NOT Extract**: "My daughter is 10 years old" → This is about the daughter, NOT the user
|
|
44
|
+
* **DO NOT Extract**: "My brother lives in Texas" → This is about the brother, NOT the user
|
|
45
|
+
* If the user shares information about someone else, that is NOT a fact about the user.`;
|
|
46
|
+
|
|
47
|
+
const criticalFragment = `# CRITICAL INSTRUCTIONS
|
|
48
|
+
|
|
49
|
+
ONLY ANALYZE the "Most Recent Messages" in the following conversation. The "Earlier Conversation" is provided for your context and has already been processed!
|
|
50
|
+
|
|
51
|
+
The JSON format is:
|
|
52
|
+
|
|
53
|
+
\`\`\`json
|
|
54
|
+
{
|
|
55
|
+
"facts": [
|
|
56
|
+
{
|
|
57
|
+
"name": "One of the missing fact names from above",
|
|
58
|
+
"value": "The exact value of the fact",
|
|
59
|
+
"evidence": "Direct quote or reference showing where this fact was stated"
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
\`\`\`
|
|
64
|
+
|
|
65
|
+
**Return JSON only.**`;
|
|
66
|
+
|
|
67
|
+
const system = `${taskFragment}
|
|
68
|
+
|
|
69
|
+
${missingFactsFragment}
|
|
70
|
+
|
|
71
|
+
${guidelinesFragment}
|
|
72
|
+
|
|
73
|
+
${criticalFragment}`;
|
|
74
|
+
|
|
75
|
+
const earlierSection = data.messages_context.length > 0
|
|
76
|
+
? `## Earlier Conversation
|
|
77
|
+
${formatMessagesAsPlaceholders(data.messages_context, personaName)}
|
|
78
|
+
|
|
79
|
+
`
|
|
80
|
+
: '';
|
|
81
|
+
|
|
82
|
+
const recentSection = `## Most Recent Messages
|
|
83
|
+
${formatMessagesAsPlaceholders(data.messages_analyze, personaName)}`;
|
|
84
|
+
|
|
85
|
+
const user = `# Conversation
|
|
86
|
+
${earlierSection}${recentSection}
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
Scan the "Most Recent Messages" for FACTS about the human user.
|
|
91
|
+
|
|
92
|
+
**Return JSON:**
|
|
93
|
+
\`\`\`json
|
|
94
|
+
{
|
|
95
|
+
"facts": [
|
|
96
|
+
{
|
|
97
|
+
"name": "One of the missing fact names from above",
|
|
98
|
+
"value": "The exact value of the fact",
|
|
99
|
+
"evidence": "Direct quote or reference showing where this fact was stated"
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
\`\`\``;
|
|
104
|
+
|
|
105
|
+
return { system, user };
|
|
106
|
+
}
|
|
@@ -75,8 +75,6 @@ Your job is to quickly identify:
|
|
|
75
75
|
> They are details OF facts (e.g., { "type_of_fact": "Birthday", "value_of_fact": "August 15th" }).
|
|
76
76
|
|
|
77
77
|
**FACTS ARE NOT**
|
|
78
|
-
- Trait: Personality patterns, communication style, behavioral tendencies
|
|
79
|
-
- These are tracked separately
|
|
80
78
|
- General Topic: Interests, hobbies, general subjects
|
|
81
79
|
- These are tracked separately
|
|
82
80
|
- Relationships: Wife, Husband, Daughter, Son, etc.
|
|
@@ -1,32 +1,39 @@
|
|
|
1
1
|
export { buildHumanFactScanPrompt } from "./fact-scan.js";
|
|
2
|
-
export {
|
|
2
|
+
export { buildFactFindPrompt } from "./fact-find.js";
|
|
3
3
|
export { buildHumanTopicScanPrompt } from "./topic-scan.js";
|
|
4
4
|
export { buildHumanPersonScanPrompt } from "./person-scan.js";
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
5
|
+
export { buildTopicMatchPrompt } from "./topic-match.js";
|
|
6
|
+
export { buildTopicUpdatePrompt } from "./topic-update.js";
|
|
7
|
+
export { buildPersonMatchPrompt } from "./person-match.js";
|
|
8
|
+
export { buildPersonUpdatePrompt } from "./person-update.js";
|
|
9
|
+
export { buildEventScanPrompt } from "./event-scan.js";
|
|
10
|
+
export type { EventScanPromptData } from "./event-scan.js";
|
|
11
|
+
|
|
12
|
+
export type { TopicMatchPromptData } from "./topic-match.js";
|
|
13
|
+
export type { TopicUpdatePromptData } from "./topic-update.js";
|
|
14
|
+
export type { PersonMatchPromptData } from "./person-match.js";
|
|
15
|
+
export type { PersonUpdatePromptData } from "./person-update.js";
|
|
7
16
|
|
|
8
17
|
export type {
|
|
9
18
|
PromptOutput,
|
|
19
|
+
ParticipantContext,
|
|
10
20
|
FactScanPromptData,
|
|
11
|
-
TraitScanPromptData,
|
|
12
21
|
TopicScanPromptData,
|
|
13
22
|
PersonScanPromptData,
|
|
23
|
+
FactFindPromptData,
|
|
14
24
|
FactScanCandidate,
|
|
15
|
-
TraitScanCandidate,
|
|
16
25
|
TopicScanCandidate,
|
|
17
26
|
PersonScanCandidate,
|
|
18
27
|
FactScanResult,
|
|
19
|
-
|
|
28
|
+
FactFindResult,
|
|
20
29
|
TopicScanResult,
|
|
21
30
|
PersonScanResult,
|
|
22
|
-
|
|
31
|
+
EventScanCandidate,
|
|
32
|
+
EventScanResult,
|
|
23
33
|
ItemMatchResult,
|
|
24
|
-
ItemUpdatePromptData,
|
|
25
34
|
ExposureImpact,
|
|
26
35
|
ItemUpdateResultBase,
|
|
27
36
|
FactUpdateResult,
|
|
28
|
-
TraitUpdateResult,
|
|
29
|
-
TopicUpdateResult,
|
|
30
37
|
PersonUpdateResult,
|
|
31
38
|
ItemUpdateResult,
|
|
32
39
|
} from "./types.js";
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { PromptOutput } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export interface PersonMatchPromptData {
|
|
4
|
+
candidate_name: string;
|
|
5
|
+
candidate_description: string;
|
|
6
|
+
candidate_relationship: string;
|
|
7
|
+
existing_people: Array<{
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
relationship?: string;
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function buildPersonMatchPrompt(data: PersonMatchPromptData): PromptOutput {
|
|
16
|
+
if (!data.candidate_name) {
|
|
17
|
+
throw new Error("buildPersonMatchPrompt: candidate_name is required");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const system = `# Task
|
|
21
|
+
|
|
22
|
+
You are checking if a PERSON already exists in our database.
|
|
23
|
+
|
|
24
|
+
## Matching Rules
|
|
25
|
+
|
|
26
|
+
1. **Exact match**: Same person by name or clear identity → return their ID
|
|
27
|
+
2. **Similar match**: Same person referred to differently ("Mom" vs "Carol", "my boss" vs "Trumble") → return their ID
|
|
28
|
+
3. **No match**: Genuinely new person → return "new"
|
|
29
|
+
|
|
30
|
+
Be conservative. If you're unsure, return "new" — a duplicate is worse than a gap.
|
|
31
|
+
|
|
32
|
+
# Existing People
|
|
33
|
+
|
|
34
|
+
\`\`\`json
|
|
35
|
+
${JSON.stringify(data.existing_people, 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 Person
|
|
51
|
+
|
|
52
|
+
Name: ${data.candidate_name}
|
|
53
|
+
Description: ${data.candidate_description}
|
|
54
|
+
Relationship: ${data.candidate_relationship}
|
|
55
|
+
|
|
56
|
+
Find the best match in existing people, or return "new" if this is a genuinely new person.
|
|
57
|
+
|
|
58
|
+
\`\`\`json
|
|
59
|
+
{
|
|
60
|
+
"matched_guid": "..." | "new"
|
|
61
|
+
}
|
|
62
|
+
\`\`\``;
|
|
63
|
+
|
|
64
|
+
return { system, user };
|
|
65
|
+
}
|
|
@@ -8,72 +8,64 @@ export function buildHumanPersonScanPrompt(data: PersonScanPromptData): PromptOu
|
|
|
8
8
|
|
|
9
9
|
const personaName = data.persona_name;
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
You are scanning a conversation to quickly identify PEOPLE
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
ONLY ANALYZE the "Most Recent Messages" in the following conversation. The "Earlier Conversation" is provided for your context and has already been processed!
|
|
53
|
-
|
|
54
|
-
The JSON format is:
|
|
11
|
+
const system = `# Task
|
|
12
|
+
|
|
13
|
+
You are scanning a conversation to quickly identify PEOPLE in the HUMAN USER's life.
|
|
14
|
+
|
|
15
|
+
Detect and flag. Do NOT analyze deeply — that happens later.
|
|
16
|
+
|
|
17
|
+
## What to Capture
|
|
18
|
+
|
|
19
|
+
Flag a PERSON when they were meaningfully discussed — not just mentioned in passing.
|
|
20
|
+
|
|
21
|
+
Be **conservative**: ignore one-off mentions, greetings, small talk, or jokes. Only flag people who matter to the human user's life.
|
|
22
|
+
|
|
23
|
+
## What a PERSON Is
|
|
24
|
+
|
|
25
|
+
Someone in the human user's world. Use the relationship as the primary classifier:
|
|
26
|
+
|
|
27
|
+
**Immediate Family**: Father, Mother, Son, Daughter, Brother, Sister, Husband, Wife, Partner (and step/in-law variants)
|
|
28
|
+
|
|
29
|
+
**Extended Family**: Grandfather, Grandmother, Aunt, Uncle, Cousin, Niece, Nephew
|
|
30
|
+
|
|
31
|
+
**Social**: Friend, Close Acquaintance, Lover, Love Interest, Fiance, Spouse
|
|
32
|
+
|
|
33
|
+
**Professional**: Coworker, Manager, Report, Mentor, Client
|
|
34
|
+
|
|
35
|
+
**AI**: Persona (use \`relationship: "AI Persona"\` for AI companions and assistants)
|
|
36
|
+
|
|
37
|
+
**NOT a PERSON:**
|
|
38
|
+
- The user themselves
|
|
39
|
+
- Biographical facts, topics, or hobbies
|
|
40
|
+
- Fictional characters from books, movies, or media
|
|
41
|
+
- Public figures only mentioned in passing (celebrities, politicians) — unless the user has a real relationship with them
|
|
42
|
+
|
|
43
|
+
## When Identity Is Unclear
|
|
44
|
+
|
|
45
|
+
If you can't identify which "Bob" or which "Brother" the user means, use "Unknown" and explain in the reason field. This triggers a later step to resolve ambiguity.
|
|
46
|
+
|
|
47
|
+
Examples:
|
|
48
|
+
- name: "Alice from work", relationship: "Coworker", description: "Mentioned but not described further", reason: "User referenced a work colleague named Alice"
|
|
49
|
+
- name: "Unknown", relationship: "Sibling", description: "User mentioned a sibling but did not give a name", reason: "User said 'my brother' without further context"
|
|
50
|
+
|
|
51
|
+
## Output Format
|
|
55
52
|
|
|
56
53
|
\`\`\`json
|
|
57
54
|
{
|
|
58
55
|
"people": [
|
|
59
56
|
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
"name": "The person's name, or 'Unknown' if not given",
|
|
58
|
+
"description": "1-2 sentences: who this person is and their role in the user's life",
|
|
59
|
+
"relationship": "Relationship type from the list above",
|
|
60
|
+
"reason": "Evidence from the conversation that justified flagging this person"
|
|
63
61
|
}
|
|
64
62
|
]
|
|
65
63
|
}
|
|
66
64
|
\`\`\`
|
|
67
65
|
|
|
68
|
-
**Return JSON only
|
|
69
|
-
|
|
70
|
-
const system = `${taskFragment}
|
|
71
|
-
|
|
72
|
-
${specificNeedsFragment}
|
|
73
|
-
|
|
74
|
-
${guidelinesFragment}
|
|
66
|
+
**Return JSON only.**
|
|
75
67
|
|
|
76
|
-
|
|
68
|
+
ONLY ANALYZE the "Most Recent Messages". The "Earlier Conversation" is provided for context only — it has already been processed.`;
|
|
77
69
|
|
|
78
70
|
const earlierSection = data.messages_context.length > 0
|
|
79
71
|
? `## Earlier Conversation
|
|
@@ -90,16 +82,17 @@ ${earlierSection}${recentSection}
|
|
|
90
82
|
|
|
91
83
|
---
|
|
92
84
|
|
|
93
|
-
Scan the "Most Recent Messages" for PEOPLE
|
|
85
|
+
Scan the "Most Recent Messages" for PEOPLE in the human user's life.
|
|
94
86
|
|
|
95
87
|
**Return JSON:**
|
|
96
88
|
\`\`\`json
|
|
97
89
|
{
|
|
98
90
|
"people": [
|
|
99
91
|
{
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
92
|
+
"name": "The person's name, or 'Unknown' if not given",
|
|
93
|
+
"description": "1-2 sentences: who this person is and their role in the user's life",
|
|
94
|
+
"relationship": "Relationship type from the list above",
|
|
95
|
+
"reason": "Evidence from the conversation that justified flagging this person"
|
|
103
96
|
}
|
|
104
97
|
]
|
|
105
98
|
}
|