ei-tui 0.9.3 → 0.9.4
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/package.json +4 -1
- package/src/README.md +1 -1
- package/src/core/context-utils.ts +2 -2
- package/src/core/handlers/heartbeat.ts +9 -1
- package/src/core/handlers/human-extraction.ts +4 -1
- package/src/core/handlers/human-matching.ts +5 -53
- package/src/core/handlers/index.ts +1 -51
- package/src/core/handlers/persona-generation.ts +1 -28
- package/src/core/handlers/utils.ts +2 -9
- package/src/core/heartbeat-manager.ts +3 -3
- package/src/core/message-manager.ts +6 -5
- package/src/core/orchestrators/ceremony.ts +4 -9
- package/src/core/orchestrators/extraction-chunker.ts +3 -3
- package/src/core/orchestrators/human-extraction.ts +17 -17
- package/src/core/orchestrators/index.ts +0 -1
- package/src/core/orchestrators/persona-topics.ts +1 -1
- package/src/core/orchestrators/room-extraction.ts +5 -5
- package/src/core/processor.ts +8 -21
- package/src/core/prompt-context-builder.ts +7 -6
- package/src/core/state/personas.ts +1 -17
- package/src/core/state-manager.ts +0 -66
- package/src/core/types/entities.ts +2 -3
- package/src/core/types/enums.ts +0 -2
- package/src/core/types/rooms.ts +1 -1
- package/src/integrations/claude-code/importer.ts +1 -1
- package/src/integrations/cursor/importer.ts +1 -1
- package/src/integrations/opencode/importer.ts +1 -1
- package/src/prompts/ceremony/index.ts +0 -10
- package/src/prompts/ceremony/types.ts +1 -42
- package/src/prompts/generation/index.ts +0 -3
- package/src/prompts/generation/types.ts +0 -15
- package/src/prompts/heartbeat/check.ts +18 -6
- package/src/prompts/heartbeat/types.ts +2 -1
- package/src/prompts/human/index.ts +0 -2
- package/src/prompts/human/types.ts +0 -16
- package/src/prompts/index.ts +0 -19
- package/src/prompts/reflection/index.ts +35 -5
- package/src/prompts/reflection/types.ts +1 -1
- package/src/prompts/response/index.ts +5 -0
- package/src/prompts/response/sections.ts +26 -0
- package/src/prompts/response/types.ts +3 -0
- package/tui/src/commands/registry.test.ts +10 -5
- package/tui/src/globals.d.ts +57 -0
- package/tui/src/util/yaml-persona.ts +8 -4
- package/tui/src/util/yaml-settings.ts +3 -3
- package/src/core/orchestrators/person-migration.ts +0 -55
- package/src/prompts/ceremony/description-check.ts +0 -54
- package/src/prompts/ceremony/expire.ts +0 -37
- package/src/prompts/ceremony/explore.ts +0 -77
- package/src/prompts/ceremony/person-migration.ts +0 -77
- package/src/prompts/generation/descriptions.ts +0 -91
- package/src/prompts/human/fact-scan.ts +0 -150
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ei-tui",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"author": "Flare576",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -63,7 +63,10 @@
|
|
|
63
63
|
"test:evals:persona-trait": "vite-node tests/evals/persona-trait-extraction.eval.ts",
|
|
64
64
|
"test:evals:dedup": "vite-node tests/evals/dedup-tool-calls.eval.ts",
|
|
65
65
|
"test:evals:response-read-memory": "vite-node tests/evals/response-read-memory.eval.ts",
|
|
66
|
+
"test:evals:response-pending-update": "vite-node tests/evals/response-pending-update.eval.ts",
|
|
67
|
+
"test:evals:heartbeat-pending-update": "vite-node tests/evals/heartbeat-pending-update.eval.ts",
|
|
66
68
|
"test:evals:real-data": "vite-node tests/evals/real-data-example.eval.ts",
|
|
69
|
+
"test:evals:persona-data-check": "vite-node tests/evals/persona-data-check.eval.ts",
|
|
67
70
|
"test:all": "npm run test && npm run test:e2e && npm run test:e2e:tui",
|
|
68
71
|
"typecheck": "tsc --noEmit",
|
|
69
72
|
"web": "cd web && npm run dev",
|
package/src/README.md
CHANGED
|
@@ -148,7 +148,7 @@ This is intentional. Concurrent LLM calls sound appealing until you're watching
|
|
|
148
148
|
|
|
149
149
|
# Context Windows
|
|
150
150
|
|
|
151
|
-
Personas don't send their entire message history to the LLM. By default, only messages from the last 8 hours are included (`
|
|
151
|
+
Personas don't send their entire message history to the LLM. By default, only messages from the last 8 hours are included (`context_window_ms`, configurable per persona). Older messages are still stored — they're just not in the prompt.
|
|
152
152
|
|
|
153
153
|
Message rolloff works differently: messages are kept until there are at least 200 of them _and_ any are older than 14 days. So a persona you chat with daily will roll off old messages gradually; one you chat with twice a year will keep everything.
|
|
154
154
|
|
|
@@ -8,12 +8,12 @@ import { ContextStatus as ContextStatusEnum } from "./types.js";
|
|
|
8
8
|
export function filterMessagesForContext(
|
|
9
9
|
messages: Message[],
|
|
10
10
|
contextBoundary: string | undefined,
|
|
11
|
-
|
|
11
|
+
contextWindowMs: number
|
|
12
12
|
): Message[] {
|
|
13
13
|
if (messages.length === 0) return [];
|
|
14
14
|
|
|
15
15
|
const now = Date.now();
|
|
16
|
-
const windowStartMs = now -
|
|
16
|
+
const windowStartMs = now - contextWindowMs;
|
|
17
17
|
const boundaryMs = contextBoundary ? new Date(contextBoundary).getTime() : 0;
|
|
18
18
|
|
|
19
19
|
return messages.filter((msg) => {
|
|
@@ -126,13 +126,15 @@ export function handleReflectionCritic(response: LLMResponse, state: StateManage
|
|
|
126
126
|
const personaDisplayName = response.request.data.personaDisplayName as string;
|
|
127
127
|
|
|
128
128
|
const result = response.parsed as ReflectionCriticResult | undefined;
|
|
129
|
-
if (!result?.
|
|
129
|
+
if (!result?.critique) {
|
|
130
130
|
console.error(`[ReflectionCritic ${personaDisplayName}] Invalid or missing parsed result`);
|
|
131
131
|
return;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
const personRecord = state.human_person_getByIdentifier("Ei Persona", personaId);
|
|
135
135
|
if (personRecord) {
|
|
136
|
+
// TODO: Remove before v1 — debug logging to inspect person log before it's cleared
|
|
137
|
+
console.log(`[ReflectionCritic ${personaDisplayName}] person_log_snapshot (${personRecord.description?.length ?? 0} chars): ${personRecord.description ?? ""}`);
|
|
136
138
|
state.human_person_upsert({
|
|
137
139
|
...personRecord,
|
|
138
140
|
description: "",
|
|
@@ -140,6 +142,12 @@ export function handleReflectionCritic(response: LLMResponse, state: StateManage
|
|
|
140
142
|
console.log(`[ReflectionCritic ${personaDisplayName}] Person record description cleared — ready for fresh evidence after reflection`);
|
|
141
143
|
}
|
|
142
144
|
|
|
145
|
+
// Escape hatch: critic found no meaningful drift — log critique and skip pending_update
|
|
146
|
+
if (!result.updated_identity) {
|
|
147
|
+
console.log(`[ReflectionCritic ${personaDisplayName}] No drift detected — skipping pending_update. Critique: ${result.critique}`);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
143
151
|
const persona = state.persona_getById(personaId);
|
|
144
152
|
if (!persona) {
|
|
145
153
|
console.error(`[ReflectionCritic ${personaDisplayName}] Persona not found after critic`);
|
|
@@ -189,7 +189,10 @@ export async function handleHumanPersonScan(response: LLMResponse, state: StateM
|
|
|
189
189
|
return;
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
const context =
|
|
192
|
+
const context = {
|
|
193
|
+
...(response.request.data as unknown as ExtractionContext),
|
|
194
|
+
channelDisplayName: (response.request.data as Record<string, unknown>).personaDisplayName as string,
|
|
195
|
+
};
|
|
193
196
|
if (!context?.personaId) return;
|
|
194
197
|
|
|
195
198
|
const { messages_context, messages_analyze } = resolveMessageWindow(response, state);
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import type { PersonIdentifier } from "../types/data-items.js";
|
|
9
9
|
import type { StateManager } from "../state-manager.js";
|
|
10
10
|
import type { ItemMatchResult, ExposureImpact, TopicUpdateResult, PersonUpdateResult } from "../../prompts/human/types.js";
|
|
11
|
-
import { queueTopicUpdate,
|
|
11
|
+
import { queueTopicUpdate, queueTopicValidate, type ExtractionContext } from "../orchestrators/index.js";
|
|
12
12
|
import { getEmbeddingService, getTopicEmbeddingText, getPersonEmbeddingText } from "../embedding-service.js";
|
|
13
13
|
import { calculateExposureCurrent } from "../utils/exposure.js";
|
|
14
14
|
|
|
@@ -48,7 +48,7 @@ export function handleTopicMatch(response: LLMResponse, state: StateManager): vo
|
|
|
48
48
|
extraction_model?: string;
|
|
49
49
|
} = {
|
|
50
50
|
personaId,
|
|
51
|
-
personaDisplayName,
|
|
51
|
+
channelDisplayName: personaDisplayName,
|
|
52
52
|
roomId,
|
|
53
53
|
messages_context,
|
|
54
54
|
messages_analyze,
|
|
@@ -64,54 +64,6 @@ export function handleTopicMatch(response: LLMResponse, state: StateManager): vo
|
|
|
64
64
|
console.log(`[handleTopicMatch] topic "${context.candidateName}": ${matched}`);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
export function handlePersonMatch(response: LLMResponse, state: StateManager): void {
|
|
68
|
-
const result = response.parsed as ItemMatchResult | undefined;
|
|
69
|
-
if (!result) {
|
|
70
|
-
throw new Error("[handlePersonMatch] No parsed result");
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const personaId = response.request.data.personaId as string;
|
|
74
|
-
const personaDisplayName = response.request.data.personaDisplayName as string;
|
|
75
|
-
const roomId = response.request.data.roomId as string | undefined;
|
|
76
|
-
const { messages_context, messages_analyze } = resolveMessageWindow(response, state);
|
|
77
|
-
|
|
78
|
-
let matched_guid = result.matched_guid;
|
|
79
|
-
let resolvedPerson: import('../types/data-items.js').Person | null = null;
|
|
80
|
-
if (matched_guid === "new") {
|
|
81
|
-
matched_guid = null;
|
|
82
|
-
} else if (matched_guid) {
|
|
83
|
-
const human = state.getHuman();
|
|
84
|
-
resolvedPerson = human.people.find(p => p.id === matched_guid) ?? null;
|
|
85
|
-
if (!resolvedPerson) {
|
|
86
|
-
console.warn(`[handlePersonMatch] matched_guid "${matched_guid}" not found in people — treating as new`);
|
|
87
|
-
matched_guid = null;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
result.matched_guid = matched_guid;
|
|
91
|
-
|
|
92
|
-
const context: ExtractionContext & {
|
|
93
|
-
candidateName: string;
|
|
94
|
-
candidateDescription: string;
|
|
95
|
-
candidateRelationship: string;
|
|
96
|
-
extraction_model?: string;
|
|
97
|
-
} = {
|
|
98
|
-
personaId,
|
|
99
|
-
personaDisplayName,
|
|
100
|
-
roomId,
|
|
101
|
-
messages_context,
|
|
102
|
-
messages_analyze,
|
|
103
|
-
sources: response.request.data.sources as string[] | undefined,
|
|
104
|
-
candidateName: response.request.data.candidateName as string,
|
|
105
|
-
candidateDescription: response.request.data.candidateDescription as string,
|
|
106
|
-
candidateRelationship: response.request.data.candidateRelationship as string,
|
|
107
|
-
extraction_model: response.request.data.extraction_model as string | undefined,
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
queuePersonUpdate(result, context, state, resolvedPerson);
|
|
111
|
-
const matched = matched_guid ? `matched GUID "${matched_guid}"` : "no match (new person)";
|
|
112
|
-
console.log(`[handlePersonMatch] person "${context.candidateName}": ${matched}`);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
67
|
export async function handleTopicUpdate(response: LLMResponse, state: StateManager): Promise<void> {
|
|
116
68
|
const result = response.parsed as (TopicUpdateResult & { quotes?: Array<{ text: string; reason: string }> }) | undefined;
|
|
117
69
|
|
|
@@ -447,7 +399,7 @@ async function validateAndStoreQuotes(
|
|
|
447
399
|
candidates: Array<{ text: string; reason: string }> | undefined,
|
|
448
400
|
messages: Message[],
|
|
449
401
|
dataItemId: string,
|
|
450
|
-
|
|
402
|
+
channelDisplayName: string,
|
|
451
403
|
personaGroup: string | null,
|
|
452
404
|
state: StateManager
|
|
453
405
|
): Promise<void> {
|
|
@@ -540,8 +492,8 @@ async function validateAndStoreQuotes(
|
|
|
540
492
|
data_item_ids: [dataItemId],
|
|
541
493
|
persona_groups: [personaGroup || "General"],
|
|
542
494
|
text: matchText,
|
|
543
|
-
speaker: message.role === "human" ? "human" : (message.speaker_name ??
|
|
544
|
-
channel:
|
|
495
|
+
speaker: message.role === "human" ? "human" : (message.speaker_name ?? channelDisplayName),
|
|
496
|
+
channel: channelDisplayName,
|
|
545
497
|
timestamp: message.timestamp,
|
|
546
498
|
start: matchStart,
|
|
547
499
|
end: matchEnd,
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { LLMNextStep } from "../types.js";
|
|
2
|
-
import type { LLMResponse } from "../types.js";
|
|
3
|
-
import type { StateManager } from "../state-manager.js";
|
|
4
2
|
import type { ResponseHandler } from "./persona-response.js";
|
|
5
|
-
import type { PersonIdentifier } from "../types/data-items.js";
|
|
6
3
|
|
|
7
4
|
export type { ResponseHandler } from "./persona-response.js";
|
|
8
5
|
|
|
9
6
|
import { handlePersonaResponse, handleToolContinuation, handleOneShot, handleOneShotJSON } from "./persona-response.js";
|
|
10
7
|
import { handleHeartbeatCheck, handleEiHeartbeat, handleReflectionCritic } from "./heartbeat.js";
|
|
11
|
-
import { handlePersonaGeneration,
|
|
8
|
+
import { handlePersonaGeneration, handlePersonaTraitExtraction } from "./persona-generation.js";
|
|
12
9
|
import {
|
|
13
10
|
handlePersonaTopicRating,
|
|
14
11
|
} from "./persona-topics.js";
|
|
@@ -19,55 +16,9 @@ import { handleDedupCurate } from "./dedup.js";
|
|
|
19
16
|
import { handleRoomResponse, handleRoomJudge } from "./rooms.js";
|
|
20
17
|
import { handlePersonaPreview } from "./persona-preview.js";
|
|
21
18
|
|
|
22
|
-
function handlePersonIdentifierMigration(response: LLMResponse, state: StateManager): void {
|
|
23
|
-
const personId = response.request.data.person_id as string;
|
|
24
|
-
if (!personId) {
|
|
25
|
-
console.error("[handlePersonIdentifierMigration] Missing person_id in request data");
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const human = state.getHuman();
|
|
30
|
-
const person = human.people.find(p => p.id === personId);
|
|
31
|
-
if (!person) {
|
|
32
|
-
console.error(`[handlePersonIdentifierMigration] Person not found: ${personId}`);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const result = response.parsed as { identifiers?: Array<{ type: string; value: string; is_primary?: boolean }> } | undefined;
|
|
37
|
-
if (!result?.identifiers || !Array.isArray(result.identifiers) || result.identifiers.length === 0) {
|
|
38
|
-
console.error(`[handlePersonIdentifierMigration] Invalid or empty identifiers for ${person.name}`);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const hasName = result.identifiers.some(i => i.value === person.name);
|
|
43
|
-
if (!hasName) {
|
|
44
|
-
result.identifiers.unshift({ type: "nickname", value: person.name });
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const hasPrimary = result.identifiers.some(i => i.is_primary);
|
|
48
|
-
if (!hasPrimary) {
|
|
49
|
-
result.identifiers[0].is_primary = true;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const identifiers: PersonIdentifier[] = result.identifiers.map(i => ({
|
|
53
|
-
type: i.type,
|
|
54
|
-
value: i.value,
|
|
55
|
-
...(i.is_primary ? { is_primary: i.is_primary } : {}),
|
|
56
|
-
}));
|
|
57
|
-
|
|
58
|
-
state.human_person_upsert({
|
|
59
|
-
...person,
|
|
60
|
-
identifiers,
|
|
61
|
-
last_updated: new Date().toISOString(),
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
console.log(`[handlePersonIdentifierMigration] Migrated ${identifiers.length} identifier(s) for ${person.name}`);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
19
|
export const handlers: Record<LLMNextStep, ResponseHandler> = {
|
|
68
20
|
handlePersonaResponse,
|
|
69
21
|
handlePersonaGeneration,
|
|
70
|
-
handlePersonaDescriptions,
|
|
71
22
|
handleFactFind,
|
|
72
23
|
handleHumanTopicScan,
|
|
73
24
|
handleHumanPersonScan,
|
|
@@ -88,7 +39,6 @@ export const handlers: Record<LLMNextStep, ResponseHandler> = {
|
|
|
88
39
|
handleRoomResponse,
|
|
89
40
|
handleRoomJudge,
|
|
90
41
|
handlePersonaPreview,
|
|
91
|
-
[LLMNextStep.HandlePersonIdentifierMigration]: handlePersonIdentifierMigration,
|
|
92
42
|
[LLMNextStep.HandleTopicValidate]: handleDedupCurate,
|
|
93
43
|
[LLMNextStep.HandleReflectionCritic]: handleReflectionCritic,
|
|
94
44
|
};
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
type PersonaTopic,
|
|
5
5
|
} from "../types.js";
|
|
6
6
|
import type { StateManager } from "../state-manager.js";
|
|
7
|
-
import type { PersonaGenerationResult
|
|
7
|
+
import type { PersonaGenerationResult } from "../../prompts/generation/types.js";
|
|
8
8
|
import type { TraitResult } from "../../prompts/persona/types.js";
|
|
9
9
|
import { orchestratePersonaGeneration, type PartialPersona } from "../orchestrators/index.js";
|
|
10
10
|
|
|
@@ -111,33 +111,6 @@ export function handlePersonaGeneration(response: LLMResponse, state: StateManag
|
|
|
111
111
|
console.log(`[handlePersonaGeneration] Orchestrated: ${personaDisplayName}`);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
export function handlePersonaDescriptions(response: LLMResponse, state: StateManager): void {
|
|
115
|
-
const personaId = response.request.data.personaId as string;
|
|
116
|
-
const personaDisplayName = response.request.data.personaDisplayName as string;
|
|
117
|
-
if (!personaId) {
|
|
118
|
-
console.error("[handlePersonaDescriptions] No personaId in request data");
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const result = response.parsed as PersonaDescriptionsResult | undefined;
|
|
123
|
-
if (!result) {
|
|
124
|
-
console.error("[handlePersonaDescriptions] No parsed result");
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (result.no_change) {
|
|
129
|
-
console.log(`[handlePersonaDescriptions] No change needed for ${personaDisplayName}`);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
state.persona_update(personaId, {
|
|
134
|
-
short_description: result.short_description,
|
|
135
|
-
long_description: result.long_description,
|
|
136
|
-
last_updated: new Date().toISOString(),
|
|
137
|
-
});
|
|
138
|
-
console.log(`[handlePersonaDescriptions] Updated descriptions for ${personaDisplayName}`);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
114
|
export function handlePersonaTraitExtraction(response: LLMResponse, state: StateManager): void {
|
|
142
115
|
const personaId = response.request.data.personaId as string;
|
|
143
116
|
const personaDisplayName = response.request.data.personaDisplayName as string;
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
import type { Message, RoomMessage, LLMResponse } from "../types.js";
|
|
2
2
|
import type { StateManager } from "../state-manager.js";
|
|
3
3
|
|
|
4
|
-
export function getMessageContent(msg: { content?: string
|
|
5
|
-
|
|
6
|
-
// Legacy fallback for data not yet migrated on disk
|
|
7
|
-
// TODO(v1.0.0): Remove legacy verbal_response/action_response fallback
|
|
8
|
-
const legacy = msg as { verbal_response?: string; action_response?: string };
|
|
9
|
-
const parts: string[] = [];
|
|
10
|
-
if (legacy.action_response) parts.push(`_${legacy.action_response}_`);
|
|
11
|
-
if (legacy.verbal_response) parts.push(legacy.verbal_response);
|
|
12
|
-
return parts.join('\n\n');
|
|
4
|
+
export function getMessageContent(msg: { content?: string }): string {
|
|
5
|
+
return msg.content ?? '';
|
|
13
6
|
}
|
|
14
7
|
|
|
15
8
|
export function normalizeRoomMessages(messages: RoomMessage[], state: StateManager): Message[] {
|
|
@@ -217,8 +217,8 @@ export async function queueHeartbeatCheck(sm: StateManager, personaId: string, i
|
|
|
217
217
|
console.log(`[HeartbeatCheck ${persona.display_name}] Queueing heartbeat check (model: ${model})`);
|
|
218
218
|
const human = sm.getHuman();
|
|
219
219
|
const history = sm.messages_get(personaId);
|
|
220
|
-
const
|
|
221
|
-
const contextHistory = filterMessagesForContext(history, persona.context_boundary,
|
|
220
|
+
const contextWindowMs = persona.context_window_ms ?? human.settings?.default_context_window_ms ?? 28800000;
|
|
221
|
+
const contextHistory = filterMessagesForContext(history, persona.context_boundary, contextWindowMs);
|
|
222
222
|
|
|
223
223
|
if (personaId === "ei") {
|
|
224
224
|
await queueEiHeartbeat(sm, human, contextHistory, isTUI);
|
|
@@ -244,7 +244,7 @@ export async function queueHeartbeatCheck(sm: StateManager, personaId: string, i
|
|
|
244
244
|
name: persona.display_name,
|
|
245
245
|
traits: persona.traits,
|
|
246
246
|
topics: persona.topics,
|
|
247
|
-
|
|
247
|
+
pending_update: persona.pending_update,
|
|
248
248
|
},
|
|
249
249
|
human: {
|
|
250
250
|
topics: sortByEngagementGap(filteredHuman.topics).slice(0, 5),
|
|
@@ -223,7 +223,7 @@ export function checkAndQueueHumanExtraction(
|
|
|
223
223
|
if (unextractedFacts.length > 0 && unextractedFacts.length >= factsThreshold) {
|
|
224
224
|
const context: ExtractionContext = {
|
|
225
225
|
personaId,
|
|
226
|
-
personaDisplayName,
|
|
226
|
+
channelDisplayName: personaDisplayName,
|
|
227
227
|
messages_context: history.filter((m) => m.f === true),
|
|
228
228
|
messages_analyze: unextractedFacts,
|
|
229
229
|
extraction_flag: "f",
|
|
@@ -239,7 +239,7 @@ export function checkAndQueueHumanExtraction(
|
|
|
239
239
|
if (unextractedTopics.length > 0 && unextractedTopics.length >= topicsThreshold) {
|
|
240
240
|
const context: ExtractionContext = {
|
|
241
241
|
personaId,
|
|
242
|
-
personaDisplayName,
|
|
242
|
+
channelDisplayName: personaDisplayName,
|
|
243
243
|
messages_context: history.filter((m) => m.t === true),
|
|
244
244
|
messages_analyze: unextractedTopics,
|
|
245
245
|
extraction_flag: "t",
|
|
@@ -257,7 +257,7 @@ export function checkAndQueueHumanExtraction(
|
|
|
257
257
|
const personScanOptions = personaForScan?.pending_update ? { reflection_progress: 1 } : undefined;
|
|
258
258
|
const context: ExtractionContext = {
|
|
259
259
|
personaId,
|
|
260
|
-
personaDisplayName,
|
|
260
|
+
channelDisplayName: personaDisplayName,
|
|
261
261
|
messages_context: history.filter((m) => m.p === true),
|
|
262
262
|
messages_analyze: unextractedPeople,
|
|
263
263
|
extraction_flag: "p",
|
|
@@ -282,8 +282,9 @@ export function fetchMessagesForLLM(
|
|
|
282
282
|
|
|
283
283
|
const human = sm.getHuman();
|
|
284
284
|
const history = sm.messages_get(personaId);
|
|
285
|
-
|
|
286
|
-
|
|
285
|
+
const contextWindowMs = persona.context_window_ms ?? human.settings?.default_context_window_ms ?? 28800000;
|
|
286
|
+
const MAX_RESPONSE_MESSAGES = 50;
|
|
287
|
+
const filteredHistory = filterMessagesForContext(history, persona.context_boundary, contextWindowMs).slice(-MAX_RESPONSE_MESSAGES);
|
|
287
288
|
|
|
288
289
|
const humanName = human.settings?.name_display
|
|
289
290
|
|| human.facts?.find(f => f.name === "Nickname/Preferred Name")?.description
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
} from "./human-extraction.js";
|
|
13
13
|
import { queuePersonaTopicRating, type PersonaTopicContext, type PersonaTopicOptions } from "./persona-topics.js";
|
|
14
14
|
import { getRoomVisibleMessages, queueRoomHumanExtraction } from "./room-extraction.js";
|
|
15
|
-
import { queuePersonMigration } from "./person-migration.js";
|
|
16
15
|
import { buildRewriteScanPrompt, type RewriteItemType } from "../../prompts/ceremony/index.js";
|
|
17
16
|
import { buildReflectionCriticPrompt } from "../../prompts/reflection/index.js";
|
|
18
17
|
import { getModelForPersona } from "../heartbeat-manager.js";
|
|
@@ -76,10 +75,6 @@ export function startCeremony(state: StateManager): void {
|
|
|
76
75
|
},
|
|
77
76
|
});
|
|
78
77
|
|
|
79
|
-
// PHASE 1: Person Migration
|
|
80
|
-
console.log("[ceremony] Starting Phase 1: Person Migration");
|
|
81
|
-
queuePersonMigration(state);
|
|
82
|
-
|
|
83
78
|
// Check if migration work was queued
|
|
84
79
|
if (!state.queue_hasPendingCeremonies()) {
|
|
85
80
|
// No migration work found → immediately advance to Expose phase
|
|
@@ -110,7 +105,7 @@ function queueExposurePhase(personaId: string, state: StateManager, options?: Ex
|
|
|
110
105
|
if (unextractedFacts.length > 0) {
|
|
111
106
|
const context: ExtractionContext = {
|
|
112
107
|
personaId,
|
|
113
|
-
|
|
108
|
+
channelDisplayName: persona.display_name,
|
|
114
109
|
messages_context: allMessages.filter(m => m.f === true),
|
|
115
110
|
messages_analyze: unextractedFacts,
|
|
116
111
|
extraction_flag: "f",
|
|
@@ -123,7 +118,7 @@ function queueExposurePhase(personaId: string, state: StateManager, options?: Ex
|
|
|
123
118
|
if (unextractedTopics.length > 0) {
|
|
124
119
|
const context: ExtractionContext = {
|
|
125
120
|
personaId,
|
|
126
|
-
|
|
121
|
+
channelDisplayName: persona.display_name,
|
|
127
122
|
messages_context: allMessages.filter(m => m.t === true),
|
|
128
123
|
messages_analyze: unextractedTopics,
|
|
129
124
|
extraction_flag: "t",
|
|
@@ -135,7 +130,7 @@ function queueExposurePhase(personaId: string, state: StateManager, options?: Ex
|
|
|
135
130
|
if (unextractedPeople.length > 0) {
|
|
136
131
|
const context: ExtractionContext = {
|
|
137
132
|
personaId,
|
|
138
|
-
|
|
133
|
+
channelDisplayName: persona.display_name,
|
|
139
134
|
messages_context: allMessages.filter(m => m.p === true),
|
|
140
135
|
messages_analyze: unextractedPeople,
|
|
141
136
|
extraction_flag: "p",
|
|
@@ -475,7 +470,7 @@ export function queueReflectionDrain(personaId: string, state: StateManager): vo
|
|
|
475
470
|
|
|
476
471
|
const context: ExtractionContext = {
|
|
477
472
|
personaId,
|
|
478
|
-
|
|
473
|
+
channelDisplayName: persona.display_name,
|
|
479
474
|
messages_context: allMessages.filter(m => m.p === true),
|
|
480
475
|
messages_analyze: unextractedPeople,
|
|
481
476
|
extraction_flag: "p",
|
|
@@ -64,7 +64,7 @@ export function chunkExtractionContext(
|
|
|
64
64
|
context: ExtractionContext,
|
|
65
65
|
maxTokens: number = DEFAULT_MAX_TOKENS
|
|
66
66
|
): ChunkedContextResult {
|
|
67
|
-
const { personaId, personaDisplayName, messages_context, messages_analyze } = context;
|
|
67
|
+
const { personaId, channelDisplayName: personaDisplayName, messages_context, messages_analyze } = context;
|
|
68
68
|
|
|
69
69
|
if (messages_analyze.length === 0) {
|
|
70
70
|
return {
|
|
@@ -85,7 +85,7 @@ export function chunkExtractionContext(
|
|
|
85
85
|
return {
|
|
86
86
|
chunks: [{
|
|
87
87
|
personaId,
|
|
88
|
-
personaDisplayName,
|
|
88
|
+
channelDisplayName: personaDisplayName,
|
|
89
89
|
messages_context: fittedContext,
|
|
90
90
|
messages_analyze,
|
|
91
91
|
}],
|
|
@@ -111,7 +111,7 @@ export function chunkExtractionContext(
|
|
|
111
111
|
|
|
112
112
|
chunks.push({
|
|
113
113
|
personaId,
|
|
114
|
-
personaDisplayName,
|
|
114
|
+
channelDisplayName: personaDisplayName,
|
|
115
115
|
messages_context: currentContext,
|
|
116
116
|
messages_analyze: pulled,
|
|
117
117
|
});
|
|
@@ -55,7 +55,7 @@ function buildParticipantContext(personaId: string, state: StateManager): Partic
|
|
|
55
55
|
|
|
56
56
|
export interface ExtractionContext {
|
|
57
57
|
personaId: string;
|
|
58
|
-
|
|
58
|
+
channelDisplayName: string;
|
|
59
59
|
messages_context: Message[];
|
|
60
60
|
messages_analyze: Message[];
|
|
61
61
|
extraction_flag?: "f" | "t" | "p" | "e";
|
|
@@ -118,7 +118,7 @@ export function queueFactFind(context: ExtractionContext, state: StateManager, o
|
|
|
118
118
|
|
|
119
119
|
for (const chunk of chunks) {
|
|
120
120
|
const prompt = buildFactFindPrompt({
|
|
121
|
-
persona_name: chunk.
|
|
121
|
+
persona_name: chunk.channelDisplayName,
|
|
122
122
|
missing_fact_names,
|
|
123
123
|
messages_context: chunk.messages_context,
|
|
124
124
|
messages_analyze: chunk.messages_analyze,
|
|
@@ -134,7 +134,7 @@ export function queueFactFind(context: ExtractionContext, state: StateManager, o
|
|
|
134
134
|
data: {
|
|
135
135
|
...options,
|
|
136
136
|
personaId: chunk.personaId,
|
|
137
|
-
personaDisplayName: chunk.
|
|
137
|
+
personaDisplayName: chunk.channelDisplayName,
|
|
138
138
|
analyze_from_timestamp: getAnalyzeFromTimestamp(chunk),
|
|
139
139
|
extraction_flag: context.extraction_flag,
|
|
140
140
|
message_ids_to_mark: chunk.messages_analyze.map(m => m.id),
|
|
@@ -160,7 +160,7 @@ export function queueTopicScan(context: ExtractionContext, state: StateManager,
|
|
|
160
160
|
|
|
161
161
|
for (const chunk of chunks) {
|
|
162
162
|
const prompt = buildHumanTopicScanPrompt({
|
|
163
|
-
persona_name: chunk.
|
|
163
|
+
persona_name: chunk.channelDisplayName,
|
|
164
164
|
messages_context: chunk.messages_context,
|
|
165
165
|
messages_analyze: chunk.messages_analyze,
|
|
166
166
|
participant_context: buildParticipantContext(context.personaId, state),
|
|
@@ -176,7 +176,7 @@ export function queueTopicScan(context: ExtractionContext, state: StateManager,
|
|
|
176
176
|
data: {
|
|
177
177
|
...options,
|
|
178
178
|
personaId: chunk.personaId,
|
|
179
|
-
personaDisplayName: chunk.
|
|
179
|
+
personaDisplayName: chunk.channelDisplayName,
|
|
180
180
|
analyze_from_timestamp: getAnalyzeFromTimestamp(chunk),
|
|
181
181
|
extraction_flag: context.extraction_flag,
|
|
182
182
|
message_ids_to_mark: chunk.messages_analyze.map(m => m.id),
|
|
@@ -209,7 +209,7 @@ export function queuePersonScan(context: ExtractionContext, state: StateManager,
|
|
|
209
209
|
|
|
210
210
|
for (const chunk of chunks) {
|
|
211
211
|
const prompt = buildHumanPersonScanPrompt({
|
|
212
|
-
persona_name: chunk.
|
|
212
|
+
persona_name: chunk.channelDisplayName,
|
|
213
213
|
messages_context: chunk.messages_context,
|
|
214
214
|
messages_analyze: chunk.messages_analyze,
|
|
215
215
|
participant_context: buildParticipantContext(context.personaId, state),
|
|
@@ -226,7 +226,7 @@ export function queuePersonScan(context: ExtractionContext, state: StateManager,
|
|
|
226
226
|
data: {
|
|
227
227
|
...options,
|
|
228
228
|
personaId: chunk.personaId,
|
|
229
|
-
personaDisplayName: chunk.
|
|
229
|
+
personaDisplayName: chunk.channelDisplayName,
|
|
230
230
|
analyze_from_timestamp: getAnalyzeFromTimestamp(chunk),
|
|
231
231
|
extraction_flag: context.extraction_flag,
|
|
232
232
|
message_ids_to_mark: chunk.messages_analyze.map(m => m.id),
|
|
@@ -273,7 +273,7 @@ export function queueDirectTopicUpdate(
|
|
|
273
273
|
existing_item: topic,
|
|
274
274
|
messages_context: chunk.messages_context,
|
|
275
275
|
messages_analyze: chunk.messages_analyze,
|
|
276
|
-
persona_name: chunk.
|
|
276
|
+
persona_name: chunk.channelDisplayName,
|
|
277
277
|
participant_context: buildParticipantContext(context.personaId, state),
|
|
278
278
|
});
|
|
279
279
|
|
|
@@ -286,7 +286,7 @@ export function queueDirectTopicUpdate(
|
|
|
286
286
|
next_step: LLMNextStep.HandleTopicUpdate,
|
|
287
287
|
data: {
|
|
288
288
|
personaId: context.personaId,
|
|
289
|
-
personaDisplayName: context.
|
|
289
|
+
personaDisplayName: context.channelDisplayName,
|
|
290
290
|
isNewItem: false,
|
|
291
291
|
existingItemId: topic.id,
|
|
292
292
|
analyze_from_timestamp: getAnalyzeFromTimestamp(chunk),
|
|
@@ -423,7 +423,7 @@ export function queueTopicUpdate(
|
|
|
423
423
|
new_topic_category: isNewItem ? context.candidateCategory : undefined,
|
|
424
424
|
messages_context: chunk.messages_context,
|
|
425
425
|
messages_analyze: chunk.messages_analyze,
|
|
426
|
-
persona_name: chunk.
|
|
426
|
+
persona_name: chunk.channelDisplayName,
|
|
427
427
|
participant_context: buildParticipantContext(primaryPersonaId, state),
|
|
428
428
|
});
|
|
429
429
|
|
|
@@ -502,7 +502,7 @@ export function queueEventSummary(
|
|
|
502
502
|
|
|
503
503
|
const context: ExtractionContext = {
|
|
504
504
|
personaId,
|
|
505
|
-
|
|
505
|
+
channelDisplayName: persona.display_name,
|
|
506
506
|
messages_context,
|
|
507
507
|
messages_analyze: windowMessages,
|
|
508
508
|
extraction_flag: "e",
|
|
@@ -512,7 +512,7 @@ export function queueEventSummary(
|
|
|
512
512
|
|
|
513
513
|
for (const chunk of chunks) {
|
|
514
514
|
const prompt = buildEventScanPrompt({
|
|
515
|
-
persona_name: chunk.
|
|
515
|
+
persona_name: chunk.channelDisplayName,
|
|
516
516
|
messages_context: chunk.messages_context,
|
|
517
517
|
messages_analyze: chunk.messages_analyze,
|
|
518
518
|
participant_context: buildParticipantContext(personaId, state),
|
|
@@ -528,7 +528,7 @@ export function queueEventSummary(
|
|
|
528
528
|
data: {
|
|
529
529
|
...options,
|
|
530
530
|
personaId: chunk.personaId,
|
|
531
|
-
personaDisplayName: chunk.
|
|
531
|
+
personaDisplayName: chunk.channelDisplayName,
|
|
532
532
|
extraction_flag: "e",
|
|
533
533
|
message_ids_to_mark: chunk.messages_analyze.map(m => m.id),
|
|
534
534
|
},
|
|
@@ -595,7 +595,7 @@ export function queuePersonUpdate(
|
|
|
595
595
|
new_person_relationship: isNewItem ? context.candidateRelationship : undefined,
|
|
596
596
|
messages_context: chunk.messages_context,
|
|
597
597
|
messages_analyze: chunk.messages_analyze,
|
|
598
|
-
persona_name: chunk.
|
|
598
|
+
persona_name: chunk.channelDisplayName,
|
|
599
599
|
participant_context: buildParticipantContext(primaryPersonaIdForUpdate, state),
|
|
600
600
|
known_identifier_types: userIdentifierTypes,
|
|
601
601
|
});
|
|
@@ -609,7 +609,7 @@ export function queuePersonUpdate(
|
|
|
609
609
|
next_step: LLMNextStep.HandlePersonUpdate,
|
|
610
610
|
data: {
|
|
611
611
|
personaId: context.personaId,
|
|
612
|
-
personaDisplayName: context.
|
|
612
|
+
personaDisplayName: context.channelDisplayName,
|
|
613
613
|
roomId: context.roomId,
|
|
614
614
|
isNewItem,
|
|
615
615
|
existingItemId: existingItem?.id,
|
|
@@ -673,7 +673,7 @@ export function queueTargetedPersonUpdate(
|
|
|
673
673
|
extraction_model?: string;
|
|
674
674
|
} = {
|
|
675
675
|
personaId: contextPersonaId,
|
|
676
|
-
|
|
676
|
+
channelDisplayName: displayName,
|
|
677
677
|
messages_context: [],
|
|
678
678
|
messages_analyze: allMessages,
|
|
679
679
|
candidateName: existingItem.name,
|
|
@@ -728,7 +728,7 @@ export function queueTargetedTopicUpdate(
|
|
|
728
728
|
const model = state.getHuman().settings?.default_model;
|
|
729
729
|
const context: ExtractionContext = {
|
|
730
730
|
personaId: contextPersonaId,
|
|
731
|
-
|
|
731
|
+
channelDisplayName: displayName,
|
|
732
732
|
messages_context: [],
|
|
733
733
|
messages_analyze: allMessages,
|
|
734
734
|
roomId,
|
|
@@ -38,7 +38,7 @@ export function queuePersonaTopicRating(
|
|
|
38
38
|
const { chunks } = chunkExtractionContext(
|
|
39
39
|
{
|
|
40
40
|
personaId: context.personaId,
|
|
41
|
-
|
|
41
|
+
channelDisplayName: context.personaDisplayName,
|
|
42
42
|
messages_context: context.messages_context,
|
|
43
43
|
messages_analyze: context.messages_analyze,
|
|
44
44
|
},
|