ei-tui 0.4.3 → 0.5.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 +14 -0
- package/package.json +1 -1
- package/src/cli/README.md +17 -12
- package/src/cli/commands/personas.ts +12 -0
- package/src/cli/mcp.ts +2 -2
- package/src/cli/retrieval.ts +86 -8
- package/src/cli.ts +8 -5
- package/src/core/constants/seed-traits.ts +29 -0
- package/src/core/context-utils.ts +1 -0
- package/src/core/handlers/human-matching.ts +53 -35
- package/src/core/handlers/index.ts +5 -0
- package/src/core/handlers/persona-preview.ts +7 -0
- package/src/core/handlers/persona-topics.ts +3 -2
- package/src/core/handlers/rooms.ts +176 -0
- package/src/core/handlers/utils.ts +55 -3
- package/src/core/heartbeat-manager.ts +3 -1
- package/src/core/llm-client.ts +1 -1
- package/src/core/message-manager.ts +10 -8
- package/src/core/orchestrators/human-extraction.ts +15 -2
- package/src/core/orchestrators/index.ts +1 -0
- package/src/core/orchestrators/persona-generation.ts +4 -0
- package/src/core/orchestrators/persona-topics.ts +2 -1
- package/src/core/orchestrators/room-extraction.ts +318 -0
- package/src/core/persona-manager.ts +16 -5
- package/src/core/personas/opencode-agent.ts +12 -2
- package/src/core/processor.ts +520 -4
- package/src/core/prompt-context-builder.ts +89 -5
- package/src/core/queue-processor.ts +68 -8
- package/src/core/room-manager.ts +408 -0
- package/src/core/state/index.ts +1 -0
- package/src/core/state/personas.ts +12 -2
- package/src/core/state/queue.ts +2 -2
- package/src/core/state/rooms.ts +182 -0
- package/src/core/state-manager.ts +124 -2
- package/src/core/tool-manager.ts +1 -1
- package/src/core/tools/index.ts +15 -0
- package/src/core/types/enums.ts +11 -0
- package/src/core/types/integrations.ts +10 -2
- package/src/core/types/llm.ts +3 -0
- package/src/core/types/rooms.ts +59 -0
- package/src/core/types.ts +1 -0
- package/src/core/utils/decay.ts +14 -8
- package/src/core/utils/exposure.ts +14 -0
- package/src/integrations/claude-code/importer.ts +23 -10
- package/src/integrations/cursor/importer.ts +22 -10
- package/src/integrations/opencode/importer.ts +30 -13
- package/src/prompts/ceremony/dedup.ts +2 -2
- package/src/prompts/generation/from-person.ts +85 -0
- package/src/prompts/generation/index.ts +2 -0
- package/src/prompts/generation/persona.ts +14 -10
- package/src/prompts/generation/seeds.ts +4 -29
- package/src/prompts/generation/types.ts +13 -0
- package/src/prompts/heartbeat/check.ts +1 -1
- package/src/prompts/heartbeat/ei.ts +4 -4
- package/src/prompts/heartbeat/types.ts +1 -0
- package/src/prompts/index.ts +15 -0
- package/src/prompts/message-utils.ts +2 -2
- package/src/prompts/persona/topics-match.ts +7 -6
- package/src/prompts/persona/topics-update.ts +8 -11
- package/src/prompts/persona/types.ts +2 -1
- package/src/prompts/response/index.ts +1 -1
- package/src/prompts/response/sections.ts +20 -8
- package/src/prompts/response/types.ts +6 -0
- package/src/prompts/room/index.ts +115 -0
- package/src/prompts/room/sections.ts +150 -0
- package/src/prompts/room/types.ts +93 -0
- package/tui/README.md +20 -0
- package/tui/src/app.tsx +3 -2
- package/tui/src/commands/activate.tsx +98 -0
- package/tui/src/commands/archive.tsx +54 -25
- package/tui/src/commands/capture.tsx +50 -0
- package/tui/src/commands/dedupe.tsx +2 -7
- package/tui/src/commands/delete.tsx +48 -0
- package/tui/src/commands/details.tsx +7 -0
- package/tui/src/commands/persona.tsx +271 -9
- package/tui/src/commands/room.tsx +261 -0
- package/tui/src/commands/silence.tsx +29 -0
- package/tui/src/components/ArchivedItemsOverlay.tsx +144 -0
- package/tui/src/components/ConfirmOverlay.tsx +6 -0
- package/tui/src/components/ConflictOverlay.tsx +6 -0
- package/tui/src/components/HelpOverlay.tsx +6 -1
- package/tui/src/components/LoadingOverlay.tsx +51 -0
- package/tui/src/components/MessageList.tsx +1 -18
- package/tui/src/components/PersonPickerOverlay.tsx +121 -0
- package/tui/src/components/PersonaListOverlay.tsx +6 -1
- package/tui/src/components/PromptInput.tsx +141 -8
- package/tui/src/components/ProviderListOverlay.tsx +5 -1
- package/tui/src/components/QuotesOverlay.tsx +5 -1
- package/tui/src/components/RoomMessageList.tsx +179 -0
- package/tui/src/components/Sidebar.tsx +54 -2
- package/tui/src/components/StatusBar.tsx +99 -8
- package/tui/src/components/ToolkitListOverlay.tsx +5 -1
- package/tui/src/components/WelcomeOverlay.tsx +6 -0
- package/tui/src/context/ei.tsx +252 -1
- package/tui/src/context/keyboard.tsx +48 -12
- package/tui/src/util/cyp-editor.tsx +152 -0
- package/tui/src/util/quote-utils.ts +19 -0
- package/tui/src/util/room-editor.tsx +164 -0
- package/tui/src/util/room-logic.ts +8 -0
- package/tui/src/util/room-parser.ts +70 -0
- package/tui/src/util/yaml-serializers.ts +151 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Room Response Handlers
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { ContextStatus, LLMNextStep, LLMPriority, LLMRequestType } from "../types.js";
|
|
6
|
+
import type { LLMResponse, RoomMessage } from "../types.js";
|
|
7
|
+
import type { StateManager } from "../state-manager.js";
|
|
8
|
+
import type { PersonaResponseResult } from "../../prompts/response/index.js";
|
|
9
|
+
import type { RoomJudgeResult } from "../../prompts/room/index.js";
|
|
10
|
+
import { buildRoomResponsePromptData } from "../prompt-context-builder.js";
|
|
11
|
+
|
|
12
|
+
export function handleRoomResponse(response: LLMResponse, state: StateManager): void {
|
|
13
|
+
const roomId = response.request.data.roomId as string;
|
|
14
|
+
const personaId = response.request.data.personaId as string;
|
|
15
|
+
const personaDisplayName = response.request.data.personaDisplayName as string;
|
|
16
|
+
const parentMessageId = response.request.data.parentMessageId as string | null ?? null;
|
|
17
|
+
|
|
18
|
+
if (!roomId || !personaId) {
|
|
19
|
+
console.error("[handleRoomResponse] Missing roomId or personaId in request data");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const now = new Date().toISOString();
|
|
24
|
+
|
|
25
|
+
if (response.parsed !== undefined) {
|
|
26
|
+
const result = response.parsed as PersonaResponseResult;
|
|
27
|
+
|
|
28
|
+
if (!result.should_respond) {
|
|
29
|
+
const reason = result.reason;
|
|
30
|
+
console.log(`[handleRoomResponse] ${personaDisplayName} chose silence in room ${roomId}: ${reason ?? "(no reason)"}`);
|
|
31
|
+
if (reason) {
|
|
32
|
+
const msg: RoomMessage = {
|
|
33
|
+
id: crypto.randomUUID(),
|
|
34
|
+
parent_id: parentMessageId,
|
|
35
|
+
role: "persona",
|
|
36
|
+
persona_id: personaId,
|
|
37
|
+
silence_reason: reason,
|
|
38
|
+
timestamp: now,
|
|
39
|
+
read: false,
|
|
40
|
+
context_status: ContextStatus.Default,
|
|
41
|
+
};
|
|
42
|
+
state.appendRoomMessage(roomId, msg);
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const verbal = result.verbal_response || undefined;
|
|
48
|
+
const action = result.action_response || undefined;
|
|
49
|
+
|
|
50
|
+
if (!verbal && !action) {
|
|
51
|
+
console.log(`[handleRoomResponse] ${personaDisplayName} returned should_respond=true but no content`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const msg: RoomMessage = {
|
|
56
|
+
id: crypto.randomUUID(),
|
|
57
|
+
parent_id: parentMessageId,
|
|
58
|
+
role: "persona",
|
|
59
|
+
persona_id: personaId,
|
|
60
|
+
verbal_response: verbal,
|
|
61
|
+
action_response: action,
|
|
62
|
+
timestamp: now,
|
|
63
|
+
read: false,
|
|
64
|
+
context_status: ContextStatus.Default,
|
|
65
|
+
};
|
|
66
|
+
state.appendRoomMessage(roomId, msg);
|
|
67
|
+
console.log(`[handleRoomResponse] Appended response from ${personaDisplayName} to room ${roomId}`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!response.content) {
|
|
72
|
+
console.log(`[handleRoomResponse] ${personaDisplayName} no response (empty content)`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const msg: RoomMessage = {
|
|
77
|
+
id: crypto.randomUUID(),
|
|
78
|
+
parent_id: parentMessageId,
|
|
79
|
+
role: "persona",
|
|
80
|
+
persona_id: personaId,
|
|
81
|
+
verbal_response: response.content,
|
|
82
|
+
timestamp: now,
|
|
83
|
+
read: false,
|
|
84
|
+
context_status: ContextStatus.Default,
|
|
85
|
+
};
|
|
86
|
+
state.appendRoomMessage(roomId, msg);
|
|
87
|
+
console.log(`[handleRoomResponse] Appended plain-text response from ${personaDisplayName} to room ${roomId}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function handleRoomJudge(response: LLMResponse, state: StateManager): Promise<void> {
|
|
91
|
+
const roomId = response.request.data.roomId as string;
|
|
92
|
+
const judgeDisplayName = response.request.data.judgePersonaDisplayName as string;
|
|
93
|
+
|
|
94
|
+
if (!roomId) {
|
|
95
|
+
console.error("[handleRoomJudge] Missing roomId in request data");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!response.parsed) {
|
|
100
|
+
console.error(`[handleRoomJudge] No parsed result from judge ${judgeDisplayName}`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const result = response.parsed as RoomJudgeResult;
|
|
105
|
+
if (!result.winner_message_id) {
|
|
106
|
+
console.error(`[handleRoomJudge] Judge ${judgeDisplayName} returned no winner_message_id`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const judgePersonaId = response.request.data.judgePersonaId as string;
|
|
111
|
+
|
|
112
|
+
const allMessages = state.getRoomMessages(roomId);
|
|
113
|
+
const winner = allMessages.find(m => m.id === result.winner_message_id);
|
|
114
|
+
if (!winner) {
|
|
115
|
+
console.error(`[handleRoomJudge] Winner message ${result.winner_message_id} not found in room ${roomId}`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const verdictParentId = winner.parent_id;
|
|
120
|
+
|
|
121
|
+
const ok = state.setRoomActiveNode(roomId, result.winner_message_id);
|
|
122
|
+
if (!ok) {
|
|
123
|
+
console.error(`[handleRoomJudge] Could not set active node ${result.winner_message_id} in room ${roomId}`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const losers = allMessages
|
|
128
|
+
.filter(m => m.parent_id === verdictParentId && m.id !== winner.id)
|
|
129
|
+
.map(m => m.id);
|
|
130
|
+
if (losers.length > 0) {
|
|
131
|
+
state.removeRoomMessages(roomId, losers);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (result.reason) {
|
|
135
|
+
console.log(`[handleRoomJudge] ${judgeDisplayName} verdict: ${result.reason}`);
|
|
136
|
+
const verdictMsg = {
|
|
137
|
+
id: crypto.randomUUID(),
|
|
138
|
+
parent_id: verdictParentId,
|
|
139
|
+
role: "persona" as const,
|
|
140
|
+
persona_id: judgePersonaId,
|
|
141
|
+
silence_reason: result.reason,
|
|
142
|
+
timestamp: new Date().toISOString(),
|
|
143
|
+
read: false,
|
|
144
|
+
context_status: "default" as import("../types.js").ContextStatus,
|
|
145
|
+
};
|
|
146
|
+
state.appendRoomMessage(roomId, verdictMsg);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const room = state.getRoom(roomId);
|
|
150
|
+
if (!room) return;
|
|
151
|
+
|
|
152
|
+
for (const personaId of room.persona_ids) {
|
|
153
|
+
if (room.judge_persona_id === personaId) continue;
|
|
154
|
+
const persona = state.persona_getById(personaId);
|
|
155
|
+
if (!persona || persona.is_archived || persona.is_paused) continue;
|
|
156
|
+
|
|
157
|
+
const isTUI = false;
|
|
158
|
+
const promptData = await buildRoomResponsePromptData(state, room, persona, isTUI);
|
|
159
|
+
const model = persona.model ?? state.getHuman().settings?.default_model ?? "";
|
|
160
|
+
|
|
161
|
+
state.queue_enqueue({
|
|
162
|
+
type: LLMRequestType.JSON,
|
|
163
|
+
priority: LLMPriority.Room,
|
|
164
|
+
system: promptData.system,
|
|
165
|
+
user: promptData.user,
|
|
166
|
+
next_step: LLMNextStep.HandleRoomResponse,
|
|
167
|
+
model,
|
|
168
|
+
data: {
|
|
169
|
+
roomId,
|
|
170
|
+
personaId,
|
|
171
|
+
personaDisplayName: persona.display_name,
|
|
172
|
+
parentMessageId: result.winner_message_id,
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -1,12 +1,53 @@
|
|
|
1
|
-
import type { Message, LLMResponse } from "../types.js";
|
|
1
|
+
import type { Message, RoomMessage, LLMResponse } from "../types.js";
|
|
2
2
|
import type { StateManager } from "../state-manager.js";
|
|
3
3
|
|
|
4
|
+
export function normalizeRoomMessages(messages: RoomMessage[], state: StateManager): Message[] {
|
|
5
|
+
const human = state.getHuman();
|
|
6
|
+
const humanName = human.settings?.name_display ?? "Human";
|
|
7
|
+
return messages.map(m => {
|
|
8
|
+
const speakerName = m.role === "human"
|
|
9
|
+
? humanName
|
|
10
|
+
: (state.persona_getById(m.persona_id ?? "")?.display_name ?? "Participant");
|
|
11
|
+
return {
|
|
12
|
+
id: m.id,
|
|
13
|
+
role: m.role === "human" ? "human" as const : "system" as const,
|
|
14
|
+
speaker_name: speakerName,
|
|
15
|
+
verbal_response: m.verbal_response,
|
|
16
|
+
action_response: m.action_response,
|
|
17
|
+
silence_reason: m.silence_reason,
|
|
18
|
+
timestamp: m.timestamp,
|
|
19
|
+
read: m.read,
|
|
20
|
+
context_status: m.context_status,
|
|
21
|
+
f: m.f,
|
|
22
|
+
t: m.t,
|
|
23
|
+
p: m.p,
|
|
24
|
+
e: m.e,
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
4
29
|
export function resolveMessageWindow(
|
|
5
30
|
response: LLMResponse,
|
|
6
31
|
state: StateManager
|
|
7
32
|
): { messages_context: Message[]; messages_analyze: Message[] } {
|
|
8
|
-
const
|
|
33
|
+
const roomId = response.request.data.roomId as string | undefined;
|
|
9
34
|
const messageIdsToMark = response.request.data.message_ids_to_mark as string[] | undefined;
|
|
35
|
+
|
|
36
|
+
if (roomId) {
|
|
37
|
+
const allRoomMessages = normalizeRoomMessages(state.getRoomMessages(roomId), state);
|
|
38
|
+
if (messageIdsToMark && messageIdsToMark.length > 0) {
|
|
39
|
+
const idSet = new Set(messageIdsToMark);
|
|
40
|
+
const messages_analyze = allRoomMessages.filter(m => idSet.has(m.id));
|
|
41
|
+
const analyzeStartTime = messages_analyze[0]?.timestamp ?? '9999';
|
|
42
|
+
const messages_context = allRoomMessages.filter(m =>
|
|
43
|
+
!idSet.has(m.id) && new Date(m.timestamp).getTime() < new Date(analyzeStartTime).getTime()
|
|
44
|
+
);
|
|
45
|
+
return { messages_context, messages_analyze };
|
|
46
|
+
}
|
|
47
|
+
return { messages_context: [], messages_analyze: allRoomMessages };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const personaId = response.request.data.personaId as string;
|
|
10
51
|
const allMessages = state.messages_get(personaId);
|
|
11
52
|
|
|
12
53
|
if (messageIdsToMark && messageIdsToMark.length > 0) {
|
|
@@ -48,10 +89,21 @@ export function markMessagesExtracted(
|
|
|
48
89
|
state: StateManager,
|
|
49
90
|
flag: ExtractionFlag
|
|
50
91
|
): void {
|
|
92
|
+
const roomId = response.request.data.roomId as string | undefined;
|
|
51
93
|
const personaId = response.request.data.personaId as string | undefined;
|
|
52
94
|
const messageIds = response.request.data.message_ids_to_mark as string[] | undefined;
|
|
53
95
|
|
|
54
|
-
if (!
|
|
96
|
+
if (!messageIds?.length) return;
|
|
97
|
+
|
|
98
|
+
if (roomId) {
|
|
99
|
+
const count = state.markRoomMessagesExtracted(roomId, messageIds, flag);
|
|
100
|
+
if (count > 0) {
|
|
101
|
+
console.log(`[markMessagesExtracted] Marked ${count} room messages with flag '${flag}' for room ${roomId}`);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!personaId) return;
|
|
55
107
|
|
|
56
108
|
const count = state.messages_markExtracted(personaId, messageIds, flag);
|
|
57
109
|
if (count > 0) {
|
|
@@ -158,9 +158,11 @@ export async function queueEiHeartbeat(
|
|
|
158
158
|
return;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
const recentHistory = history.slice(-10);
|
|
161
162
|
const promptData: EiHeartbeatPromptData = {
|
|
162
163
|
items,
|
|
163
|
-
recent_history:
|
|
164
|
+
recent_history: recentHistory,
|
|
165
|
+
system_messages: recentHistory.filter(m => m.role === "system"),
|
|
164
166
|
};
|
|
165
167
|
|
|
166
168
|
const prompt = buildEiHeartbeatPrompt(promptData);
|
package/src/core/llm-client.ts
CHANGED
|
@@ -159,7 +159,7 @@ export async function callLLMRaw(
|
|
|
159
159
|
const chatMessages: ChatMessage[] = [
|
|
160
160
|
{ role: "system", content: systemPrompt },
|
|
161
161
|
...messages,
|
|
162
|
-
{ role: "user", content: userPrompt },
|
|
162
|
+
...(userPrompt ? [{ role: "user" as const, content: userPrompt }] : []),
|
|
163
163
|
];
|
|
164
164
|
|
|
165
165
|
const finalMessages = ensureUserFirst(chatMessages);
|
|
@@ -36,7 +36,7 @@ export async function getMessages(
|
|
|
36
36
|
): Promise<Message[]> {
|
|
37
37
|
const persona = sm.persona_getById(personaId);
|
|
38
38
|
if (!persona) return [];
|
|
39
|
-
return sm.messages_get(personaId);
|
|
39
|
+
return sm.messages_get(personaId).filter(m => m.external !== true);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export async function markMessageRead(
|
|
@@ -128,12 +128,13 @@ export async function sendMessage(
|
|
|
128
128
|
qp: QueueProcessor,
|
|
129
129
|
currentRequest: LLMRequest | null,
|
|
130
130
|
personaId: string,
|
|
131
|
-
content: string,
|
|
131
|
+
content: string | null,
|
|
132
132
|
isTUI: boolean,
|
|
133
133
|
getModelForPersona: (id?: string) => string | undefined,
|
|
134
134
|
onError: (err: { code: string; message: string }) => void,
|
|
135
135
|
onMessageAdded: (id: string) => void,
|
|
136
|
-
onMessageQueued: (id: string) => void
|
|
136
|
+
onMessageQueued: (id: string) => void,
|
|
137
|
+
silenceReason?: string
|
|
137
138
|
): Promise<void> {
|
|
138
139
|
const persona = sm.persona_getById(personaId);
|
|
139
140
|
if (!persona) {
|
|
@@ -149,7 +150,8 @@ export async function sendMessage(
|
|
|
149
150
|
const message: Message = {
|
|
150
151
|
id: crypto.randomUUID(),
|
|
151
152
|
role: "human",
|
|
152
|
-
verbal_response: content,
|
|
153
|
+
verbal_response: content ?? undefined,
|
|
154
|
+
silence_reason: content ? undefined : (silenceReason ?? "passed"),
|
|
153
155
|
timestamp: new Date().toISOString(),
|
|
154
156
|
read: false,
|
|
155
157
|
context_status: "default" as ContextStatus,
|
|
@@ -158,7 +160,7 @@ export async function sendMessage(
|
|
|
158
160
|
onMessageAdded(persona.id);
|
|
159
161
|
|
|
160
162
|
const tools = sm.tools_getForPersona(persona.id, isTUI);
|
|
161
|
-
const promptData = await buildResponsePromptData(sm, persona, isTUI, content, tools);
|
|
163
|
+
const promptData = await buildResponsePromptData(sm, persona, isTUI, content ?? "", tools);
|
|
162
164
|
const prompt = buildResponsePrompt(promptData);
|
|
163
165
|
|
|
164
166
|
sm.queue_enqueue({
|
|
@@ -215,7 +217,7 @@ export function checkAndQueueHumanExtraction(
|
|
|
215
217
|
): void {
|
|
216
218
|
const human = sm.getHuman();
|
|
217
219
|
|
|
218
|
-
const unextractedFacts = sm.messages_getUnextracted(personaId, "f");
|
|
220
|
+
const unextractedFacts = sm.messages_getUnextracted(personaId, "f", undefined, "exclude");
|
|
219
221
|
const factsThreshold = Math.min(EXTRACTION_TAPER_CAP, human.facts.filter(f => f.description && f.description !== "").length);
|
|
220
222
|
if (unextractedFacts.length > 0 && unextractedFacts.length >= factsThreshold) {
|
|
221
223
|
const context: ExtractionContext = {
|
|
@@ -231,7 +233,7 @@ export function checkAndQueueHumanExtraction(
|
|
|
231
233
|
);
|
|
232
234
|
}
|
|
233
235
|
|
|
234
|
-
const unextractedTopics = sm.messages_getUnextracted(personaId, "t");
|
|
236
|
+
const unextractedTopics = sm.messages_getUnextracted(personaId, "t", undefined, "exclude");
|
|
235
237
|
const topicsThreshold = Math.min(EXTRACTION_TAPER_CAP, human.topics.length);
|
|
236
238
|
if (unextractedTopics.length > 0 && unextractedTopics.length >= topicsThreshold) {
|
|
237
239
|
const context: ExtractionContext = {
|
|
@@ -247,7 +249,7 @@ export function checkAndQueueHumanExtraction(
|
|
|
247
249
|
);
|
|
248
250
|
}
|
|
249
251
|
|
|
250
|
-
const unextractedPeople = sm.messages_getUnextracted(personaId, "p");
|
|
252
|
+
const unextractedPeople = sm.messages_getUnextracted(personaId, "p", undefined, "exclude");
|
|
251
253
|
const peopleThreshold = Math.min(EXTRACTION_TAPER_CAP, human.people.length);
|
|
252
254
|
if (unextractedPeople.length > 0 && unextractedPeople.length >= peopleThreshold) {
|
|
253
255
|
const context: ExtractionContext = {
|
|
@@ -58,6 +58,7 @@ export interface ExtractionContext {
|
|
|
58
58
|
messages_context: Message[];
|
|
59
59
|
messages_analyze: Message[];
|
|
60
60
|
extraction_flag?: "f" | "t" | "p" | "e";
|
|
61
|
+
roomId?: string;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
export interface ExtractionOptions {
|
|
@@ -67,6 +68,15 @@ export interface ExtractionOptions {
|
|
|
67
68
|
extraction_model?: string;
|
|
68
69
|
/** Override token budget for chunking */
|
|
69
70
|
extraction_token_limit?: number;
|
|
71
|
+
/**
|
|
72
|
+
* Controls whether external (integration-imported) messages are included.
|
|
73
|
+
* - "exclude": skip messages where external === true
|
|
74
|
+
* - "only": include ONLY messages where external === true
|
|
75
|
+
* - "include": include all messages (backward-compat default; omit means same)
|
|
76
|
+
*
|
|
77
|
+
* NOTE: "include" is the backward-compat default only. All new callers must explicitly pass "exclude" or "only". Will be removed in a future release.
|
|
78
|
+
*/
|
|
79
|
+
external_filter?: "include" | "exclude" | "only";
|
|
70
80
|
}
|
|
71
81
|
|
|
72
82
|
function getAnalyzeFromTimestamp(context: ExtractionContext): string | null {
|
|
@@ -466,6 +476,7 @@ export function queueTopicUpdate(
|
|
|
466
476
|
if (chunks.length === 0) return 0;
|
|
467
477
|
|
|
468
478
|
for (const chunk of chunks) {
|
|
479
|
+
const primaryPersonaId = context.personaId.split("|")[0];
|
|
469
480
|
const prompt = buildTopicUpdatePrompt({
|
|
470
481
|
existing_item: existingItem,
|
|
471
482
|
new_topic_name: isNewItem ? context.candidateName : undefined,
|
|
@@ -474,7 +485,7 @@ export function queueTopicUpdate(
|
|
|
474
485
|
messages_context: chunk.messages_context,
|
|
475
486
|
messages_analyze: chunk.messages_analyze,
|
|
476
487
|
persona_name: chunk.personaDisplayName,
|
|
477
|
-
participant_context: buildParticipantContext(
|
|
488
|
+
participant_context: buildParticipantContext(primaryPersonaId, state),
|
|
478
489
|
});
|
|
479
490
|
|
|
480
491
|
state.queue_enqueue({
|
|
@@ -487,6 +498,7 @@ export function queueTopicUpdate(
|
|
|
487
498
|
data: {
|
|
488
499
|
personaId: context.personaId,
|
|
489
500
|
personaDisplayName: context.personaDisplayName,
|
|
501
|
+
roomId: context.roomId,
|
|
490
502
|
isNewItem,
|
|
491
503
|
existingItemId: existingItem?.id,
|
|
492
504
|
candidateCategory: context.candidateCategory,
|
|
@@ -509,7 +521,7 @@ export function queueEventSummary(
|
|
|
509
521
|
return 0;
|
|
510
522
|
}
|
|
511
523
|
|
|
512
|
-
const unextractedMessages = state.messages_getUnextracted(personaId, "e");
|
|
524
|
+
const unextractedMessages = state.messages_getUnextracted(personaId, "e", undefined, options?.external_filter);
|
|
513
525
|
if (unextractedMessages.length === 0) {
|
|
514
526
|
console.log(`[queueEventSummary] No unprocessed messages for ${persona.display_name}`);
|
|
515
527
|
return 0;
|
|
@@ -624,6 +636,7 @@ export function queuePersonUpdate(
|
|
|
624
636
|
data: {
|
|
625
637
|
personaId: context.personaId,
|
|
626
638
|
personaDisplayName: context.personaDisplayName,
|
|
639
|
+
roomId: context.roomId,
|
|
627
640
|
isNewItem,
|
|
628
641
|
existingItemId: existingItem?.id,
|
|
629
642
|
candidateRelationship: context.candidateRelationship,
|
|
@@ -39,12 +39,16 @@ export function orchestratePersonaGeneration(
|
|
|
39
39
|
const needsMoreTopics = topicCount < 3;
|
|
40
40
|
|
|
41
41
|
if (needsShortDescription || needsMoreTraits || needsMoreTopics) {
|
|
42
|
+
const filteredTraits = (partial.traits ?? []).filter(t => t.name?.trim());
|
|
43
|
+
const filteredTopics = (partial.topics ?? []).filter(t => t.name?.trim());
|
|
42
44
|
const prompt = buildPersonaGenerationPrompt({
|
|
43
45
|
name: partial.name,
|
|
44
46
|
long_description: partial.long_description,
|
|
45
47
|
short_description: partial.short_description,
|
|
46
48
|
existing_traits: partial.traits,
|
|
47
49
|
existing_topics: partial.topics,
|
|
50
|
+
filtered_traits: filteredTraits,
|
|
51
|
+
filtered_topics: filteredTopics,
|
|
48
52
|
});
|
|
49
53
|
|
|
50
54
|
stateManager.queue_enqueue({
|
|
@@ -110,7 +110,8 @@ export function queuePersonaTopicUpdate(
|
|
|
110
110
|
personaId: context.personaId,
|
|
111
111
|
personaDisplayName: context.personaDisplayName,
|
|
112
112
|
candidate,
|
|
113
|
-
|
|
113
|
+
existingTopicId: existingTopic?.id ?? null,
|
|
114
|
+
isNewTopic: !existingTopic,
|
|
114
115
|
analyze_from_timestamp: getAnalyzeFromTimestamp(context),
|
|
115
116
|
},
|
|
116
117
|
});
|