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
package/src/core/processor.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
LLMNextStep,
|
|
3
|
+
LLMRequestType,
|
|
4
|
+
LLMPriority,
|
|
3
5
|
type LLMRequest,
|
|
4
6
|
type Ei_Interface,
|
|
5
7
|
type PersonaSummary,
|
|
@@ -21,18 +23,23 @@ import {
|
|
|
21
23
|
type ToolDefinition,
|
|
22
24
|
type ToolProvider,
|
|
23
25
|
} from "./types.js";
|
|
26
|
+
import { buildPersonaFromPersonPrompt } from "../prompts/index.js";
|
|
27
|
+
import type { PersonaGenerationResult } from "../prompts/generation/types.js";
|
|
28
|
+
|
|
24
29
|
import type { Storage } from "../storage/interface.js";
|
|
25
30
|
import { remoteSync } from "../storage/remote.js";
|
|
26
31
|
import { yoloMerge } from "../storage/merge.js";
|
|
27
32
|
import { StateManager } from "./state-manager.js";
|
|
28
33
|
import { QueueProcessor } from "./queue-processor.js";
|
|
29
34
|
import { handlers } from "./handlers/index.js";
|
|
35
|
+
import { normalizeRoomMessages } from "./handlers/utils.js";
|
|
30
36
|
import { ContextStatus as ContextStatusEnum } from "./types.js";
|
|
31
37
|
import { registerReadMemoryExecutor, registerFileReadExecutor } from "./tools/index.js";
|
|
32
38
|
import { createReadMemoryExecutor } from "./tools/builtin/read-memory.js";
|
|
33
39
|
import { EI_WELCOME_MESSAGE, EI_PERSONA_DEFINITION } from "../templates/welcome.js";
|
|
34
|
-
import { shouldStartCeremony, startCeremony, handleCeremonyProgress, queueUserDedupRequest } from "./orchestrators/index.js";
|
|
40
|
+
import { shouldStartCeremony, startCeremony, handleCeremonyProgress, queueUserDedupRequest, queueRoomCapture, queuePersonaCapture, checkAndQueueRoomExtraction } from "./orchestrators/index.js";
|
|
35
41
|
import { BUILT_IN_FACTS } from "./constants/built-in-facts.js";
|
|
42
|
+
import { DEFAULT_SEED_TRAITS } from "./constants/seed-traits.js";
|
|
36
43
|
|
|
37
44
|
// Static module imports
|
|
38
45
|
import {
|
|
@@ -103,6 +110,24 @@ import {
|
|
|
103
110
|
clearQueue,
|
|
104
111
|
submitOneShot,
|
|
105
112
|
} from "./queue-manager.js";
|
|
113
|
+
import {
|
|
114
|
+
getRoomList,
|
|
115
|
+
getRoom,
|
|
116
|
+
getRoomMessages,
|
|
117
|
+
getRoomActivePath,
|
|
118
|
+
resolveRoomName,
|
|
119
|
+
createRoom,
|
|
120
|
+
submitHumanRoomMessage,
|
|
121
|
+
recallHumanRoomMessage,
|
|
122
|
+
sendFfaMessage,
|
|
123
|
+
activateRoom,
|
|
124
|
+
selectCYPBranch,
|
|
125
|
+
archiveRoom,
|
|
126
|
+
unarchiveRoom,
|
|
127
|
+
deleteRoom,
|
|
128
|
+
markAllRoomMessagesRead,
|
|
129
|
+
} from "./room-manager.js";
|
|
130
|
+
import type { RoomCreationInput, RoomEntity, RoomMessage, RoomSummary } from "./types.js";
|
|
106
131
|
|
|
107
132
|
const DEFAULT_LOOP_INTERVAL_MS = 100;
|
|
108
133
|
const DEFAULT_OPENCODE_POLLING_MS = 60000;
|
|
@@ -133,6 +158,7 @@ export class Processor {
|
|
|
133
158
|
private pendingConflict: StateConflictData | null = null;
|
|
134
159
|
private storage: Storage | null = null;
|
|
135
160
|
private importAbortController = new AbortController();
|
|
161
|
+
private personaPreviewResolvers = new Map<string, { resolve: (r: PersonaGenerationResult) => void; reject: (e: Error) => void }>();
|
|
136
162
|
|
|
137
163
|
constructor(ei: Ei_Interface) {
|
|
138
164
|
this.interface = ei;
|
|
@@ -601,6 +627,180 @@ export class Processor {
|
|
|
601
627
|
max_calls_per_interaction: 1,
|
|
602
628
|
});
|
|
603
629
|
}
|
|
630
|
+
|
|
631
|
+
// submit_response tool — auto-injected for HandlePersonaResponse and HandleRoomResponse.
|
|
632
|
+
// Not user-configurable; invisible in the tools UI. Terminates the tool loop immediately
|
|
633
|
+
// when called; its arguments become response.parsed.
|
|
634
|
+
if (!this.stateManager.tools_getByName("submit_response")) {
|
|
635
|
+
this.stateManager.tools_add({
|
|
636
|
+
id: crypto.randomUUID(),
|
|
637
|
+
provider_id: "ei",
|
|
638
|
+
name: "submit_response",
|
|
639
|
+
display_name: "Submit Response",
|
|
640
|
+
description: "Submit your response to the conversation. Call this when you are ready to respond — after any research or tool use is complete.",
|
|
641
|
+
input_schema: {
|
|
642
|
+
type: "object",
|
|
643
|
+
properties: {
|
|
644
|
+
should_respond: {
|
|
645
|
+
type: "boolean",
|
|
646
|
+
description: "Whether you are responding (true) or staying silent (false)",
|
|
647
|
+
},
|
|
648
|
+
verbal_response: {
|
|
649
|
+
type: "string",
|
|
650
|
+
description: "What you say out loud. Required when should_respond is true (unless action_response is provided).",
|
|
651
|
+
},
|
|
652
|
+
action_response: {
|
|
653
|
+
type: "string",
|
|
654
|
+
description: "What you do — rendered as italics stage directions. Optional alongside verbal_response.",
|
|
655
|
+
},
|
|
656
|
+
reason: {
|
|
657
|
+
type: "string",
|
|
658
|
+
description: "Why you are staying silent. Only used when should_respond is false.",
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
required: ["should_respond"],
|
|
662
|
+
additionalProperties: false,
|
|
663
|
+
},
|
|
664
|
+
runtime: "any",
|
|
665
|
+
builtin: true,
|
|
666
|
+
enabled: true,
|
|
667
|
+
is_submit: true,
|
|
668
|
+
max_calls_per_interaction: 1,
|
|
669
|
+
created_at: now,
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (!this.stateManager.tools_getByName("submit_heartbeat_check")) {
|
|
674
|
+
this.stateManager.tools_add({
|
|
675
|
+
id: crypto.randomUUID(),
|
|
676
|
+
provider_id: "ei",
|
|
677
|
+
name: "submit_heartbeat_check",
|
|
678
|
+
display_name: "Submit Heartbeat Decision",
|
|
679
|
+
description: "Submit your decision on whether to reach out with a message. Call this when you have decided.",
|
|
680
|
+
input_schema: {
|
|
681
|
+
type: "object",
|
|
682
|
+
properties: {
|
|
683
|
+
should_respond: { type: "boolean", description: "Whether you want to initiate a message" },
|
|
684
|
+
topic: { type: "string", description: "The specific topic you want to discuss (when should_respond is true)" },
|
|
685
|
+
message: { type: "string", description: "Your actual message to them (when should_respond is true)" },
|
|
686
|
+
},
|
|
687
|
+
required: ["should_respond"],
|
|
688
|
+
additionalProperties: false,
|
|
689
|
+
},
|
|
690
|
+
runtime: "any",
|
|
691
|
+
builtin: true,
|
|
692
|
+
enabled: true,
|
|
693
|
+
is_submit: true,
|
|
694
|
+
max_calls_per_interaction: 1,
|
|
695
|
+
created_at: now,
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
if (!this.stateManager.tools_getByName("submit_ei_heartbeat")) {
|
|
700
|
+
this.stateManager.tools_add({
|
|
701
|
+
id: crypto.randomUUID(),
|
|
702
|
+
provider_id: "ei",
|
|
703
|
+
name: "submit_ei_heartbeat",
|
|
704
|
+
display_name: "Submit Ei Heartbeat Decision",
|
|
705
|
+
description: "Submit your choice of item to follow up on, or indicate nothing warrants reaching out.",
|
|
706
|
+
input_schema: {
|
|
707
|
+
type: "object",
|
|
708
|
+
properties: {
|
|
709
|
+
should_respond: { type: "boolean", description: "Whether Ei wants to check in about an item" },
|
|
710
|
+
id: { type: "string", description: "ID of the item you chose (when should_respond is true)" },
|
|
711
|
+
my_response: { type: "string", description: "The check-in message (for Person/Topic/Persona items)" },
|
|
712
|
+
},
|
|
713
|
+
required: ["should_respond"],
|
|
714
|
+
additionalProperties: false,
|
|
715
|
+
},
|
|
716
|
+
runtime: "any",
|
|
717
|
+
builtin: true,
|
|
718
|
+
enabled: true,
|
|
719
|
+
is_submit: true,
|
|
720
|
+
max_calls_per_interaction: 1,
|
|
721
|
+
created_at: now,
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
if (!this.stateManager.tools_getByName("submit_dedup_decisions")) {
|
|
726
|
+
this.stateManager.tools_add({
|
|
727
|
+
id: crypto.randomUUID(),
|
|
728
|
+
provider_id: "ei",
|
|
729
|
+
name: "submit_dedup_decisions",
|
|
730
|
+
display_name: "Submit Dedup Decisions",
|
|
731
|
+
description: "Submit your merge, remove, and add decisions for this cluster of records.",
|
|
732
|
+
input_schema: {
|
|
733
|
+
type: "object",
|
|
734
|
+
properties: {
|
|
735
|
+
update: {
|
|
736
|
+
type: "array",
|
|
737
|
+
description: "Records to update with merged data. Must include at least one (the canonical record).",
|
|
738
|
+
items: {
|
|
739
|
+
type: "object",
|
|
740
|
+
properties: {
|
|
741
|
+
id: { type: "string" },
|
|
742
|
+
type: { type: "string", enum: ["topic", "person", "trait"] },
|
|
743
|
+
name: { type: "string" },
|
|
744
|
+
description: { type: "string" },
|
|
745
|
+
sentiment: { type: "number" },
|
|
746
|
+
strength: { type: "number" },
|
|
747
|
+
confidence: { type: "number" },
|
|
748
|
+
exposure_current: { type: "number" },
|
|
749
|
+
exposure_desired: { type: "number" },
|
|
750
|
+
relationship: { type: "string" },
|
|
751
|
+
category: { type: "string" },
|
|
752
|
+
last_updated: { type: "string" },
|
|
753
|
+
},
|
|
754
|
+
required: ["id", "type", "name", "description"],
|
|
755
|
+
additionalProperties: false,
|
|
756
|
+
},
|
|
757
|
+
},
|
|
758
|
+
remove: {
|
|
759
|
+
type: "array",
|
|
760
|
+
description: "Duplicates to remove. Each must reference its canonical record via replaced_by.",
|
|
761
|
+
items: {
|
|
762
|
+
type: "object",
|
|
763
|
+
properties: {
|
|
764
|
+
to_be_removed: { type: "string" },
|
|
765
|
+
replaced_by: { type: "string" },
|
|
766
|
+
},
|
|
767
|
+
required: ["to_be_removed", "replaced_by"],
|
|
768
|
+
additionalProperties: false,
|
|
769
|
+
},
|
|
770
|
+
},
|
|
771
|
+
add: {
|
|
772
|
+
type: "array",
|
|
773
|
+
description: "New records to create. Only when merging reveals a missing concept.",
|
|
774
|
+
items: {
|
|
775
|
+
type: "object",
|
|
776
|
+
properties: {
|
|
777
|
+
type: { type: "string", enum: ["topic", "person", "trait"] },
|
|
778
|
+
name: { type: "string" },
|
|
779
|
+
description: { type: "string" },
|
|
780
|
+
sentiment: { type: "number" },
|
|
781
|
+
strength: { type: "number" },
|
|
782
|
+
confidence: { type: "number" },
|
|
783
|
+
exposure_current: { type: "number" },
|
|
784
|
+
exposure_desired: { type: "number" },
|
|
785
|
+
relationship: { type: "string" },
|
|
786
|
+
category: { type: "string" },
|
|
787
|
+
},
|
|
788
|
+
required: ["type", "name", "description"],
|
|
789
|
+
additionalProperties: false,
|
|
790
|
+
},
|
|
791
|
+
},
|
|
792
|
+
},
|
|
793
|
+
required: ["update", "remove", "add"],
|
|
794
|
+
additionalProperties: false,
|
|
795
|
+
},
|
|
796
|
+
runtime: "any",
|
|
797
|
+
builtin: true,
|
|
798
|
+
enabled: true,
|
|
799
|
+
is_submit: true,
|
|
800
|
+
max_calls_per_interaction: 1,
|
|
801
|
+
created_at: now,
|
|
802
|
+
});
|
|
803
|
+
}
|
|
604
804
|
}
|
|
605
805
|
|
|
606
806
|
/**
|
|
@@ -837,8 +1037,14 @@ export class Processor {
|
|
|
837
1037
|
this.interface.onMessageProcessing?.(personaId);
|
|
838
1038
|
}
|
|
839
1039
|
|
|
1040
|
+
const roomId = request.data.roomId as string | undefined;
|
|
1041
|
+
if (roomId && (request.next_step === LLMNextStep.HandleRoomResponse || request.next_step === LLMNextStep.HandleRoomJudge)) {
|
|
1042
|
+
this.interface.onRoomMessageProcessing?.(roomId);
|
|
1043
|
+
}
|
|
1044
|
+
|
|
840
1045
|
const toolNextSteps = new Set([
|
|
841
1046
|
LLMNextStep.HandlePersonaResponse,
|
|
1047
|
+
LLMNextStep.HandleRoomResponse,
|
|
842
1048
|
LLMNextStep.HandleHeartbeatCheck,
|
|
843
1049
|
LLMNextStep.HandleEiHeartbeat,
|
|
844
1050
|
LLMNextStep.HandleToolContinuation,
|
|
@@ -866,6 +1072,25 @@ const toolNextSteps = new Set([
|
|
|
866
1072
|
} else if (toolNextSteps.has(request.next_step) && toolPersonaId) {
|
|
867
1073
|
tools = this.stateManager.tools_getForPersona(toolPersonaId, this.isTUI);
|
|
868
1074
|
}
|
|
1075
|
+
|
|
1076
|
+
// Auto-inject each handler's dedicated submit tool — infrastructure, not user-visible.
|
|
1077
|
+
const submitToolByStep: Partial<Record<string, string>> = {
|
|
1078
|
+
[LLMNextStep.HandlePersonaResponse]: "submit_response",
|
|
1079
|
+
[LLMNextStep.HandleRoomResponse]: "submit_response",
|
|
1080
|
+
[LLMNextStep.HandleHeartbeatCheck]: "submit_heartbeat_check",
|
|
1081
|
+
[LLMNextStep.HandleEiHeartbeat]: "submit_ei_heartbeat",
|
|
1082
|
+
[LLMNextStep.HandleDedupCurate]: "submit_dedup_decisions",
|
|
1083
|
+
};
|
|
1084
|
+
const effectiveStep = request.next_step === LLMNextStep.HandleToolContinuation
|
|
1085
|
+
? (request.data.originalNextStep as string | undefined)
|
|
1086
|
+
: request.next_step;
|
|
1087
|
+
const submitToolName = effectiveStep ? submitToolByStep[effectiveStep] : undefined;
|
|
1088
|
+
if (submitToolName) {
|
|
1089
|
+
const submitTool = this.stateManager.tools_getByName(submitToolName);
|
|
1090
|
+
if (submitTool?.enabled && !tools.find(t => t.name === submitToolName)) {
|
|
1091
|
+
tools = [...tools, submitTool];
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
869
1094
|
|
|
870
1095
|
console.log(
|
|
871
1096
|
`[Tools] Dispatch for ${request.next_step} persona=${toolPersonaId ?? "none"}: ${tools.length} tool(s) attached`
|
|
@@ -882,7 +1107,13 @@ const toolNextSteps = new Set([
|
|
|
882
1107
|
{
|
|
883
1108
|
accounts: this.stateManager.getHuman().settings?.accounts,
|
|
884
1109
|
messageFetcher: (pName) => fetchMessagesForLLM(this.stateManager, pName),
|
|
885
|
-
rawMessageFetcher: (
|
|
1110
|
+
rawMessageFetcher: (id) => {
|
|
1111
|
+
if (id.startsWith("room:")) {
|
|
1112
|
+
const roomId = id.slice(5);
|
|
1113
|
+
return normalizeRoomMessages(this.stateManager.getRoomMessages(roomId), this.stateManager);
|
|
1114
|
+
}
|
|
1115
|
+
return this.stateManager.messages_get(id);
|
|
1116
|
+
},
|
|
886
1117
|
tools: tools.length > 0 ? tools : undefined,
|
|
887
1118
|
onEnqueue: (req) => this.stateManager.queue_enqueue(req),
|
|
888
1119
|
onProviderConfigUpdate: (providerId, updates) => {
|
|
@@ -1206,6 +1437,15 @@ const toolNextSteps = new Set([
|
|
|
1206
1437
|
message += ` (attempt ${response.request.attempts}, retrying in ${Math.round(result.retryDelay / 1000)}s)`;
|
|
1207
1438
|
} else if (result.dropped) {
|
|
1208
1439
|
message += " (permanent failure \u2014 request removed)";
|
|
1440
|
+
if (response.request.next_step === LLMNextStep.HandlePersonaPreview) {
|
|
1441
|
+
const guid = response.request.data.guid as string;
|
|
1442
|
+
const entry = this.personaPreviewResolvers.get(guid);
|
|
1443
|
+
if (entry) {
|
|
1444
|
+
this.personaPreviewResolvers.delete(guid);
|
|
1445
|
+
entry.reject(new Error("Persona preview generation failed after max retries"));
|
|
1446
|
+
return;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1209
1449
|
if (response.request.next_step === LLMNextStep.HandleOneShot) {
|
|
1210
1450
|
const guid = response.request.data.guid as string;
|
|
1211
1451
|
this.interface.onOneShotReturned?.(guid, "");
|
|
@@ -1255,6 +1495,88 @@ const toolNextSteps = new Set([
|
|
|
1255
1495
|
this.interface.onOneShotReturned?.(guid, content);
|
|
1256
1496
|
}
|
|
1257
1497
|
|
|
1498
|
+
if (response.request.next_step === LLMNextStep.HandlePersonaPreview) {
|
|
1499
|
+
const guid = response.request.data.guid as string;
|
|
1500
|
+
const loopCounter = (response.request.data.loop_counter as number) ?? 0;
|
|
1501
|
+
const existingPersonaId = response.request.data.personaId as string | undefined;
|
|
1502
|
+
const MAX_PREVIEW_LOOPS = 3;
|
|
1503
|
+
const entry = this.personaPreviewResolvers.get(guid);
|
|
1504
|
+
if (!entry) return;
|
|
1505
|
+
|
|
1506
|
+
if (!response.success || !response.parsed) {
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
let result = response.parsed as import("../prompts/generation/types.js").PersonaGenerationResult;
|
|
1511
|
+
const isComplete =
|
|
1512
|
+
result.traits && result.traits.length >= 3 &&
|
|
1513
|
+
result.topics && result.topics.length >= 3 &&
|
|
1514
|
+
result.long_description && result.short_description;
|
|
1515
|
+
|
|
1516
|
+
const hydrateWithExisting = (r: typeof result): typeof result => {
|
|
1517
|
+
if (!existingPersonaId) return r;
|
|
1518
|
+
const existing = this.stateManager.persona_getById(existingPersonaId);
|
|
1519
|
+
if (!existing) return r;
|
|
1520
|
+
|
|
1521
|
+
const existingTraitNames = new Set(existing.traits.map(t => t.name.toLowerCase().trim()));
|
|
1522
|
+
const newTraits = r.traits.filter(t => !existingTraitNames.has(t.name.toLowerCase().trim()));
|
|
1523
|
+
const mergedTraits = [
|
|
1524
|
+
...existing.traits.map(t => ({
|
|
1525
|
+
name: t.name,
|
|
1526
|
+
description: t.description,
|
|
1527
|
+
strength: t.strength ?? 0.5,
|
|
1528
|
+
sentiment: t.sentiment,
|
|
1529
|
+
})),
|
|
1530
|
+
...newTraits,
|
|
1531
|
+
];
|
|
1532
|
+
|
|
1533
|
+
const existingTopicNames = new Set(existing.topics.map(t => t.name.toLowerCase().trim()));
|
|
1534
|
+
const newTopics = r.topics.filter(t => !existingTopicNames.has(t.name.toLowerCase().trim()));
|
|
1535
|
+
const mergedTopics = [...existing.topics, ...newTopics];
|
|
1536
|
+
|
|
1537
|
+
return {
|
|
1538
|
+
...r,
|
|
1539
|
+
traits: mergedTraits,
|
|
1540
|
+
topics: mergedTopics,
|
|
1541
|
+
previous_long_description: existing.long_description,
|
|
1542
|
+
previous_short_description: existing.short_description,
|
|
1543
|
+
aliases: existing.aliases ?? [],
|
|
1544
|
+
};
|
|
1545
|
+
};
|
|
1546
|
+
|
|
1547
|
+
if (isComplete) {
|
|
1548
|
+
this.personaPreviewResolvers.delete(guid);
|
|
1549
|
+
const hydratedComplete = hydrateWithExisting(result);
|
|
1550
|
+
const seedTraitNamesComplete = new Set(hydratedComplete.traits.map((t: { name: string }) => t.name.toLowerCase().trim()));
|
|
1551
|
+
DEFAULT_SEED_TRAITS
|
|
1552
|
+
.filter(s => !seedTraitNamesComplete.has(s.name.toLowerCase().trim()))
|
|
1553
|
+
.forEach(s => hydratedComplete.traits.push({ name: s.name, description: s.description, sentiment: s.sentiment, strength: s.strength }));
|
|
1554
|
+
entry.resolve(hydratedComplete);
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
if (loopCounter < MAX_PREVIEW_LOOPS) {
|
|
1559
|
+
this.stateManager.queue_enqueue({
|
|
1560
|
+
type: LLMRequestType.JSON,
|
|
1561
|
+
priority: LLMPriority.High,
|
|
1562
|
+
system: response.request.system,
|
|
1563
|
+
user: response.request.user,
|
|
1564
|
+
next_step: LLMNextStep.HandlePersonaPreview,
|
|
1565
|
+
model: response.request.model,
|
|
1566
|
+
data: { guid, loop_counter: loopCounter + 1, personaId: existingPersonaId },
|
|
1567
|
+
});
|
|
1568
|
+
return;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
this.personaPreviewResolvers.delete(guid);
|
|
1572
|
+
const hydratedFallback = hydrateWithExisting(result);
|
|
1573
|
+
const seedTraitNamesFallback = new Set(hydratedFallback.traits.map((t: { name: string }) => t.name.toLowerCase().trim()));
|
|
1574
|
+
DEFAULT_SEED_TRAITS
|
|
1575
|
+
.filter(s => !seedTraitNamesFallback.has(s.name.toLowerCase().trim()))
|
|
1576
|
+
.forEach(s => hydratedFallback.traits.push({ name: s.name, description: s.description, sentiment: s.sentiment, strength: s.strength }));
|
|
1577
|
+
entry.resolve(hydratedFallback);
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1258
1580
|
if (response.request.next_step === LLMNextStep.HandlePersonaGeneration) {
|
|
1259
1581
|
const personaId = response.request.data.personaId as string;
|
|
1260
1582
|
if (personaId) {
|
|
@@ -1307,6 +1629,27 @@ const toolNextSteps = new Set([
|
|
|
1307
1629
|
this.interface.onHumanUpdated?.();
|
|
1308
1630
|
}
|
|
1309
1631
|
|
|
1632
|
+
if (response.request.next_step === LLMNextStep.HandleDedupCurate) {
|
|
1633
|
+
this.interface.onHumanUpdated?.();
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
const isRoomResponse =
|
|
1637
|
+
response.request.next_step === LLMNextStep.HandleRoomResponse ||
|
|
1638
|
+
(response.request.next_step === LLMNextStep.HandleToolContinuation &&
|
|
1639
|
+
response.request.data.originalNextStep === LLMNextStep.HandleRoomResponse);
|
|
1640
|
+
if (isRoomResponse) {
|
|
1641
|
+
const roomId = response.request.data.roomId as string;
|
|
1642
|
+
if (roomId) {
|
|
1643
|
+
this.interface.onRoomMessageAdded?.(roomId);
|
|
1644
|
+
checkAndQueueRoomExtraction(this.stateManager, roomId);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
if (response.request.next_step === LLMNextStep.HandleRoomJudge) {
|
|
1649
|
+
const roomId = response.request.data.roomId as string;
|
|
1650
|
+
if (roomId) this.interface.onRoomUpdated?.(roomId);
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1310
1653
|
if (typeof response.request.data.ceremony_progress === "number") {
|
|
1311
1654
|
handleCeremonyProgress(this.stateManager, response.request.data.ceremony_progress);
|
|
1312
1655
|
}
|
|
@@ -1377,6 +1720,11 @@ const toolNextSteps = new Set([
|
|
|
1377
1720
|
if (ok) this.interface.onPersonaUpdated?.(personaId);
|
|
1378
1721
|
}
|
|
1379
1722
|
|
|
1723
|
+
async updateRoom(roomId: string, updates: Partial<RoomEntity>): Promise<void> {
|
|
1724
|
+
const ok = this.stateManager.updateRoom(roomId, updates);
|
|
1725
|
+
if (ok) this.interface.onRoomUpdated?.(roomId);
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1380
1728
|
async getGroupList(): Promise<string[]> {
|
|
1381
1729
|
return getGroupList(this.stateManager);
|
|
1382
1730
|
}
|
|
@@ -1408,7 +1756,7 @@ const toolNextSteps = new Set([
|
|
|
1408
1756
|
);
|
|
1409
1757
|
}
|
|
1410
1758
|
|
|
1411
|
-
async sendMessage(personaId: string, content: string): Promise<void> {
|
|
1759
|
+
async sendMessage(personaId: string, content: string | null, silenceReason?: string): Promise<void> {
|
|
1412
1760
|
return sendMessage(
|
|
1413
1761
|
this.stateManager,
|
|
1414
1762
|
this.queueProcessor,
|
|
@@ -1419,7 +1767,8 @@ const toolNextSteps = new Set([
|
|
|
1419
1767
|
(id) => getModelForPersona(this.stateManager, id),
|
|
1420
1768
|
(err) => this.interface.onError?.(err),
|
|
1421
1769
|
(id) => this.interface.onMessageAdded?.(id),
|
|
1422
|
-
(id) => this.interface.onMessageQueued?.(id)
|
|
1770
|
+
(id) => this.interface.onMessageQueued?.(id),
|
|
1771
|
+
silenceReason
|
|
1423
1772
|
);
|
|
1424
1773
|
}
|
|
1425
1774
|
|
|
@@ -1590,6 +1939,14 @@ const toolNextSteps = new Set([
|
|
|
1590
1939
|
queueUserDedupRequest(this.stateManager, itemType, entityIds);
|
|
1591
1940
|
}
|
|
1592
1941
|
|
|
1942
|
+
capturePersona(personaId: string): void {
|
|
1943
|
+
queuePersonaCapture(this.stateManager, personaId);
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
captureRoom(roomId: string): void {
|
|
1947
|
+
queueRoomCapture(this.stateManager, roomId);
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1593
1950
|
async submitOneShot(guid: string, systemPrompt: string, userPrompt: string): Promise<void> {
|
|
1594
1951
|
return submitOneShot(
|
|
1595
1952
|
this.stateManager,
|
|
@@ -1600,6 +1957,45 @@ const toolNextSteps = new Set([
|
|
|
1600
1957
|
);
|
|
1601
1958
|
}
|
|
1602
1959
|
|
|
1960
|
+
async generatePersonaPreview(
|
|
1961
|
+
name: string,
|
|
1962
|
+
description: string,
|
|
1963
|
+
relationship?: string,
|
|
1964
|
+
personaId?: string
|
|
1965
|
+
): Promise<PersonaGenerationResult> {
|
|
1966
|
+
let existing_trait_names: string[] | undefined;
|
|
1967
|
+
let existing_topic_names: string[] | undefined;
|
|
1968
|
+
|
|
1969
|
+
if (personaId) {
|
|
1970
|
+
const existing = this.stateManager.persona_getById(personaId);
|
|
1971
|
+
if (existing) {
|
|
1972
|
+
existing_trait_names = existing.traits.map((t) => t.name);
|
|
1973
|
+
existing_topic_names = existing.topics.map((t) => t.name);
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
const prompt = buildPersonaFromPersonPrompt({
|
|
1978
|
+
name,
|
|
1979
|
+
description,
|
|
1980
|
+
relationship,
|
|
1981
|
+
existing_trait_names,
|
|
1982
|
+
existing_topic_names,
|
|
1983
|
+
});
|
|
1984
|
+
const guid = crypto.randomUUID();
|
|
1985
|
+
return new Promise<PersonaGenerationResult>((resolve, reject) => {
|
|
1986
|
+
this.personaPreviewResolvers.set(guid, { resolve, reject });
|
|
1987
|
+
this.stateManager.queue_enqueue({
|
|
1988
|
+
type: LLMRequestType.JSON,
|
|
1989
|
+
priority: LLMPriority.High,
|
|
1990
|
+
system: prompt.system,
|
|
1991
|
+
user: prompt.user,
|
|
1992
|
+
next_step: LLMNextStep.HandlePersonaPreview,
|
|
1993
|
+
model: getOneshotModel(this.stateManager),
|
|
1994
|
+
data: { guid, loop_counter: 0, personaId },
|
|
1995
|
+
});
|
|
1996
|
+
});
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1603
1999
|
// ==========================================================================
|
|
1604
2000
|
// TOOL API
|
|
1605
2001
|
// ==========================================================================
|
|
@@ -1662,6 +2058,126 @@ const toolNextSteps = new Set([
|
|
|
1662
2058
|
return result;
|
|
1663
2059
|
}
|
|
1664
2060
|
|
|
2061
|
+
// ==========================================================================
|
|
2062
|
+
// ROOM API
|
|
2063
|
+
// ==========================================================================
|
|
2064
|
+
|
|
2065
|
+
getRoomList(includeArchived = false): RoomSummary[] {
|
|
2066
|
+
return getRoomList(this.stateManager, includeArchived);
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
getRoom(roomId: string): RoomEntity | null {
|
|
2070
|
+
return getRoom(this.stateManager, roomId);
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
getRoomMessages(roomId: string): RoomMessage[] {
|
|
2074
|
+
return getRoomMessages(this.stateManager, roomId);
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
getRoomActivePath(roomId: string): RoomMessage[] {
|
|
2078
|
+
return getRoomActivePath(this.stateManager, roomId);
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
resolveRoomName(nameOrAlias: string): string | null {
|
|
2082
|
+
return resolveRoomName(this.stateManager, nameOrAlias);
|
|
2083
|
+
}
|
|
2084
|
+
|
|
2085
|
+
async createRoom(input: RoomCreationInput): Promise<string> {
|
|
2086
|
+
const id = await createRoom(
|
|
2087
|
+
this.stateManager,
|
|
2088
|
+
input,
|
|
2089
|
+
this.isTUI,
|
|
2090
|
+
(err) => this.interface.onError?.(err),
|
|
2091
|
+
(id) => this.interface.onRoomMessageAdded?.(id),
|
|
2092
|
+
(id) => this.interface.onRoomMessageQueued?.(id)
|
|
2093
|
+
);
|
|
2094
|
+
if (id) this.interface.onRoomAdded?.();
|
|
2095
|
+
return id;
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
submitHumanRoomMessage(
|
|
2099
|
+
roomId: string,
|
|
2100
|
+
content: string | null,
|
|
2101
|
+
silenceReason?: string
|
|
2102
|
+
): string | null {
|
|
2103
|
+
return submitHumanRoomMessage(
|
|
2104
|
+
this.stateManager,
|
|
2105
|
+
roomId,
|
|
2106
|
+
content,
|
|
2107
|
+
silenceReason,
|
|
2108
|
+
(err) => this.interface.onError?.(err),
|
|
2109
|
+
(id) => this.interface.onRoomMessageAdded?.(id)
|
|
2110
|
+
);
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
recallHumanRoomMessage(roomId: string): boolean {
|
|
2114
|
+
return recallHumanRoomMessage(
|
|
2115
|
+
this.stateManager,
|
|
2116
|
+
roomId,
|
|
2117
|
+
(id) => this.interface.onRoomUpdated?.(id)
|
|
2118
|
+
);
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
async activateRoom(roomId: string): Promise<void> {
|
|
2122
|
+
return activateRoom(
|
|
2123
|
+
this.stateManager,
|
|
2124
|
+
roomId,
|
|
2125
|
+
this.isTUI,
|
|
2126
|
+
(err) => this.interface.onError?.(err),
|
|
2127
|
+
(id) => this.interface.onRoomUpdated?.(id),
|
|
2128
|
+
(id) => this.interface.onRoomMessageQueued?.(id)
|
|
2129
|
+
);
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
async sendFfaMessage(
|
|
2133
|
+
roomId: string,
|
|
2134
|
+
content: string | null,
|
|
2135
|
+
silenceReason?: string
|
|
2136
|
+
): Promise<void> {
|
|
2137
|
+
return sendFfaMessage(
|
|
2138
|
+
this.stateManager,
|
|
2139
|
+
roomId,
|
|
2140
|
+
content,
|
|
2141
|
+
silenceReason,
|
|
2142
|
+
this.isTUI,
|
|
2143
|
+
(err) => this.interface.onError?.(err),
|
|
2144
|
+
(id) => this.interface.onRoomUpdated?.(id),
|
|
2145
|
+
(id) => this.interface.onRoomMessageAdded?.(id),
|
|
2146
|
+
(id) => this.interface.onRoomMessageQueued?.(id)
|
|
2147
|
+
);
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
async selectCYPBranch(roomId: string, messageId: string): Promise<void> {
|
|
2151
|
+
return selectCYPBranch(
|
|
2152
|
+
this.stateManager,
|
|
2153
|
+
roomId,
|
|
2154
|
+
messageId,
|
|
2155
|
+
this.isTUI,
|
|
2156
|
+
(err) => this.interface.onError?.(err),
|
|
2157
|
+
(id) => this.interface.onRoomUpdated?.(id),
|
|
2158
|
+
(id) => this.interface.onRoomMessageQueued?.(id)
|
|
2159
|
+
);
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
async archiveRoom(roomId: string): Promise<void> {
|
|
2163
|
+
const ok = archiveRoom(this.stateManager, roomId);
|
|
2164
|
+
if (ok) this.interface.onRoomRemoved?.();
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
async unarchiveRoom(roomId: string): Promise<void> {
|
|
2168
|
+
const ok = unarchiveRoom(this.stateManager, roomId);
|
|
2169
|
+
if (ok) this.interface.onRoomAdded?.();
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
async deleteRoom(roomId: string): Promise<void> {
|
|
2173
|
+
const ok = deleteRoom(this.stateManager, roomId);
|
|
2174
|
+
if (ok) this.interface.onRoomRemoved?.();
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
async markAllRoomMessagesRead(roomId: string): Promise<number> {
|
|
2178
|
+
return markAllRoomMessagesRead(this.stateManager, roomId);
|
|
2179
|
+
}
|
|
2180
|
+
|
|
1665
2181
|
// ==========================================================================
|
|
1666
2182
|
// DEBUG / TESTING UTILITIES
|
|
1667
2183
|
// ==========================================================================
|