ei-tui 0.1.3

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 (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +170 -0
  3. package/package.json +63 -0
  4. package/src/README.md +96 -0
  5. package/src/cli/README.md +47 -0
  6. package/src/cli/commands/facts.ts +25 -0
  7. package/src/cli/commands/people.ts +25 -0
  8. package/src/cli/commands/quotes.ts +19 -0
  9. package/src/cli/commands/topics.ts +25 -0
  10. package/src/cli/commands/traits.ts +25 -0
  11. package/src/cli/retrieval.ts +269 -0
  12. package/src/cli.ts +176 -0
  13. package/src/core/AGENTS.md +104 -0
  14. package/src/core/embedding-service.ts +241 -0
  15. package/src/core/handlers/index.ts +1057 -0
  16. package/src/core/index.ts +4 -0
  17. package/src/core/llm-client.ts +265 -0
  18. package/src/core/model-context-windows.ts +49 -0
  19. package/src/core/orchestrators/ceremony.ts +500 -0
  20. package/src/core/orchestrators/extraction-chunker.ts +138 -0
  21. package/src/core/orchestrators/human-extraction.ts +457 -0
  22. package/src/core/orchestrators/index.ts +28 -0
  23. package/src/core/orchestrators/persona-generation.ts +76 -0
  24. package/src/core/orchestrators/persona-topics.ts +117 -0
  25. package/src/core/personas/index.ts +5 -0
  26. package/src/core/personas/opencode-agent.ts +81 -0
  27. package/src/core/processor.ts +1413 -0
  28. package/src/core/queue-processor.ts +197 -0
  29. package/src/core/state/checkpoints.ts +68 -0
  30. package/src/core/state/human.ts +176 -0
  31. package/src/core/state/index.ts +5 -0
  32. package/src/core/state/personas.ts +217 -0
  33. package/src/core/state/queue.ts +144 -0
  34. package/src/core/state-manager.ts +347 -0
  35. package/src/core/types.ts +421 -0
  36. package/src/core/utils/decay.ts +33 -0
  37. package/src/index.ts +1 -0
  38. package/src/integrations/opencode/importer.ts +896 -0
  39. package/src/integrations/opencode/index.ts +16 -0
  40. package/src/integrations/opencode/json-reader.ts +304 -0
  41. package/src/integrations/opencode/reader-factory.ts +35 -0
  42. package/src/integrations/opencode/sqlite-reader.ts +189 -0
  43. package/src/integrations/opencode/types.ts +244 -0
  44. package/src/prompts/AGENTS.md +62 -0
  45. package/src/prompts/ceremony/description-check.ts +47 -0
  46. package/src/prompts/ceremony/expire.ts +30 -0
  47. package/src/prompts/ceremony/explore.ts +60 -0
  48. package/src/prompts/ceremony/index.ts +11 -0
  49. package/src/prompts/ceremony/types.ts +42 -0
  50. package/src/prompts/generation/descriptions.ts +91 -0
  51. package/src/prompts/generation/index.ts +15 -0
  52. package/src/prompts/generation/persona.ts +155 -0
  53. package/src/prompts/generation/seeds.ts +31 -0
  54. package/src/prompts/generation/types.ts +47 -0
  55. package/src/prompts/heartbeat/check.ts +179 -0
  56. package/src/prompts/heartbeat/ei.ts +208 -0
  57. package/src/prompts/heartbeat/index.ts +15 -0
  58. package/src/prompts/heartbeat/types.ts +70 -0
  59. package/src/prompts/human/fact-scan.ts +152 -0
  60. package/src/prompts/human/index.ts +32 -0
  61. package/src/prompts/human/item-match.ts +74 -0
  62. package/src/prompts/human/item-update.ts +322 -0
  63. package/src/prompts/human/person-scan.ts +115 -0
  64. package/src/prompts/human/topic-scan.ts +135 -0
  65. package/src/prompts/human/trait-scan.ts +115 -0
  66. package/src/prompts/human/types.ts +127 -0
  67. package/src/prompts/index.ts +90 -0
  68. package/src/prompts/message-utils.ts +39 -0
  69. package/src/prompts/persona/index.ts +16 -0
  70. package/src/prompts/persona/topics-match.ts +69 -0
  71. package/src/prompts/persona/topics-scan.ts +98 -0
  72. package/src/prompts/persona/topics-update.ts +157 -0
  73. package/src/prompts/persona/traits.ts +117 -0
  74. package/src/prompts/persona/types.ts +74 -0
  75. package/src/prompts/response/index.ts +147 -0
  76. package/src/prompts/response/sections.ts +355 -0
  77. package/src/prompts/response/types.ts +38 -0
  78. package/src/prompts/validation/ei.ts +93 -0
  79. package/src/prompts/validation/index.ts +6 -0
  80. package/src/prompts/validation/types.ts +22 -0
  81. package/src/storage/crypto.ts +96 -0
  82. package/src/storage/index.ts +5 -0
  83. package/src/storage/interface.ts +9 -0
  84. package/src/storage/local.ts +79 -0
  85. package/src/storage/merge.ts +69 -0
  86. package/src/storage/remote.ts +145 -0
  87. package/src/templates/welcome.ts +91 -0
  88. package/tui/README.md +62 -0
  89. package/tui/bunfig.toml +4 -0
  90. package/tui/src/app.tsx +55 -0
  91. package/tui/src/commands/archive.tsx +93 -0
  92. package/tui/src/commands/context.tsx +124 -0
  93. package/tui/src/commands/delete.tsx +71 -0
  94. package/tui/src/commands/details.tsx +41 -0
  95. package/tui/src/commands/editor.tsx +46 -0
  96. package/tui/src/commands/help.tsx +12 -0
  97. package/tui/src/commands/me.tsx +145 -0
  98. package/tui/src/commands/model.ts +47 -0
  99. package/tui/src/commands/new.ts +31 -0
  100. package/tui/src/commands/pause.ts +46 -0
  101. package/tui/src/commands/persona.tsx +58 -0
  102. package/tui/src/commands/provider.tsx +124 -0
  103. package/tui/src/commands/quit.ts +22 -0
  104. package/tui/src/commands/quotes.tsx +172 -0
  105. package/tui/src/commands/registry.test.ts +137 -0
  106. package/tui/src/commands/registry.ts +130 -0
  107. package/tui/src/commands/resume.ts +39 -0
  108. package/tui/src/commands/setsync.tsx +43 -0
  109. package/tui/src/commands/settings.tsx +83 -0
  110. package/tui/src/components/ConfirmOverlay.tsx +51 -0
  111. package/tui/src/components/ConflictOverlay.tsx +78 -0
  112. package/tui/src/components/HelpOverlay.tsx +69 -0
  113. package/tui/src/components/Layout.tsx +24 -0
  114. package/tui/src/components/MessageList.tsx +174 -0
  115. package/tui/src/components/PersonaListOverlay.tsx +186 -0
  116. package/tui/src/components/PromptInput.tsx +145 -0
  117. package/tui/src/components/ProviderListOverlay.tsx +208 -0
  118. package/tui/src/components/QuotesOverlay.tsx +157 -0
  119. package/tui/src/components/Sidebar.tsx +95 -0
  120. package/tui/src/components/StatusBar.tsx +77 -0
  121. package/tui/src/components/WelcomeOverlay.tsx +73 -0
  122. package/tui/src/context/ei.tsx +623 -0
  123. package/tui/src/context/keyboard.tsx +164 -0
  124. package/tui/src/context/overlay.tsx +53 -0
  125. package/tui/src/index.tsx +8 -0
  126. package/tui/src/storage/file.ts +185 -0
  127. package/tui/src/util/duration.ts +32 -0
  128. package/tui/src/util/editor.ts +188 -0
  129. package/tui/src/util/logger.ts +109 -0
  130. package/tui/src/util/persona-editor.tsx +181 -0
  131. package/tui/src/util/provider-editor.tsx +168 -0
  132. package/tui/src/util/syntax.ts +35 -0
  133. package/tui/src/util/yaml-serializers.ts +755 -0
@@ -0,0 +1,244 @@
1
+ /**
2
+ * OpenCode Integration Types
3
+ *
4
+ * These types represent the data structures read from OpenCode's storage.
5
+ * They are based on the actual file format in ~/.local/share/opencode/storage/
6
+ * and the SQLite database in ~/.local/share/opencode/opencode.db (1.2+)
7
+ */
8
+
9
+ // ============================================================================
10
+ // Reader Interface
11
+ // ============================================================================
12
+
13
+ /**
14
+ * Common interface for reading OpenCode data.
15
+ * Implemented by both JsonReader (legacy) and SqliteReader (1.2+).
16
+ */
17
+ export interface IOpenCodeReader {
18
+ getSessionsUpdatedSince(since: Date): Promise<OpenCodeSession[]>;
19
+ getSessionsInRange(from: Date, to: Date): Promise<OpenCodeSession[]>;
20
+ getMessagesForSession(sessionId: string, since?: Date): Promise<OpenCodeMessage[]>;
21
+ getAgentInfo(agentName: string): Promise<OpenCodeAgent | null>;
22
+ getAllUniqueAgents(sessionId: string): Promise<string[]>;
23
+ getFirstAgent(sessionId: string): Promise<string | null>;
24
+ }
25
+
26
+ // ============================================================================
27
+ // Session Types (storage/session/{project_hash}/ses_xxx.json)
28
+ // ============================================================================
29
+
30
+ /**
31
+ * Raw session data as stored by OpenCode
32
+ */
33
+ export interface OpenCodeSessionRaw {
34
+ id: string; // ses_xxx
35
+ slug: string;
36
+ version: string;
37
+ projectID: string; // git root commit hash or "global"
38
+ directory: string; // Full path to project
39
+ parentID?: string; // Parent session ID if this is a subagent session
40
+ title: string;
41
+ time: {
42
+ created: number; // Unix timestamp ms
43
+ updated: number; // Unix timestamp ms
44
+ archived?: number; // Only set by web app
45
+ };
46
+ summary?: {
47
+ additions: number;
48
+ deletions: number;
49
+ files: number;
50
+ };
51
+ }
52
+
53
+ /**
54
+ * Cleaned session data for Ei consumption
55
+ */
56
+ export interface OpenCodeSession {
57
+ id: string; // ses_xxx
58
+ title: string;
59
+ directory: string;
60
+ projectId: string;
61
+ parentId?: string;
62
+ time: {
63
+ created: number;
64
+ updated: number;
65
+ };
66
+ }
67
+
68
+ // ============================================================================
69
+ // Message Types (storage/message/{session_id}/msg_xxx.json)
70
+ // ============================================================================
71
+
72
+ /**
73
+ * Raw message data as stored by OpenCode
74
+ */
75
+ export interface OpenCodeMessageRaw {
76
+ id: string; // msg_xxx
77
+ sessionID: string;
78
+ role: "user" | "assistant";
79
+ time: {
80
+ created: number;
81
+ completed?: number;
82
+ };
83
+ agent: string; // "build", "sisyphus", "librarian", etc.
84
+ parentID?: string;
85
+ model?: {
86
+ providerID: string;
87
+ modelID: string;
88
+ };
89
+ modelID?: string;
90
+ providerID?: string;
91
+ mode?: string;
92
+ cost?: number;
93
+ tokens?: {
94
+ input: number;
95
+ output: number;
96
+ reasoning: number;
97
+ cache?: {
98
+ read: number;
99
+ write: number;
100
+ };
101
+ };
102
+ finish?: string;
103
+ }
104
+
105
+ /**
106
+ * Cleaned message data for Ei consumption
107
+ */
108
+ export interface OpenCodeMessage {
109
+ id: string; // msg_xxx
110
+ sessionId: string;
111
+ role: "user" | "assistant";
112
+ agent: string;
113
+ content: string; // Filtered, concatenated text parts
114
+ timestamp: string; // ISO string from time.created
115
+ }
116
+
117
+ // ============================================================================
118
+ // Part Types (storage/part/{msg_id}/prt_xxx.json)
119
+ // ============================================================================
120
+
121
+ /**
122
+ * Raw part data as stored by OpenCode
123
+ * Parts can be of various types, but we only care about text parts
124
+ */
125
+ export interface OpenCodePartRaw {
126
+ id: string; // prt_xxx
127
+ sessionID: string;
128
+ messageID: string;
129
+ type: "text" | "tool" | "file" | "step-start" | "step-finish" | string;
130
+ text?: string; // Only present for type="text"
131
+ synthetic?: boolean; // true = tool call summary, skip
132
+ time?: {
133
+ start: number;
134
+ end: number;
135
+ };
136
+ }
137
+
138
+ // ============================================================================
139
+ // Agent Types
140
+ // ============================================================================
141
+
142
+ /**
143
+ * Agent info for persona creation
144
+ */
145
+ export interface OpenCodeAgent {
146
+ name: string;
147
+ description?: string;
148
+ }
149
+
150
+ /**
151
+ * Utility agents that should not have personas created for them.
152
+ * These are internal agents that do housekeeping tasks.
153
+ */
154
+ export const UTILITY_AGENTS = ["compaction", "title", "summary"] as const;
155
+
156
+ /**
157
+ * Message content prefixes that indicate agent-to-agent communication.
158
+ * Messages starting with these should be filtered out during import.
159
+ */
160
+ export const AGENT_TO_AGENT_PREFIXES = [
161
+ "[search-mode]",
162
+ "[analyze-mode]",
163
+ "[CONTEXT]",
164
+ "<analysis>",
165
+ "<results>",
166
+ ] as const;
167
+
168
+ /**
169
+ * Agent name aliases for consolidating variants into a single persona.
170
+ * OpenCode's plugin ecosystem results in the same logical agent having
171
+ * different names across versions/configs (e.g., "sisyphus", "Sisyphus",
172
+ * "Sisyphus (Ultraworker)", "Planner-Sisyphus" are all the same agent).
173
+ *
174
+ * Key = canonical name (used for display_name, Title Case)
175
+ * Value = array of variants that should resolve to this persona
176
+ */
177
+ export const AGENT_ALIASES: Record<string, string[]> = {
178
+ Sisyphus: [
179
+ "sisyphus",
180
+ "Sisyphus",
181
+ "Sisyphus (Ultraworker)",
182
+ "Planner-Sisyphus",
183
+ "planner-sisyphus",
184
+ ],
185
+ };
186
+
187
+ /**
188
+ * Built-in agent definitions (fallback when config unavailable)
189
+ */
190
+ export const BUILTIN_AGENTS: Record<string, OpenCodeAgent> = {
191
+ build: {
192
+ name: "build",
193
+ description: "The main coding agent that implements features and fixes bugs",
194
+ },
195
+ sisyphus: {
196
+ name: "sisyphus",
197
+ description: "Powerful AI Agent with orchestration capabilities",
198
+ },
199
+ plan: {
200
+ name: "plan",
201
+ description: "Creates implementation plans for complex features",
202
+ },
203
+ general: {
204
+ name: "general",
205
+ description: "General purpose assistant for non-coding tasks",
206
+ },
207
+ explore: {
208
+ name: "explore",
209
+ description: "Explores codebases to understand structure and patterns",
210
+ },
211
+ librarian: {
212
+ name: "librarian",
213
+ description:
214
+ "Searches external references including documentation and open source examples",
215
+ },
216
+ compaction: {
217
+ name: "compaction",
218
+ description: "Compacts conversation history to save context",
219
+ },
220
+ title: {
221
+ name: "title",
222
+ description: "Generates session titles",
223
+ },
224
+ summary: {
225
+ name: "summary",
226
+ description: "Generates session summaries",
227
+ },
228
+ oracle: {
229
+ name: "oracle",
230
+ description: "High-IQ read-only consultant for architecture and debugging",
231
+ },
232
+ atlas: {
233
+ name: "atlas",
234
+ description: "Task execution agent for delegated work",
235
+ },
236
+ metis: {
237
+ name: "metis",
238
+ description: "Pre-planning consultant for scope clarification",
239
+ },
240
+ momus: {
241
+ name: "momus",
242
+ description: "Expert reviewer for evaluating work plans",
243
+ },
244
+ };
@@ -0,0 +1,62 @@
1
+ # Prompts Module
2
+
3
+ LLM prompt builders. All prompts are **synchronous** and **pure**.
4
+
5
+ ## Structure
6
+
7
+ ```
8
+ prompts/
9
+ ├── index.ts # Re-exports all builders
10
+ ├── message-utils.ts # Format messages for context
11
+ ├── ceremony/ # Exposure decay, persona enrichment
12
+ ├── generation/ # New persona creation
13
+ ├── heartbeat/ # Periodic check-ins
14
+ ├── human/ # Fact/trait/topic/person extraction
15
+ ├── persona/ # Persona trait extraction, topic matching
16
+ ├── response/ # Main conversation responses
17
+ └── validation/ # Data validation prompts
18
+ ```
19
+
20
+ ## Pattern
21
+
22
+ Every prompt builder follows this contract:
23
+
24
+ ```typescript
25
+ interface PromptBuilder<T> {
26
+ (data: T): { system: string; user: string }
27
+ }
28
+ ```
29
+
30
+ **Rules**:
31
+ 1. **Synchronous** - No async, no fetching
32
+ 2. **Pure** - Same input → same output
33
+ 3. **Pre-processed data** - Processor fetches/filters before calling
34
+ 4. **Minimal logic** - String interpolation, not computation
35
+
36
+ ## Adding New Prompts
37
+
38
+ 1. Create directory: `prompts/[purpose]/`
39
+ 2. Create files:
40
+ - `types.ts` - Input data interface
41
+ - `sections.ts` - Reusable prompt fragments
42
+ - `index.ts` - Builder function + exports
43
+ 3. Export from `prompts/index.ts`
44
+ 4. Call from Processor with pre-fetched data
45
+
46
+ ## Subdirectories
47
+
48
+ | Directory | Purpose | Key Output |
49
+ |-----------|---------|------------|
50
+ | `response/` | Generate persona replies | Conversational text |
51
+ | `human/` | Extract facts/traits/topics/people | JSON arrays |
52
+ | `persona/` | Extract persona traits, match topics | JSON |
53
+ | `generation/` | Create new personas | PersonaEntity JSON |
54
+ | `ceremony/` | Exposure decay prompts | Updated values |
55
+ | `heartbeat/` | Check if persona should speak | Boolean-ish |
56
+ | `validation/` | Verify extracted data | Confirmation |
57
+
58
+ ## Key Insight
59
+
60
+ **Prompt engineering lives here. Code logic lives in Processor.**
61
+
62
+ When modifying persona behavior, check prompts first—the "personality" is in the English, not the TypeScript.
@@ -0,0 +1,47 @@
1
+ import type { DescriptionCheckPromptData } from "./types.js";
2
+
3
+ export function buildDescriptionCheckPrompt(data: DescriptionCheckPromptData): { system: string; user: string } {
4
+ const traitList = data.traits.length > 0
5
+ ? data.traits.map(t => `- ${t.name}: ${t.description}`).join("\n")
6
+ : "No traits defined";
7
+
8
+ const topicList = data.topics.length > 0
9
+ ? data.topics.map(t => `- ${t.name}: ${t.perspective} (exposure: ${t.exposure_current.toFixed(2)})`).join("\n")
10
+ : "No topics defined";
11
+
12
+ const system = `You are evaluating whether a persona's description needs updating.
13
+
14
+ A description should ONLY be updated if there is a SIGNIFICANT mismatch between:
15
+ - The current description
16
+ - The persona's current traits and topics
17
+
18
+ Be VERY conservative. Only recommend updating if:
19
+ - The description mentions interests/traits that are completely absent from current data
20
+ - The persona has developed major new interests not reflected in the description
21
+ - The description actively contradicts the current personality
22
+
23
+ Do NOT recommend updating for:
24
+ - Minor differences or evolution
25
+ - Natural topic drift over time
26
+ - Missing details that aren't contradictions
27
+
28
+ Return JSON: { "should_update": true/false, "reason": "explanation" }`;
29
+
30
+ const user = `Persona: ${data.persona_name}
31
+
32
+ Current short description:
33
+ ${data.current_short_description ?? "(none)"}
34
+
35
+ Current long description:
36
+ ${data.current_long_description ?? "(none)"}
37
+
38
+ Current personality traits:
39
+ ${traitList}
40
+
41
+ Current topics of interest:
42
+ ${topicList}
43
+
44
+ Does this persona's description need updating based on their current traits and topics?`;
45
+
46
+ return { system, user };
47
+ }
@@ -0,0 +1,30 @@
1
+ import type { PersonaExpirePromptData } from "./types.js";
2
+
3
+ export function buildPersonaExpirePrompt(data: PersonaExpirePromptData): { system: string; user: string } {
4
+ const topicList = data.topics.map(t => {
5
+ const display = t.perspective || t.name;
6
+ return `- "${t.name}" (exposure: ${t.exposure_current.toFixed(2)}, sentiment: ${t.sentiment.toFixed(2)})\n Perspective: ${display}`;
7
+ }).join("\n");
8
+
9
+ const system = `You are evaluating which topics a persona should stop caring about.
10
+
11
+ A topic should be removed if:
12
+ - Its exposure_current is very low (< 0.15) indicating prolonged disinterest
13
+ - It no longer aligns with the persona's current interests
14
+ - It was a temporary interest that has faded
15
+
16
+ Be conservative - only suggest removing topics that are clearly irrelevant.
17
+ If unsure, keep the topic.
18
+
19
+ Return JSON: { "topic_ids_to_remove": ["id1", "id2"] }
20
+ Return empty array if no topics should be removed.`;
21
+
22
+ const user = `Persona: ${data.persona_name}
23
+
24
+ Current topics:
25
+ ${topicList}
26
+
27
+ Which topics, if any, should this persona stop caring about?`;
28
+
29
+ return { system, user };
30
+ }
@@ -0,0 +1,60 @@
1
+ import type { PersonaExplorePromptData } from "./types.js";
2
+
3
+ export function buildPersonaExplorePrompt(data: PersonaExplorePromptData): { system: string; user: string } {
4
+ const traitList = data.traits.map(t =>
5
+ `- ${t.name}: ${t.description} (strength: ${t.strength?.toFixed(2) ?? "N/A"})`
6
+ ).join("\n");
7
+
8
+ const topicList = data.remaining_topics.map(t =>
9
+ `- ${t.name}: ${t.perspective || t.name}`
10
+ ).join("\n");
11
+
12
+ const themeList = data.recent_conversation_themes.length > 0
13
+ ? data.recent_conversation_themes.map(t => `- ${t}`).join("\n")
14
+ : "No recent themes identified";
15
+
16
+ const system = `You are generating new conversation topics for a persona.
17
+
18
+ Topics should:
19
+ - Align with the persona's traits and personality
20
+ - Complement (not duplicate) existing topics
21
+ - Be natural extensions of recent conversation themes
22
+ - Be specific enough to drive interesting conversations
23
+
24
+ Generate 1-3 new topics that this persona would genuinely care about.
25
+
26
+ Return JSON:
27
+ {
28
+ "new_topics": [{
29
+ "name": "Topic Name",
30
+ "perspective": "The persona's view or opinion on this topic",
31
+ "approach": "How they prefer to engage with this topic",
32
+ "personal_stake": "Why this topic matters to them personally",
33
+ "sentiment": 0.5,
34
+ "exposure_current": 0.2,
35
+ "exposure_desired": 0.6
36
+ }]
37
+ }
38
+
39
+ **Field guidance:**
40
+ - perspective: REQUIRED - their actual view/opinion
41
+ - approach: Optional if unclear - how they discuss this topic
42
+ - personal_stake: Optional if unclear - why it matters to them
43
+ - exposure_current: Low (0.2) since these are new topics
44
+ - exposure_desired: How much the persona would want to discuss this`;
45
+
46
+ const user = `Persona: ${data.persona_name}
47
+
48
+ Personality traits:
49
+ ${traitList}
50
+
51
+ Current topics (do not duplicate):
52
+ ${topicList}
53
+
54
+ Recent conversation themes:
55
+ ${themeList}
56
+
57
+ Generate new topics this persona would care about.`;
58
+
59
+ return { system, user };
60
+ }
@@ -0,0 +1,11 @@
1
+ export { buildPersonaExpirePrompt } from "./expire.js";
2
+ export { buildPersonaExplorePrompt } from "./explore.js";
3
+ export { buildDescriptionCheckPrompt } from "./description-check.js";
4
+ export type {
5
+ PersonaExpirePromptData,
6
+ PersonaExpireResult,
7
+ PersonaExplorePromptData,
8
+ PersonaExploreResult,
9
+ DescriptionCheckPromptData,
10
+ DescriptionCheckResult,
11
+ } from "./types.js";
@@ -0,0 +1,42 @@
1
+ import type { Trait, PersonaTopic } from "../../core/types.js";
2
+
3
+ export interface PersonaExpirePromptData {
4
+ persona_name: string;
5
+ topics: PersonaTopic[];
6
+ }
7
+
8
+ export interface PersonaExpireResult {
9
+ topic_ids_to_remove: string[];
10
+ }
11
+
12
+ export interface PersonaExplorePromptData {
13
+ persona_name: string;
14
+ traits: Trait[];
15
+ remaining_topics: PersonaTopic[];
16
+ recent_conversation_themes: string[];
17
+ }
18
+
19
+ export interface PersonaExploreResult {
20
+ new_topics: Array<{
21
+ name: string;
22
+ perspective: string;
23
+ approach: string;
24
+ personal_stake: string;
25
+ sentiment: number;
26
+ exposure_current: number;
27
+ exposure_desired: number;
28
+ }>;
29
+ }
30
+
31
+ export interface DescriptionCheckPromptData {
32
+ persona_name: string;
33
+ current_short_description?: string;
34
+ current_long_description?: string;
35
+ traits: Trait[];
36
+ topics: PersonaTopic[];
37
+ }
38
+
39
+ export interface DescriptionCheckResult {
40
+ should_update: boolean;
41
+ reason?: string;
42
+ }
@@ -0,0 +1,91 @@
1
+ import type { PersonaDescriptionsPromptData, PromptOutput } from "./types.js";
2
+ import type { Trait, PersonaTopic } from "../../core/types.js";
3
+
4
+ function formatTraitsForPrompt(traits: Trait[]): string {
5
+ if (traits.length === 0) return "(No traits defined)";
6
+
7
+ return traits.map(t => {
8
+ const strength = t.strength !== undefined ? ` (strength: ${t.strength.toFixed(1)})` : "";
9
+ return `- **${t.name}**${strength}: ${t.description}`;
10
+ }).join('\n');
11
+ }
12
+
13
+ function formatTopicsForPrompt(topics: PersonaTopic[]): string {
14
+ if (topics.length === 0) return "(No topics defined)";
15
+
16
+ return topics.map(t => {
17
+ const sentiment = t.sentiment > 0.3 ? "enjoys" : t.sentiment < -0.3 ? "dislikes" : "neutral";
18
+ return `- **${t.name}** (${sentiment}): ${t.perspective || t.name}`;
19
+ }).join('\n');
20
+ }
21
+
22
+ export function buildPersonaDescriptionsPrompt(data: PersonaDescriptionsPromptData): PromptOutput {
23
+ if (!data.name) {
24
+ throw new Error("buildPersonaDescriptionsPrompt: name is required");
25
+ }
26
+
27
+ const taskFragment = `You are regenerating descriptions for an existing AI persona named "${data.name}".
28
+
29
+ Their traits and topics have evolved, and the descriptions may no longer accurately represent who they are.
30
+
31
+ **Important**: Only change descriptions if there's a significant mismatch. If the current descriptions still fit, return \`{ "no_change": true }\`.`;
32
+
33
+ const currentStateFragment = `## Current State
34
+
35
+ ### Aliases
36
+ ${data.aliases.length > 0 ? data.aliases.join(", ") : "(None)"}
37
+
38
+ ### Traits
39
+ ${formatTraitsForPrompt(data.traits)}
40
+
41
+ ### Topics
42
+ ${formatTopicsForPrompt(data.topics)}`;
43
+
44
+ const guidelinesFragment = `## Guidelines
45
+
46
+ **When to change:**
47
+ - Traits/topics have shifted significantly from the original concept
48
+ - Current descriptions mention things that are no longer true
49
+ - The persona's core identity has evolved
50
+
51
+ **When NOT to change:**
52
+ - Descriptions are still accurate even if incomplete
53
+ - Changes are minor refinements
54
+ - Original descriptions capture the essence well
55
+
56
+ **If changing:**
57
+ - short_description: 10-15 words capturing the essence
58
+ - long_description: 2-3 sentences describing personality, interests, approach
59
+ - Preserve the persona's core identity while reflecting evolution`;
60
+
61
+ const schemaFragment = `## Response Format
62
+
63
+ If descriptions should change:
64
+ \`\`\`json
65
+ {
66
+ "short_description": "New short description here",
67
+ "long_description": "New long description here."
68
+ }
69
+ \`\`\`
70
+
71
+ If descriptions are still accurate:
72
+ \`\`\`json
73
+ {
74
+ "no_change": true
75
+ }
76
+ \`\`\``;
77
+
78
+ const system = `${taskFragment}
79
+
80
+ ${currentStateFragment}
81
+
82
+ ${guidelinesFragment}
83
+
84
+ ${schemaFragment}`;
85
+
86
+ const user = `Based on the traits and topics above, should ${data.name}'s descriptions be updated?
87
+
88
+ Remember: Only change if there's a significant mismatch. Stability is preferred.`;
89
+
90
+ return { system, user };
91
+ }
@@ -0,0 +1,15 @@
1
+ export { buildPersonaGenerationPrompt } from "./persona.js";
2
+ export { buildPersonaDescriptionsPrompt } from "./descriptions.js";
3
+ export {
4
+ DEFAULT_SEED_TRAITS,
5
+ SEED_TRAIT_GENUINE,
6
+ SEED_TRAIT_NATURAL_SPEECH,
7
+ type SeedTrait,
8
+ } from "./seeds.js";
9
+ export type {
10
+ PersonaGenerationPromptData,
11
+ PersonaGenerationResult,
12
+ PersonaDescriptionsPromptData,
13
+ PersonaDescriptionsResult,
14
+ PromptOutput,
15
+ } from "./types.js";