1
1
import { v4 as uuidv4 } from 'uuid' ;
2
- import type { UUID , ChannelType } from '@elizaos/core' ;
3
- import { logger } from '@elizaos/core' ;
2
+ import type { UUID , ChannelType , IAgentRuntime } from '@elizaos/core' ;
3
+ import { logger , createUniqueUuid } from '@elizaos/core' ;
4
4
5
5
/**
6
6
* Interface representing a room mapping entry
@@ -17,33 +17,55 @@ export interface RoomMappingEntry {
17
17
* and agent-specific rooms (as they exist in each agent's context)
18
18
*/
19
19
export class RoomMappingService {
20
+ private static instance : RoomMappingService | null = null ;
20
21
// In-memory storage of room mappings
21
22
private roomMappings : RoomMappingEntry [ ] = [ ] ;
22
23
// Conceptual room registry
23
- private conceptualRooms : Map < UUID , { name : string ; type : ChannelType ; createdAt : number } > =
24
- new Map ( ) ;
24
+ private conceptualRooms : Map <
25
+ UUID ,
26
+ {
27
+ name : string ;
28
+ type : ChannelType ;
29
+ createdAt : number ;
30
+ ownerAgentId : UUID ; // The user agent that created/owns this room
31
+ }
32
+ > = new Map ( ) ;
25
33
26
- constructor ( ) {
34
+ private constructor ( ) {
27
35
logger . info ( '[RoomMappingService] Initializing room mapping service' ) ;
28
36
}
29
37
38
+ /**
39
+ * Get the singleton instance of RoomMappingService
40
+ */
41
+ public static getInstance ( ) : RoomMappingService {
42
+ if ( ! RoomMappingService . instance ) {
43
+ RoomMappingService . instance = new RoomMappingService ( ) ;
44
+ }
45
+ return RoomMappingService . instance ;
46
+ }
47
+
30
48
/**
31
49
* Creates a new conceptual room
32
50
* @param name The name of the room
33
51
* @param type The type of room (DM, GROUP, etc)
52
+ * @param ownerAgentId The user agent ID that owns this room
34
53
* @returns The ID of the newly created conceptual room
35
54
*/
36
- async createConceptualRoom ( name : string , type : ChannelType ) : Promise < UUID > {
55
+ async createConceptualRoom ( name : string , type : ChannelType , ownerAgentId : UUID ) : Promise < UUID > {
37
56
const roomId = uuidv4 ( ) as UUID ;
38
57
39
58
// Register the conceptual room
40
59
this . conceptualRooms . set ( roomId , {
41
60
name,
42
61
type,
43
62
createdAt : Date . now ( ) ,
63
+ ownerAgentId,
44
64
} ) ;
45
65
46
- logger . info ( `[RoomMappingService] Created conceptual room ${ roomId } (${ name } , ${ type } )` ) ;
66
+ logger . info (
67
+ `[RoomMappingService] Created conceptual room ${ roomId } (${ name } , ${ type } , owned by ${ ownerAgentId } )`
68
+ ) ;
47
69
48
70
return roomId ;
49
71
}
@@ -133,4 +155,141 @@ export class RoomMappingService {
133
155
const mappings = await this . getAllMappingsForRoom ( conceptualRoomId ) ;
134
156
return mappings . map ( ( mapping ) => mapping . agentId ) ;
135
157
}
158
+
159
+ /**
160
+ * Creates a new mirrored room in an agent's context for a conceptual room
161
+ * @param conceptualRoomId The ID of the conceptual room
162
+ * @param targetAgentId The ID of the agent to create a room for
163
+ * @param agentRuntime The runtime for the agent
164
+ * @returns The ID of the newly created agent-specific room
165
+ */
166
+ async createMirroredRoom (
167
+ conceptualRoomId : UUID ,
168
+ targetAgentId : UUID ,
169
+ agentRuntime : IAgentRuntime
170
+ ) : Promise < UUID > {
171
+ // Get the conceptual room details
172
+ const conceptualRoom = await this . getConceptualRoom ( conceptualRoomId ) ;
173
+ if ( ! conceptualRoom ) {
174
+ throw new Error ( `Conceptual room ${ conceptualRoomId } not found` ) ;
175
+ }
176
+
177
+ // Create a unique room ID for this agent
178
+ const agentRoomId = createUniqueUuid ( agentRuntime , `room-${ conceptualRoomId } ` ) as UUID ;
179
+
180
+ // Ensure the room exists in the agent's context
181
+ await agentRuntime . ensureRoomExists ( {
182
+ id : agentRoomId ,
183
+ name : conceptualRoom . name ,
184
+ type : conceptualRoom . type ,
185
+ source : 'mirrored' ,
186
+ worldId : null ,
187
+ metadata : {
188
+ conceptualRoomId, // Store the conceptual room ID for reference
189
+ ownerAgentId : conceptualRoom . ownerAgentId ,
190
+ } ,
191
+ } ) ;
192
+
193
+ // Store the mapping
194
+ await this . storeRoomMapping ( conceptualRoomId , targetAgentId , agentRoomId ) ;
195
+
196
+ logger . info (
197
+ `[RoomMappingService] Created mirrored room ${ agentRoomId } for agent ${ targetAgentId } (conceptual room: ${ conceptualRoomId } )`
198
+ ) ;
199
+
200
+ return agentRoomId ;
201
+ }
202
+
203
+ /**
204
+ * Gets the conceptual room ID for an agent-specific room
205
+ * @param agentRoomId The ID of the agent-specific room
206
+ * @param agentId The ID of the agent
207
+ * @returns The conceptual room ID or undefined if not found
208
+ */
209
+ async getConceptualRoomId ( agentRoomId : UUID , agentId : UUID ) : Promise < UUID | undefined > {
210
+ const mapping = this . roomMappings . find (
211
+ ( mapping ) => mapping . agentRoomId === agentRoomId && mapping . agentId === agentId
212
+ ) ;
213
+ return mapping ?. conceptualRoomId ;
214
+ }
215
+
216
+ /**
217
+ * Gets all mirrored rooms for a conceptual room
218
+ * @param conceptualRoomId The ID of the conceptual room
219
+ * @returns Map of agent IDs to their room IDs for this conceptual room
220
+ */
221
+ async getMirroredRooms ( conceptualRoomId : UUID ) : Promise < Map < UUID , UUID > > {
222
+ const mappings = await this . getAllMappingsForRoom ( conceptualRoomId ) ;
223
+ const mirroredRooms = new Map < UUID , UUID > ( ) ;
224
+
225
+ for ( const mapping of mappings ) {
226
+ mirroredRooms . set ( mapping . agentId , mapping . agentRoomId ) ;
227
+ }
228
+
229
+ return mirroredRooms ;
230
+ }
231
+
232
+ /**
233
+ * Add a participant to all mirrored rooms for a conceptual room
234
+ * @param conceptualRoomId The ID of the conceptual room
235
+ * @param participantId The ID of the participant to add
236
+ * @param agentRuntimes Map of agent IDs to their runtimes
237
+ */
238
+ async addParticipantToMirroredRooms (
239
+ conceptualRoomId : UUID ,
240
+ participantId : UUID ,
241
+ agentRuntimes : Map < UUID , IAgentRuntime >
242
+ ) : Promise < void > {
243
+ const mirroredRooms = await this . getMirroredRooms ( conceptualRoomId ) ;
244
+
245
+ for ( const [ agentId , agentRoomId ] of mirroredRooms . entries ( ) ) {
246
+ const runtime = agentRuntimes . get ( agentId ) ;
247
+ if ( runtime ) {
248
+ // Ensure the participant entity exists in this agent's context
249
+ try {
250
+ await runtime . getEntityById ( participantId ) ;
251
+ } catch ( error ) {
252
+ // Create a basic entity if it doesn't exist
253
+ await runtime . createEntity ( {
254
+ id : participantId ,
255
+ names : [ 'User' ] ,
256
+ agentId : runtime . agentId ,
257
+ } ) ;
258
+ }
259
+
260
+ // Add the participant to the room
261
+ await runtime . addParticipant ( participantId , agentRoomId ) ;
262
+ await runtime . setParticipantUserState ( agentRoomId , participantId , 'FOLLOWED' ) ;
263
+
264
+ logger . info (
265
+ `[RoomMappingService] Added participant ${ participantId } to mirrored room ${ agentRoomId } for agent ${ agentId } `
266
+ ) ;
267
+ }
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Remove a participant from all mirrored rooms for a conceptual room
273
+ * @param conceptualRoomId The ID of the conceptual room
274
+ * @param participantId The ID of the participant to remove
275
+ * @param agentRuntimes Map of agent IDs to their runtimes
276
+ */
277
+ async removeParticipantFromMirroredRooms (
278
+ conceptualRoomId : UUID ,
279
+ participantId : UUID ,
280
+ agentRuntimes : Map < UUID , IAgentRuntime >
281
+ ) : Promise < void > {
282
+ const mirroredRooms = await this . getMirroredRooms ( conceptualRoomId ) ;
283
+
284
+ for ( const [ agentId , agentRoomId ] of mirroredRooms . entries ( ) ) {
285
+ const runtime = agentRuntimes . get ( agentId ) ;
286
+ if ( runtime ) {
287
+ await runtime . removeParticipant ( participantId , agentRoomId ) ;
288
+
289
+ logger . info (
290
+ `[RoomMappingService] Removed participant ${ participantId } from mirrored room ${ agentRoomId } for agent ${ agentId } `
291
+ ) ;
292
+ }
293
+ }
294
+ }
136
295
}
0 commit comments