@elizaos/client 1.5.5-alpha.10
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/LICENSE +21 -0
- package/README.md +350 -0
- package/dist/assets/empty-module-CLMscLYw.js +1 -0
- package/dist/assets/main-BBZ_3lkn.css +5999 -0
- package/dist/assets/main-C5zNUkXH.js +7 -0
- package/dist/assets/main-Dz64ENQg.js +614 -0
- package/dist/assets/react-vendor-DM5m98rr.js +545 -0
- package/dist/assets/ui-vendor-BQCqNqg0.js +1 -0
- package/dist/elizaos-avatar.png +0 -0
- package/dist/elizaos-icon.png +0 -0
- package/dist/elizaos-logo-light.png +0 -0
- package/dist/elizaos.webp +0 -0
- package/dist/favicon.ico +0 -0
- package/dist/images/agents/agent1.png +0 -0
- package/dist/images/agents/agent2.png +0 -0
- package/dist/images/agents/agent3.png +0 -0
- package/dist/images/agents/agent4.png +0 -0
- package/dist/images/agents/agent5.png +0 -0
- package/dist/index.html +14 -0
- package/index.html +24 -0
- package/package.json +159 -0
- package/postcss.config.js +3 -0
- package/public/elizaos-avatar.png +0 -0
- package/public/elizaos-icon.png +0 -0
- package/public/elizaos-logo-light.png +0 -0
- package/public/elizaos.webp +0 -0
- package/public/favicon.ico +0 -0
- package/public/images/agents/agent1.png +0 -0
- package/public/images/agents/agent2.png +0 -0
- package/public/images/agents/agent3.png +0 -0
- package/public/images/agents/agent4.png +0 -0
- package/public/images/agents/agent5.png +0 -0
- package/src/App.tsx +222 -0
- package/src/components/AgentDetailsPanel.tsx +147 -0
- package/src/components/ChatInputArea.tsx +196 -0
- package/src/components/ChatMessageListComponent.tsx +139 -0
- package/src/components/actionTool.tsx +186 -0
- package/src/components/add-agent-card.tsx +77 -0
- package/src/components/agent-action-viewer.tsx +816 -0
- package/src/components/agent-avatar-stack.tsx +121 -0
- package/src/components/agent-card.cy.tsx +259 -0
- package/src/components/agent-card.tsx +177 -0
- package/src/components/agent-creator.tsx +142 -0
- package/src/components/agent-log-viewer.tsx +645 -0
- package/src/components/agent-memory-edit-overlay.tsx +461 -0
- package/src/components/agent-memory-viewer.tsx +504 -0
- package/src/components/agent-settings.tsx +270 -0
- package/src/components/agent-sidebar.tsx +178 -0
- package/src/components/api-key-dialog.tsx +113 -0
- package/src/components/app-sidebar.tsx +685 -0
- package/src/components/array-input.tsx +116 -0
- package/src/components/audio-recorder.tsx +292 -0
- package/src/components/avatar-panel.tsx +141 -0
- package/src/components/character-form.tsx +1138 -0
- package/src/components/chat.tsx +1813 -0
- package/src/components/combobox.tsx +187 -0
- package/src/components/confirmation-dialog.tsx +59 -0
- package/src/components/connection-error-banner.tsx +101 -0
- package/src/components/connection-status.cy.tsx +73 -0
- package/src/components/connection-status.tsx +155 -0
- package/src/components/copy-button.tsx +35 -0
- package/src/components/delete-button.tsx +24 -0
- package/src/components/env-settings.tsx +261 -0
- package/src/components/group-card.tsx +160 -0
- package/src/components/group-panel.tsx +543 -0
- package/src/components/input-copy.tsx +21 -0
- package/src/components/logs-page.tsx +41 -0
- package/src/components/media-content.tsx +385 -0
- package/src/components/memory-graph.tsx +170 -0
- package/src/components/missing-secrets-dialog.tsx +72 -0
- package/src/components/onboarding-tour.tsx +247 -0
- package/src/components/page-title.tsx +8 -0
- package/src/components/plugins-panel.tsx +383 -0
- package/src/components/profile-card.tsx +66 -0
- package/src/components/profile-overlay.tsx +283 -0
- package/src/components/retry-button.tsx +28 -0
- package/src/components/secret-panel.tsx +1505 -0
- package/src/components/server-management.tsx +264 -0
- package/src/components/split-button.tsx +148 -0
- package/src/components/stop-agent-button.tsx +99 -0
- package/src/components/ui/alert-dialog.cy.tsx +333 -0
- package/src/components/ui/alert-dialog.tsx +115 -0
- package/src/components/ui/alert.tsx +49 -0
- package/src/components/ui/avatar.cy.tsx +180 -0
- package/src/components/ui/avatar.tsx +57 -0
- package/src/components/ui/badge.cy.tsx +146 -0
- package/src/components/ui/badge.tsx +43 -0
- package/src/components/ui/button.cy.tsx +177 -0
- package/src/components/ui/button.tsx +56 -0
- package/src/components/ui/card.cy.tsx +160 -0
- package/src/components/ui/card.tsx +73 -0
- package/src/components/ui/chat/animated-markdown.tsx +59 -0
- package/src/components/ui/chat/chat-bubble.tsx +178 -0
- package/src/components/ui/chat/chat-container.tsx +51 -0
- package/src/components/ui/chat/chat-input.cy.tsx +169 -0
- package/src/components/ui/chat/chat-input.tsx +47 -0
- package/src/components/ui/chat/chat-message-list.tsx +61 -0
- package/src/components/ui/chat/chat-tts-button.tsx +199 -0
- package/src/components/ui/chat/code-block.tsx +79 -0
- package/src/components/ui/chat/expandable-chat.tsx +131 -0
- package/src/components/ui/chat/hooks/useAutoScroll.ts +86 -0
- package/src/components/ui/chat/markdown.tsx +209 -0
- package/src/components/ui/chat/message-loading.tsx +48 -0
- package/src/components/ui/checkbox.cy.tsx +170 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/collapsible.cy.tsx +283 -0
- package/src/components/ui/collapsible.tsx +9 -0
- package/src/components/ui/command.cy.tsx +313 -0
- package/src/components/ui/command.tsx +143 -0
- package/src/components/ui/dialog.cy.tsx +279 -0
- package/src/components/ui/dialog.tsx +104 -0
- package/src/components/ui/dropdown-menu.cy.tsx +273 -0
- package/src/components/ui/dropdown-menu.tsx +281 -0
- package/src/components/ui/input.cy.tsx +82 -0
- package/src/components/ui/input.tsx +27 -0
- package/src/components/ui/label.cy.tsx +157 -0
- package/src/components/ui/label.tsx +19 -0
- package/src/components/ui/resizable.tsx +42 -0
- package/src/components/ui/scroll-area.cy.tsx +242 -0
- package/src/components/ui/scroll-area.tsx +46 -0
- package/src/components/ui/select.cy.tsx +277 -0
- package/src/components/ui/select.tsx +155 -0
- package/src/components/ui/separator.cy.tsx +145 -0
- package/src/components/ui/separator.tsx +29 -0
- package/src/components/ui/sheet.cy.tsx +324 -0
- package/src/components/ui/sheet.tsx +119 -0
- package/src/components/ui/sidebar.tsx +734 -0
- package/src/components/ui/skeleton.cy.tsx +149 -0
- package/src/components/ui/skeleton.tsx +17 -0
- package/src/components/ui/split-button.cy.tsx +274 -0
- package/src/components/ui/split-button.tsx +112 -0
- package/src/components/ui/switch.tsx +28 -0
- package/src/components/ui/tabs.cy.tsx +271 -0
- package/src/components/ui/tabs.tsx +53 -0
- package/src/components/ui/textarea.cy.tsx +136 -0
- package/src/components/ui/textarea.tsx +26 -0
- package/src/components/ui/toast.cy.tsx +209 -0
- package/src/components/ui/toast.tsx +126 -0
- package/src/components/ui/toaster.tsx +29 -0
- package/src/components/ui/tooltip.cy.tsx +244 -0
- package/src/components/ui/tooltip.tsx +30 -0
- package/src/config/agent-templates.ts +349 -0
- package/src/config/voice-models.ts +181 -0
- package/src/constants.ts +23 -0
- package/src/context/AuthContext.tsx +44 -0
- package/src/context/ConnectionContext.tsx +194 -0
- package/src/entry.tsx +9 -0
- package/src/hooks/__tests__/use-agent-tab-state.test.ts +137 -0
- package/src/hooks/__tests__/use-agent-update.test.tsx +250 -0
- package/src/hooks/__tests__/use-character-convert.test.ts +102 -0
- package/src/hooks/__tests__/use-panel-width-state.test.ts +243 -0
- package/src/hooks/__tests__/use-sidebar-state.test.ts +117 -0
- package/src/hooks/use-agent-management.ts +130 -0
- package/src/hooks/use-agent-tab-state.ts +74 -0
- package/src/hooks/use-agent-update.ts +469 -0
- package/src/hooks/use-character-convert.ts +138 -0
- package/src/hooks/use-confirmation.ts +55 -0
- package/src/hooks/use-delete-agent.ts +123 -0
- package/src/hooks/use-dm-channels.ts +198 -0
- package/src/hooks/use-elevenlabs-voices.ts +83 -0
- package/src/hooks/use-file-upload.ts +224 -0
- package/src/hooks/use-mobile.tsx +19 -0
- package/src/hooks/use-onboarding.tsx +49 -0
- package/src/hooks/use-panel-width-state.ts +147 -0
- package/src/hooks/use-partial-update.ts +288 -0
- package/src/hooks/use-plugin-details.ts +462 -0
- package/src/hooks/use-plugins.ts +119 -0
- package/src/hooks/use-query-hooks.ts +1263 -0
- package/src/hooks/use-server-agents.ts +62 -0
- package/src/hooks/use-server-version.tsx +47 -0
- package/src/hooks/use-sidebar-state.ts +50 -0
- package/src/hooks/use-socket-chat.ts +264 -0
- package/src/hooks/use-toast.ts +260 -0
- package/src/hooks/use-version.tsx +64 -0
- package/src/index.css +146 -0
- package/src/lib/api-client-config.ts +53 -0
- package/src/lib/api-type-mappers.ts +196 -0
- package/src/lib/export-utils.ts +123 -0
- package/src/lib/logger.ts +19 -0
- package/src/lib/media-utils.ts +170 -0
- package/src/lib/pca.test.ts +17 -0
- package/src/lib/pca.ts +52 -0
- package/src/lib/socketio-manager.ts +664 -0
- package/src/lib/utils.ts +168 -0
- package/src/main.tsx +16 -0
- package/src/mocks/empty-module.ts +12 -0
- package/src/mocks/node-module.ts +57 -0
- package/src/polyfills.ts +37 -0
- package/src/routes/agent-detail.tsx +30 -0
- package/src/routes/agent-list.tsx +27 -0
- package/src/routes/agent-settings.tsx +48 -0
- package/src/routes/character-detail.tsx +52 -0
- package/src/routes/character-form.tsx +79 -0
- package/src/routes/character-list.tsx +38 -0
- package/src/routes/chat.tsx +128 -0
- package/src/routes/createAgent.tsx +13 -0
- package/src/routes/group-new.tsx +50 -0
- package/src/routes/group.tsx +29 -0
- package/src/routes/home.tsx +218 -0
- package/src/routes/not-found.tsx +71 -0
- package/src/test/setup.ts +154 -0
- package/src/types/crypto-browserify.d.ts +4 -0
- package/src/types/index.ts +13 -0
- package/src/types/rooms.ts +8 -0
- package/src/types.ts +84 -0
- package/src/vite-env.d.ts +40 -0
- package/tailwind.config.ts +90 -0
- package/tsconfig.json +10 -0
- package/vite.config.ts +102 -0
|
@@ -0,0 +1,664 @@
|
|
|
1
|
+
import { USER_NAME } from '@/constants';
|
|
2
|
+
import { SOCKET_MESSAGE_TYPE } from '@elizaos/core';
|
|
3
|
+
import { Evt } from 'evt';
|
|
4
|
+
import { io, type Socket } from 'socket.io-client';
|
|
5
|
+
import { randomUUID } from './utils';
|
|
6
|
+
import clientLogger from './logger';
|
|
7
|
+
|
|
8
|
+
// Define types for the events
|
|
9
|
+
export type MessageBroadcastData = {
|
|
10
|
+
senderId: string;
|
|
11
|
+
senderName: string;
|
|
12
|
+
text: string;
|
|
13
|
+
channelId: string;
|
|
14
|
+
roomId?: string; // Deprecated - for backward compatibility only
|
|
15
|
+
createdAt: number;
|
|
16
|
+
source: string;
|
|
17
|
+
name: string; // Required for ContentWithUser compatibility
|
|
18
|
+
attachments?: any[];
|
|
19
|
+
thought?: string; // Agent's thought process
|
|
20
|
+
actions?: string[]; // Actions taken by the agent
|
|
21
|
+
prompt?: string; // The LLM prompt used to generate this message
|
|
22
|
+
[key: string]: any;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type MessageCompleteData = {
|
|
26
|
+
channelId: string;
|
|
27
|
+
roomId?: string; // Deprecated - for backward compatibility only
|
|
28
|
+
[key: string]: any;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Define type for control messages
|
|
32
|
+
export type ControlMessageData = {
|
|
33
|
+
action: 'enable_input' | 'disable_input';
|
|
34
|
+
target?: string;
|
|
35
|
+
channelId: string;
|
|
36
|
+
roomId?: string; // Deprecated - for backward compatibility only
|
|
37
|
+
[key: string]: any;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Define type for message deletion events
|
|
41
|
+
export type MessageDeletedData = {
|
|
42
|
+
messageId: string;
|
|
43
|
+
channelId: string;
|
|
44
|
+
roomId?: string; // Deprecated - for backward compatibility only
|
|
45
|
+
[key: string]: any;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Define type for channel cleared events
|
|
49
|
+
export type ChannelClearedData = {
|
|
50
|
+
channelId: string;
|
|
51
|
+
roomId?: string; // Deprecated - for backward compatibility only
|
|
52
|
+
[key: string]: any;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Define type for channel deleted events
|
|
56
|
+
export type ChannelDeletedData = {
|
|
57
|
+
channelId: string;
|
|
58
|
+
roomId?: string; // Deprecated - for backward compatibility only
|
|
59
|
+
[key: string]: any;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Define type for log stream messages
|
|
63
|
+
export type LogStreamData = {
|
|
64
|
+
level: number;
|
|
65
|
+
time: number;
|
|
66
|
+
msg: string;
|
|
67
|
+
agentId?: string;
|
|
68
|
+
agentName?: string;
|
|
69
|
+
channelId?: string;
|
|
70
|
+
roomId?: string; // Deprecated - for backward compatibility only
|
|
71
|
+
[key: string]: string | number | boolean | null | undefined;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// A simple class that provides EventEmitter-like interface using Evt internally
|
|
75
|
+
class EventAdapter {
|
|
76
|
+
private events: Record<string, Evt<any>> = {};
|
|
77
|
+
|
|
78
|
+
constructor() {
|
|
79
|
+
// Initialize common events
|
|
80
|
+
this.events.messageBroadcast = Evt.create<MessageBroadcastData>();
|
|
81
|
+
this.events.messageComplete = Evt.create<MessageCompleteData>();
|
|
82
|
+
this.events.controlMessage = Evt.create<ControlMessageData>();
|
|
83
|
+
this.events.messageDeleted = Evt.create<MessageDeletedData>();
|
|
84
|
+
this.events.channelCleared = Evt.create<ChannelClearedData>();
|
|
85
|
+
this.events.channelDeleted = Evt.create<ChannelDeletedData>();
|
|
86
|
+
this.events.logStream = Evt.create<LogStreamData>();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
on(eventName: string, listener: (...args: any[]) => void) {
|
|
90
|
+
if (!this.events[eventName]) {
|
|
91
|
+
this.events[eventName] = Evt.create();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.events[eventName].attach(listener);
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
off(eventName: string, listener: (...args: any[]) => void) {
|
|
99
|
+
if (this.events[eventName]) {
|
|
100
|
+
const handlers = this.events[eventName].getHandlers();
|
|
101
|
+
for (const handler of handlers) {
|
|
102
|
+
if (handler.callback === listener) {
|
|
103
|
+
handler.detach();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
emit(eventName: string, ...args: any[]) {
|
|
111
|
+
if (this.events[eventName]) {
|
|
112
|
+
this.events[eventName].post(args.length === 1 ? args[0] : args);
|
|
113
|
+
}
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
once(eventName: string, listener: (...args: any[]) => void) {
|
|
118
|
+
if (!this.events[eventName]) {
|
|
119
|
+
this.events[eventName] = Evt.create();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.events[eventName].attachOnce(listener);
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// For checking if EventEmitter has listeners
|
|
127
|
+
listenerCount(eventName: string): number {
|
|
128
|
+
if (!this.events[eventName]) return 0;
|
|
129
|
+
return this.events[eventName].getHandlers().length;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Used only for internal access to the Evt instances
|
|
133
|
+
_getEvt(eventName: string): Evt<any> | undefined {
|
|
134
|
+
return this.events[eventName];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* SocketIOManager handles real-time communication between the client and server
|
|
140
|
+
* using Socket.io. It maintains a single connection to the server and allows
|
|
141
|
+
* joining and messaging in multiple rooms.
|
|
142
|
+
*/
|
|
143
|
+
export class SocketIOManager extends EventAdapter {
|
|
144
|
+
private static instance: SocketIOManager | null = null;
|
|
145
|
+
private socket: Socket | null = null;
|
|
146
|
+
private isConnected = false;
|
|
147
|
+
private connectPromise: Promise<void> | null = null;
|
|
148
|
+
private resolveConnect: (() => void) | null = null;
|
|
149
|
+
private activeChannelIds: Set<string> = new Set();
|
|
150
|
+
private clientEntityId: string | null = null;
|
|
151
|
+
private logStreamSubscribed: boolean = false;
|
|
152
|
+
|
|
153
|
+
// Public accessor for EVT instances (for advanced usage)
|
|
154
|
+
public get evtMessageBroadcast() {
|
|
155
|
+
return this._getEvt('messageBroadcast') as Evt<MessageBroadcastData>;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
public get evtMessageComplete() {
|
|
159
|
+
return this._getEvt('messageComplete') as Evt<MessageCompleteData>;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
public get evtControlMessage() {
|
|
163
|
+
return this._getEvt('controlMessage') as Evt<ControlMessageData>;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
public get evtMessageDeleted() {
|
|
167
|
+
return this._getEvt('messageDeleted') as Evt<MessageDeletedData>;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public get evtChannelCleared() {
|
|
171
|
+
return this._getEvt('channelCleared') as Evt<ChannelClearedData>;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
public get evtChannelDeleted() {
|
|
175
|
+
return this._getEvt('channelDeleted') as Evt<ChannelDeletedData>;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
public get evtLogStream() {
|
|
179
|
+
return this._getEvt('logStream') as Evt<LogStreamData>;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private constructor() {
|
|
183
|
+
super();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
public static getInstance(): SocketIOManager {
|
|
187
|
+
if (!SocketIOManager.instance) {
|
|
188
|
+
SocketIOManager.instance = new SocketIOManager();
|
|
189
|
+
}
|
|
190
|
+
return SocketIOManager.instance;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
public static isConnected(): boolean {
|
|
194
|
+
return SocketIOManager.instance?.isConnected || false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
public isChannelActive(channelId: string): boolean {
|
|
198
|
+
return this.activeChannelIds.has(channelId);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Initialize the Socket.io connection to the server
|
|
203
|
+
* @param clientEntityId The client entity ID (central user ID)
|
|
204
|
+
*/
|
|
205
|
+
public initialize(clientEntityId: string): void {
|
|
206
|
+
this.clientEntityId = clientEntityId;
|
|
207
|
+
|
|
208
|
+
if (this.socket) {
|
|
209
|
+
clientLogger.debug('[SocketIO] Socket already initialized');
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Create a single socket connection
|
|
214
|
+
const fullURL = window.location.origin + '/';
|
|
215
|
+
clientLogger.info('connecting to', fullURL);
|
|
216
|
+
this.socket = io(fullURL, {
|
|
217
|
+
autoConnect: true,
|
|
218
|
+
reconnection: true,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Set up connection promise for async operations that depend on connection
|
|
222
|
+
this.connectPromise = new Promise<void>((resolve) => {
|
|
223
|
+
this.resolveConnect = resolve;
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
this.socket.on('connect', () => {
|
|
227
|
+
clientLogger.info('[SocketIO] Connected to server');
|
|
228
|
+
this.isConnected = true;
|
|
229
|
+
this.resolveConnect?.();
|
|
230
|
+
|
|
231
|
+
// Add debug listener for all incoming events
|
|
232
|
+
if (process.env.NODE_ENV === 'development' && this.socket) {
|
|
233
|
+
this.socket.onAny((event, ...args) => {
|
|
234
|
+
clientLogger.debug(`[SocketIO DEBUG] Received event '${event}':`, args);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
this.emit('connect');
|
|
239
|
+
|
|
240
|
+
// CRITICAL: Ensure this loop remains commented out or removed.
|
|
241
|
+
// this.activeChannelIds.forEach((channelId) => {
|
|
242
|
+
// clientLogger.info(`[SocketIO] 'connect' event: Attempting to re-join active channel ${channelId} (THIS SHOULD NOT HAPPEN AUTOMATICALLY)`);
|
|
243
|
+
// this.joinChannel(channelId);
|
|
244
|
+
// });
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
this.socket.on('unauthorized', (reason: string) => {
|
|
248
|
+
this.emit('unauthorized', reason);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
this.socket.on('messageBroadcast', (raw: any) => {
|
|
252
|
+
// Server sends objects to others and JSON strings back to the sender.
|
|
253
|
+
const data: MessageBroadcastData =
|
|
254
|
+
typeof raw === 'string'
|
|
255
|
+
? (() => {
|
|
256
|
+
try {
|
|
257
|
+
return JSON.parse(raw);
|
|
258
|
+
} catch (e) {
|
|
259
|
+
clientLogger.warn(
|
|
260
|
+
'[SocketIO] Failed to parse string messageBroadcast payload:',
|
|
261
|
+
raw
|
|
262
|
+
);
|
|
263
|
+
return {} as any;
|
|
264
|
+
}
|
|
265
|
+
})()
|
|
266
|
+
: (raw as MessageBroadcastData);
|
|
267
|
+
|
|
268
|
+
clientLogger.info(`[SocketIO] Message broadcast received:`, data);
|
|
269
|
+
|
|
270
|
+
// Log the full data structure to understand formats
|
|
271
|
+
const isObj = data && typeof data === 'object';
|
|
272
|
+
const keys = isObj ? Object.keys(data as any) : [];
|
|
273
|
+
clientLogger.debug('[SocketIO] Message broadcast data structure:', {
|
|
274
|
+
keys,
|
|
275
|
+
senderId: (data as any).senderId,
|
|
276
|
+
senderNameType: typeof (data as any).senderName,
|
|
277
|
+
textType: typeof (data as any).text,
|
|
278
|
+
textLength: (data as any).text ? (data as any).text.length : 0,
|
|
279
|
+
hasThought: isObj && 'thought' in (data as any),
|
|
280
|
+
hasActions: isObj && 'actions' in (data as any),
|
|
281
|
+
additionalKeys: keys.filter(
|
|
282
|
+
(k) =>
|
|
283
|
+
![
|
|
284
|
+
'senderId',
|
|
285
|
+
'senderName',
|
|
286
|
+
'text',
|
|
287
|
+
'roomId',
|
|
288
|
+
'createdAt',
|
|
289
|
+
'source',
|
|
290
|
+
'thought',
|
|
291
|
+
'actions',
|
|
292
|
+
].includes(k)
|
|
293
|
+
),
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Check if this is a message for one of our active channels
|
|
297
|
+
const channelId = (data as any).channelId || (data as any).roomId; // Handle both new and old message format
|
|
298
|
+
if (channelId && this.activeChannelIds.has(channelId)) {
|
|
299
|
+
clientLogger.info(`[SocketIO] Handling message for active channel ${channelId}`);
|
|
300
|
+
// Post the message to the event for UI updates
|
|
301
|
+
this.emit('messageBroadcast', {
|
|
302
|
+
...(data as any),
|
|
303
|
+
channelId: channelId, // Ensure channelId is always set
|
|
304
|
+
roomId: channelId, // Keep roomId for backward compatibility
|
|
305
|
+
name: (data as any).senderName, // Required for ContentWithUser compatibility
|
|
306
|
+
});
|
|
307
|
+
} else {
|
|
308
|
+
clientLogger.warn(
|
|
309
|
+
`[SocketIO] Received message for inactive channel ${channelId}, active channels:`,
|
|
310
|
+
Array.from(this.activeChannelIds)
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
this.socket.on('messageComplete', (data) => {
|
|
316
|
+
this.emit('messageComplete', data);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// Listen for control messages
|
|
320
|
+
this.socket.on('controlMessage', (data) => {
|
|
321
|
+
clientLogger.info(`[SocketIO] Control message received:`, data);
|
|
322
|
+
|
|
323
|
+
// Check if this is for one of our active channels
|
|
324
|
+
const channelId = data.channelId || data.roomId; // Handle both new and old message format
|
|
325
|
+
if (channelId && this.activeChannelIds.has(channelId)) {
|
|
326
|
+
clientLogger.info(`[SocketIO] Handling control message for active channel ${channelId}`);
|
|
327
|
+
|
|
328
|
+
// Emit the control message event
|
|
329
|
+
this.emit('controlMessage', {
|
|
330
|
+
...data,
|
|
331
|
+
channelId: channelId, // Ensure channelId is always set
|
|
332
|
+
roomId: channelId, // Keep roomId for backward compatibility
|
|
333
|
+
});
|
|
334
|
+
} else {
|
|
335
|
+
clientLogger.warn(
|
|
336
|
+
`[SocketIO] Received control message for inactive channel ${channelId}, active channels:`,
|
|
337
|
+
Array.from(this.activeChannelIds)
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// Listen for message deletion events
|
|
343
|
+
this.socket.on('messageDeleted', (data) => {
|
|
344
|
+
clientLogger.debug(`[SocketIO] Message deleted event received:`, data);
|
|
345
|
+
|
|
346
|
+
// Check if this is for one of our active channels
|
|
347
|
+
const channelId = data.channelId || data.roomId; // Handle both new and old message format
|
|
348
|
+
if (channelId && this.activeChannelIds.has(channelId)) {
|
|
349
|
+
clientLogger.info(`[SocketIO] Handling message deletion for active channel ${channelId}`);
|
|
350
|
+
|
|
351
|
+
// Emit the message deleted event
|
|
352
|
+
this.emit('messageDeleted', {
|
|
353
|
+
...data,
|
|
354
|
+
channelId: channelId, // Ensure channelId is always set
|
|
355
|
+
roomId: channelId, // Deprecated: Retained for backward compatibility with older clients
|
|
356
|
+
});
|
|
357
|
+
} else {
|
|
358
|
+
clientLogger.warn(
|
|
359
|
+
`[SocketIO] Received message deleted event for inactive channel ${channelId}, active channels:`,
|
|
360
|
+
Array.from(this.activeChannelIds)
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// Listen for channel cleared events
|
|
366
|
+
this.socket.on('channelCleared', (data) => {
|
|
367
|
+
clientLogger.info(`[SocketIO] Channel cleared event received:`, data);
|
|
368
|
+
|
|
369
|
+
// Check if this is for one of our active channels
|
|
370
|
+
const channelId = data.channelId || data.roomId; // Handle both new and old message format
|
|
371
|
+
if (channelId && this.activeChannelIds.has(channelId)) {
|
|
372
|
+
clientLogger.info(`[SocketIO] Handling channel cleared for active channel ${channelId}`);
|
|
373
|
+
|
|
374
|
+
// Emit the channel cleared event
|
|
375
|
+
this.emit('channelCleared', {
|
|
376
|
+
...data,
|
|
377
|
+
channelId: channelId, // Ensure channelId is always set
|
|
378
|
+
roomId: channelId, // Keep roomId for backward compatibility
|
|
379
|
+
});
|
|
380
|
+
} else {
|
|
381
|
+
clientLogger.warn(
|
|
382
|
+
`[SocketIO] Received channel cleared event for inactive channel ${channelId}, active channels:`,
|
|
383
|
+
Array.from(this.activeChannelIds)
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
// Listen for channel deleted events
|
|
389
|
+
this.socket.on('channelDeleted', (data) => {
|
|
390
|
+
clientLogger.info(`[SocketIO] Channel deleted event received:`, data);
|
|
391
|
+
|
|
392
|
+
// Check if this is for one of our active channels
|
|
393
|
+
const channelId = data.channelId || data.roomId; // Handle both new and old message format
|
|
394
|
+
if (channelId && this.activeChannelIds.has(channelId)) {
|
|
395
|
+
clientLogger.info(`[SocketIO] Handling channel deleted for active channel ${channelId}`);
|
|
396
|
+
|
|
397
|
+
// Emit the channel deleted event (same as cleared for now)
|
|
398
|
+
this.emit('channelDeleted', {
|
|
399
|
+
...data,
|
|
400
|
+
channelId: channelId, // Ensure channelId is always set
|
|
401
|
+
roomId: channelId, // Keep roomId for backward compatibility
|
|
402
|
+
});
|
|
403
|
+
} else {
|
|
404
|
+
clientLogger.warn(
|
|
405
|
+
`[SocketIO] Received channel deleted event for inactive channel ${channelId}, active channels:`,
|
|
406
|
+
Array.from(this.activeChannelIds)
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
this.socket.on('disconnect', (reason) => {
|
|
412
|
+
clientLogger.info(`[SocketIO] Disconnected. Reason: ${reason}`);
|
|
413
|
+
this.isConnected = false;
|
|
414
|
+
|
|
415
|
+
this.emit('disconnect', reason);
|
|
416
|
+
|
|
417
|
+
// Reset connect promise for next connection
|
|
418
|
+
this.connectPromise = new Promise<void>((resolve) => {
|
|
419
|
+
this.resolveConnect = resolve;
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
if (reason === 'io server disconnect') {
|
|
423
|
+
this.socket?.connect();
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
this.socket.on('reconnect_attempt', (attempt) => {
|
|
428
|
+
clientLogger.info('[SocketIO] Reconnect attempt', attempt);
|
|
429
|
+
this.emit('reconnect_attempt', attempt);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
this.socket.on('reconnect', (attempt) => {
|
|
433
|
+
clientLogger.info(`[SocketIO] Reconnected after ${attempt} attempts`);
|
|
434
|
+
this.emit('reconnect', attempt);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
this.socket.on('connect_error', (error) => {
|
|
438
|
+
clientLogger.error('[SocketIO] Connection error:', error);
|
|
439
|
+
this.emit('connect_error', error);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// Handle log stream events
|
|
443
|
+
this.socket.on('log_stream', (data) => {
|
|
444
|
+
clientLogger.debug('[SocketIO] Log stream data received:', data);
|
|
445
|
+
if (data.type === 'log_entry' && data.payload) {
|
|
446
|
+
this.emit('logStream', data.payload);
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
this.socket.on('log_subscription_confirmed', (data) => {
|
|
451
|
+
clientLogger.info('[SocketIO] Log subscription confirmed:', data);
|
|
452
|
+
this.logStreamSubscribed = data.subscribed;
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Join a channel to receive messages from it
|
|
458
|
+
* @param channelId Channel ID to join
|
|
459
|
+
*/
|
|
460
|
+
public async joinChannel(channelId: string): Promise<void> {
|
|
461
|
+
if (!this.socket) {
|
|
462
|
+
clientLogger.error('[SocketIO] Cannot join channel: socket not initialized');
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Wait for connection if needed
|
|
467
|
+
if (!this.isConnected) {
|
|
468
|
+
await this.connectPromise;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
clientLogger.info(
|
|
472
|
+
`[SocketIO] joinChannel: Attempting to join ${channelId}. Current activeChannelIds before add:`,
|
|
473
|
+
new Set(this.activeChannelIds)
|
|
474
|
+
);
|
|
475
|
+
this.activeChannelIds.add(channelId);
|
|
476
|
+
clientLogger.info(
|
|
477
|
+
`[SocketIO] joinChannel: Joined ${channelId}. Current activeChannelIds after add:`,
|
|
478
|
+
new Set(this.activeChannelIds)
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
this.socket.emit('message', {
|
|
482
|
+
type: SOCKET_MESSAGE_TYPE.ROOM_JOINING,
|
|
483
|
+
payload: {
|
|
484
|
+
channelId: channelId,
|
|
485
|
+
roomId: channelId, // Keep for backward compatibility
|
|
486
|
+
entityId: this.clientEntityId,
|
|
487
|
+
},
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
clientLogger.info(`[SocketIO] Emitted ROOM_JOINING for ${channelId}`);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* @deprecated Use joinChannel instead
|
|
495
|
+
*/
|
|
496
|
+
public async joinRoom(channelId: string): Promise<void> {
|
|
497
|
+
return this.joinChannel(channelId);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Leave a channel to stop receiving messages from it
|
|
502
|
+
* @param channelId Channel ID to leave
|
|
503
|
+
*/
|
|
504
|
+
public leaveChannel(channelId: string): void {
|
|
505
|
+
if (!this.socket || !this.isConnected) {
|
|
506
|
+
clientLogger.warn(`[SocketIO] Cannot leave channel ${channelId}: not connected`);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
clientLogger.info(
|
|
511
|
+
`[SocketIO] leaveChannel: Attempting to leave ${channelId}. Current activeChannelIds before delete:`,
|
|
512
|
+
new Set(this.activeChannelIds)
|
|
513
|
+
);
|
|
514
|
+
this.activeChannelIds.delete(channelId);
|
|
515
|
+
clientLogger.info(
|
|
516
|
+
`[SocketIO] leaveChannel: Left ${channelId}. Current activeChannelIds after delete:`,
|
|
517
|
+
new Set(this.activeChannelIds)
|
|
518
|
+
);
|
|
519
|
+
// No server-side message for leaving a room in this client's protocol,
|
|
520
|
+
// client just stops listening / tracking.
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* @deprecated Use leaveChannel instead
|
|
525
|
+
*/
|
|
526
|
+
public leaveRoom(channelId: string): void {
|
|
527
|
+
return this.leaveChannel(channelId);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Send a message to a specific channel
|
|
532
|
+
* @param message Message text to send
|
|
533
|
+
* @param channelId Channel ID to send the message to
|
|
534
|
+
* @param serverId Server ID to send the message to
|
|
535
|
+
* @param source Source identifier (e.g., 'client_chat')
|
|
536
|
+
* @param attachments Optional media attachments
|
|
537
|
+
* @param messageId Optional message ID for tracking optimistic updates
|
|
538
|
+
*/
|
|
539
|
+
public async sendMessage(
|
|
540
|
+
message: string,
|
|
541
|
+
channelId: string,
|
|
542
|
+
serverId: string,
|
|
543
|
+
source: string,
|
|
544
|
+
attachments?: any[],
|
|
545
|
+
messageId?: string,
|
|
546
|
+
metadata?: Record<string, any>
|
|
547
|
+
): Promise<void> {
|
|
548
|
+
if (!this.socket) {
|
|
549
|
+
clientLogger.error('[SocketIO] Cannot send message: socket not initialized');
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Wait for connection if needed
|
|
554
|
+
if (!this.isConnected) {
|
|
555
|
+
await this.connectPromise;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Use provided messageId or generate a new one
|
|
559
|
+
const finalMessageId = messageId || randomUUID();
|
|
560
|
+
|
|
561
|
+
clientLogger.info(
|
|
562
|
+
`[SocketIO] Sending message to central channel ${channelId} on server ${serverId}`
|
|
563
|
+
);
|
|
564
|
+
|
|
565
|
+
// Emit message to server
|
|
566
|
+
this.socket.emit('message', {
|
|
567
|
+
type: SOCKET_MESSAGE_TYPE.SEND_MESSAGE,
|
|
568
|
+
payload: {
|
|
569
|
+
senderId: this.clientEntityId,
|
|
570
|
+
senderName: USER_NAME,
|
|
571
|
+
message,
|
|
572
|
+
channelId: channelId,
|
|
573
|
+
roomId: channelId, // Keep for backward compatibility
|
|
574
|
+
serverId: serverId, // Client uses serverId, not worldId
|
|
575
|
+
messageId: finalMessageId,
|
|
576
|
+
source,
|
|
577
|
+
attachments,
|
|
578
|
+
metadata,
|
|
579
|
+
},
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
// Note: We no longer broadcast locally - the server will send the message back with the proper ID
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Subscribe to log streaming
|
|
587
|
+
*/
|
|
588
|
+
public async subscribeToLogStream(): Promise<void> {
|
|
589
|
+
if (!this.socket) {
|
|
590
|
+
clientLogger.error('[SocketIO] Cannot subscribe to logs: socket not initialized');
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Wait for connection if needed
|
|
595
|
+
if (!this.isConnected) {
|
|
596
|
+
await this.connectPromise;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
this.socket.emit('subscribe_logs');
|
|
600
|
+
clientLogger.info('[SocketIO] Subscribed to log stream');
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Unsubscribe from log streaming
|
|
605
|
+
*/
|
|
606
|
+
public async unsubscribeFromLogStream(): Promise<void> {
|
|
607
|
+
if (!this.socket) {
|
|
608
|
+
clientLogger.error('[SocketIO] Cannot unsubscribe from logs: socket not initialized');
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Wait for connection if needed
|
|
613
|
+
if (!this.isConnected) {
|
|
614
|
+
await this.connectPromise;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
this.socket.emit('unsubscribe_logs');
|
|
618
|
+
clientLogger.info('[SocketIO] Unsubscribed from log stream');
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Update log stream filters
|
|
623
|
+
*/
|
|
624
|
+
public async updateLogStreamFilters(filters: {
|
|
625
|
+
agentName?: string;
|
|
626
|
+
level?: string;
|
|
627
|
+
}): Promise<void> {
|
|
628
|
+
if (!this.socket) {
|
|
629
|
+
clientLogger.error('[SocketIO] Cannot update log filters: socket not initialized');
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Wait for connection if needed
|
|
634
|
+
if (!this.isConnected) {
|
|
635
|
+
await this.connectPromise;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
this.socket.emit('update_log_filters', filters);
|
|
639
|
+
clientLogger.info('[SocketIO] Updated log stream filters:', filters);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Check if subscribed to log streaming
|
|
644
|
+
*/
|
|
645
|
+
public isLogStreamSubscribed(): boolean {
|
|
646
|
+
return this.logStreamSubscribed;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Disconnect from the server
|
|
651
|
+
*/
|
|
652
|
+
public disconnect(): void {
|
|
653
|
+
if (this.socket) {
|
|
654
|
+
this.socket.disconnect();
|
|
655
|
+
this.socket = null;
|
|
656
|
+
this.isConnected = false;
|
|
657
|
+
this.activeChannelIds.clear();
|
|
658
|
+
this.logStreamSubscribed = false;
|
|
659
|
+
clientLogger.info('[SocketIO] Disconnected from server');
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
export default SocketIOManager;
|