@teneo-protocol/sdk 1.0.1 → 2.0.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.
Files changed (153) hide show
  1. package/.github/workflows/publish-npm.yml +8 -6
  2. package/CHANGELOG.md +265 -0
  3. package/README.md +406 -53
  4. package/dist/core/websocket-client.d.ts +12 -0
  5. package/dist/core/websocket-client.d.ts.map +1 -1
  6. package/dist/core/websocket-client.js +22 -2
  7. package/dist/core/websocket-client.js.map +1 -1
  8. package/dist/handlers/message-handlers/agent-room-operation-response-handler.d.ts +76 -0
  9. package/dist/handlers/message-handlers/agent-room-operation-response-handler.d.ts.map +1 -0
  10. package/dist/handlers/message-handlers/agent-room-operation-response-handler.js +70 -0
  11. package/dist/handlers/message-handlers/agent-room-operation-response-handler.js.map +1 -0
  12. package/dist/handlers/message-handlers/agent-selected-handler.d.ts +92 -38
  13. package/dist/handlers/message-handlers/agent-selected-handler.d.ts.map +1 -1
  14. package/dist/handlers/message-handlers/agent-status-update-handler.d.ts +904 -0
  15. package/dist/handlers/message-handlers/agent-status-update-handler.d.ts.map +1 -0
  16. package/dist/handlers/message-handlers/agent-status-update-handler.js +51 -0
  17. package/dist/handlers/message-handlers/agent-status-update-handler.js.map +1 -0
  18. package/dist/handlers/message-handlers/auth-error-handler.d.ts +45 -31
  19. package/dist/handlers/message-handlers/auth-error-handler.d.ts.map +1 -1
  20. package/dist/handlers/message-handlers/auth-message-handler.d.ts +6 -0
  21. package/dist/handlers/message-handlers/auth-message-handler.d.ts.map +1 -1
  22. package/dist/handlers/message-handlers/auth-message-handler.js +65 -5
  23. package/dist/handlers/message-handlers/auth-message-handler.js.map +1 -1
  24. package/dist/handlers/message-handlers/auth-required-handler.d.ts +49 -31
  25. package/dist/handlers/message-handlers/auth-required-handler.d.ts.map +1 -1
  26. package/dist/handlers/message-handlers/auth-success-handler.d.ts +6 -0
  27. package/dist/handlers/message-handlers/auth-success-handler.d.ts.map +1 -1
  28. package/dist/handlers/message-handlers/auth-success-handler.js +46 -4
  29. package/dist/handlers/message-handlers/auth-success-handler.js.map +1 -1
  30. package/dist/handlers/message-handlers/challenge-handler.d.ts +45 -31
  31. package/dist/handlers/message-handlers/challenge-handler.d.ts.map +1 -1
  32. package/dist/handlers/message-handlers/error-message-handler.d.ts +49 -31
  33. package/dist/handlers/message-handlers/error-message-handler.d.ts.map +1 -1
  34. package/dist/handlers/message-handlers/index.d.ts +5 -0
  35. package/dist/handlers/message-handlers/index.d.ts.map +1 -1
  36. package/dist/handlers/message-handlers/index.js +23 -1
  37. package/dist/handlers/message-handlers/index.js.map +1 -1
  38. package/dist/handlers/message-handlers/list-available-agents-handler.d.ts +877 -0
  39. package/dist/handlers/message-handlers/list-available-agents-handler.d.ts.map +1 -0
  40. package/dist/handlers/message-handlers/list-available-agents-handler.js +38 -0
  41. package/dist/handlers/message-handlers/list-available-agents-handler.js.map +1 -0
  42. package/dist/handlers/message-handlers/list-room-agents-handler.d.ts +886 -0
  43. package/dist/handlers/message-handlers/list-room-agents-handler.d.ts.map +1 -0
  44. package/dist/handlers/message-handlers/list-room-agents-handler.js +51 -0
  45. package/dist/handlers/message-handlers/list-room-agents-handler.js.map +1 -0
  46. package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts +178 -89
  47. package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts.map +1 -1
  48. package/dist/handlers/message-handlers/ping-pong-handler.d.ts +62 -58
  49. package/dist/handlers/message-handlers/ping-pong-handler.d.ts.map +1 -1
  50. package/dist/handlers/message-handlers/regular-message-handler.d.ts +31 -29
  51. package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -1
  52. package/dist/handlers/message-handlers/room-operation-response-handler.d.ts +328 -0
  53. package/dist/handlers/message-handlers/room-operation-response-handler.d.ts.map +1 -0
  54. package/dist/handlers/message-handlers/room-operation-response-handler.js +92 -0
  55. package/dist/handlers/message-handlers/room-operation-response-handler.js.map +1 -0
  56. package/dist/handlers/message-handlers/subscribe-response-handler.d.ts +53 -31
  57. package/dist/handlers/message-handlers/subscribe-response-handler.d.ts.map +1 -1
  58. package/dist/handlers/message-handlers/types.d.ts +2 -0
  59. package/dist/handlers/message-handlers/types.d.ts.map +1 -1
  60. package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts +53 -31
  61. package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts.map +1 -1
  62. package/dist/managers/agent-room-manager.d.ts +222 -0
  63. package/dist/managers/agent-room-manager.d.ts.map +1 -0
  64. package/dist/managers/agent-room-manager.js +508 -0
  65. package/dist/managers/agent-room-manager.js.map +1 -0
  66. package/dist/managers/index.d.ts +2 -0
  67. package/dist/managers/index.d.ts.map +1 -1
  68. package/dist/managers/index.js +5 -1
  69. package/dist/managers/index.js.map +1 -1
  70. package/dist/managers/room-management-manager.d.ts +213 -0
  71. package/dist/managers/room-management-manager.d.ts.map +1 -0
  72. package/dist/managers/room-management-manager.js +440 -0
  73. package/dist/managers/room-management-manager.js.map +1 -0
  74. package/dist/managers/room-manager.d.ts +4 -4
  75. package/dist/managers/room-manager.d.ts.map +1 -1
  76. package/dist/managers/room-manager.js.map +1 -1
  77. package/dist/teneo-sdk.d.ts +333 -13
  78. package/dist/teneo-sdk.d.ts.map +1 -1
  79. package/dist/teneo-sdk.js +468 -1
  80. package/dist/teneo-sdk.js.map +1 -1
  81. package/dist/types/config.d.ts +63 -54
  82. package/dist/types/config.d.ts.map +1 -1
  83. package/dist/types/config.js +8 -4
  84. package/dist/types/config.js.map +1 -1
  85. package/dist/types/error-codes.d.ts +2 -0
  86. package/dist/types/error-codes.d.ts.map +1 -1
  87. package/dist/types/error-codes.js +3 -0
  88. package/dist/types/error-codes.js.map +1 -1
  89. package/dist/types/events.d.ts +132 -68
  90. package/dist/types/events.d.ts.map +1 -1
  91. package/dist/types/events.js.map +1 -1
  92. package/dist/types/index.d.ts +1 -1
  93. package/dist/types/index.d.ts.map +1 -1
  94. package/dist/types/index.js +27 -2
  95. package/dist/types/index.js.map +1 -1
  96. package/dist/types/messages.d.ts +11396 -2559
  97. package/dist/types/messages.d.ts.map +1 -1
  98. package/dist/types/messages.js +294 -27
  99. package/dist/types/messages.js.map +1 -1
  100. package/examples/.env.example +1 -1
  101. package/examples/agent-room-management-example.ts +334 -0
  102. package/examples/claude-agent-x-follower/.env.example +2 -2
  103. package/examples/claude-agent-x-follower/QUICKSTART.md +1 -1
  104. package/examples/claude-agent-x-follower/README.md +1 -1
  105. package/examples/n8n-teneo/.env.example +2 -2
  106. package/examples/n8n-teneo/README.md +1 -1
  107. package/examples/openai-teneo/.env.example +2 -2
  108. package/examples/openai-teneo/README.md +1 -1
  109. package/examples/production-dashboard/.env.example +2 -2
  110. package/examples/production-dashboard/README.md +89 -12
  111. package/examples/production-dashboard/public/dashboard.html +1173 -601
  112. package/examples/production-dashboard/server.ts +347 -5
  113. package/examples/room-management-example.ts +285 -0
  114. package/examples/usage/.env.example +1 -1
  115. package/examples/usage/01-connect.ts +1 -1
  116. package/examples/usage/02-list-agents.ts +1 -1
  117. package/examples/usage/03-pick-agent.ts +1 -1
  118. package/examples/usage/04-find-by-capability.ts +1 -1
  119. package/examples/usage/05-webhook-example.ts +1 -1
  120. package/examples/usage/06-simple-api-server.ts +1 -1
  121. package/examples/usage/07-event-listener.ts +1 -1
  122. package/examples/usage/README.md +1 -1
  123. package/package.json +9 -1
  124. package/src/core/websocket-client.ts +26 -2
  125. package/src/handlers/message-handlers/agent-room-operation-response-handler.ts +83 -0
  126. package/src/handlers/message-handlers/agent-status-update-handler.ts +58 -0
  127. package/src/handlers/message-handlers/auth-message-handler.ts +73 -5
  128. package/src/handlers/message-handlers/auth-success-handler.ts +58 -6
  129. package/src/handlers/message-handlers/index.ts +19 -0
  130. package/src/handlers/message-handlers/list-available-agents-handler.ts +41 -0
  131. package/src/handlers/message-handlers/list-room-agents-handler.ts +61 -0
  132. package/src/handlers/message-handlers/room-operation-response-handler.ts +105 -0
  133. package/src/handlers/message-handlers/types.ts +6 -0
  134. package/src/managers/agent-room-manager.ts +609 -0
  135. package/src/managers/index.ts +2 -0
  136. package/src/managers/room-management-manager.ts +523 -0
  137. package/src/managers/room-manager.ts +4 -5
  138. package/src/teneo-sdk.ts +505 -4
  139. package/src/types/config.ts +10 -5
  140. package/src/types/error-codes.ts +4 -0
  141. package/src/types/events.ts +24 -0
  142. package/src/types/index.ts +55 -0
  143. package/src/types/messages.ts +374 -41
  144. package/tests/integration/room-management.test.ts +514 -0
  145. package/tests/integration/websocket.test.ts +1 -1
  146. package/tests/unit/handlers/agent-room-operation-response-handler.test.ts +394 -0
  147. package/tests/unit/handlers/agent-status-update-handler.test.ts +407 -0
  148. package/tests/unit/handlers/auth-success-handler-rooms.test.ts +699 -0
  149. package/tests/unit/handlers/list-available-agents-handler.test.ts +256 -0
  150. package/tests/unit/handlers/list-room-agents-handler.test.ts +294 -0
  151. package/tests/unit/handlers/room-operation-response-handler.test.ts +527 -0
  152. package/tests/unit/managers/agent-room-manager.test.ts +534 -0
  153. package/tests/unit/managers/room-management-manager.test.ts +438 -0
@@ -0,0 +1,609 @@
1
+ /**
2
+ * AgentRoomManager - Manages agent-room associations (v2.0.0)
3
+ * Allows room owners to control which agents are available in their rooms
4
+ * Implements caching with 5-minute TTL for performance
5
+ */
6
+
7
+ import { EventEmitter } from "eventemitter3";
8
+ import { WebSocketClient } from "../core/websocket-client";
9
+ import { Logger } from "../types";
10
+ import { SDKEvents, SDKError } from "../types/events";
11
+ import { ErrorCode } from "../types/error-codes";
12
+ import { RoomManagementManager } from "./room-management-manager";
13
+
14
+ // AgentRoomInfo from server response
15
+ export interface AgentRoomInfo {
16
+ agent_id: string;
17
+ agent_name?: string;
18
+ description?: string;
19
+ capabilities?: Array<{ name: string; description?: string }>;
20
+ commands?: Array<{ trigger: string; argument?: string; description?: string }>;
21
+ image?: string;
22
+ status?: string;
23
+ added_by?: string;
24
+ added_at?: string;
25
+ }
26
+
27
+ // Cache TTL: 5 minutes
28
+ const CACHE_TTL_MS = 5 * 60 * 1000;
29
+
30
+ export class AgentRoomManager extends EventEmitter<SDKEvents> {
31
+ private readonly wsClient: WebSocketClient;
32
+ private readonly logger: Logger;
33
+ private readonly roomManagementManager: RoomManagementManager; // Reference to check ownership
34
+
35
+ // Caches with TTL
36
+ private readonly roomAgentsCache = new Map<string, AgentRoomInfo[]>();
37
+ private readonly availableAgentsCache = new Map<string, AgentRoomInfo[]>();
38
+ private readonly roomAgentsCacheTime = new Map<string, number>();
39
+ private readonly availableAgentsCacheTime = new Map<string, number>();
40
+
41
+ constructor(
42
+ wsClient: WebSocketClient,
43
+ logger: Logger,
44
+ roomManagementManager: RoomManagementManager
45
+ ) {
46
+ super();
47
+ this.wsClient = wsClient;
48
+ this.logger = logger;
49
+ this.roomManagementManager = roomManagementManager;
50
+ }
51
+
52
+ // ============================================================================
53
+ // AGENT ROOM OPERATIONS
54
+ // ============================================================================
55
+
56
+ /**
57
+ * Adds an agent to a room, making it available for interactions in that room.
58
+ * User must own the room to perform this operation.
59
+ *
60
+ * @param roomId - ID of the room to add agent to
61
+ * @param agentId - ID of the agent to add
62
+ * @returns Promise that resolves when agent is added
63
+ * @throws {SDKError} If not connected, not room owner, or operation fails
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * await sdk.addAgentToRoom('room-123', 'agent-456');
68
+ * console.log('Agent added to room');
69
+ * ```
70
+ */
71
+ public async addAgentToRoom(roomId: string, agentId: string): Promise<void> {
72
+ if (!this.wsClient.isConnected) {
73
+ throw new SDKError("Not connected to Teneo network", ErrorCode.NOT_CONNECTED);
74
+ }
75
+
76
+ // Validate inputs
77
+ this.validateRoomId(roomId);
78
+ this.validateAgentId(agentId);
79
+
80
+ // Verify room exists
81
+ if (!this.roomExists(roomId)) {
82
+ throw new SDKError("Room not found", ErrorCode.VALIDATION_ERROR);
83
+ }
84
+
85
+ // Verify user owns room
86
+ if (!this.verifyOwnership(roomId)) {
87
+ throw new SDKError(
88
+ "Cannot add agent to room: You don't own this room",
89
+ ErrorCode.PERMISSION_DENIED
90
+ );
91
+ }
92
+
93
+ this.logger.debug("AgentRoomManager: Adding agent to room", { roomId, agentId });
94
+
95
+ const message = {
96
+ type: "add_agent_to_room" as const,
97
+ data: {
98
+ room_id: roomId,
99
+ agent_id: agentId
100
+ }
101
+ };
102
+
103
+ return new Promise((resolve, reject) => {
104
+ const timeout = setTimeout(() => {
105
+ cleanup();
106
+ reject(new SDKError("Add agent to room timeout", ErrorCode.TIMEOUT));
107
+ }, 30000);
108
+
109
+ const onSuccess = (responseRoomId: string, responseAgentId: string) => {
110
+ if (responseRoomId === roomId && responseAgentId === agentId) {
111
+ cleanup();
112
+ // Invalidate caches for this room
113
+ this.invalidateRoomCaches(roomId);
114
+ resolve();
115
+ }
116
+ };
117
+
118
+ const onError = (error: Error, responseRoomId?: string) => {
119
+ if (!responseRoomId || responseRoomId === roomId) {
120
+ cleanup();
121
+ reject(error);
122
+ }
123
+ };
124
+
125
+ const cleanup = () => {
126
+ clearTimeout(timeout);
127
+ this.off("agent_room:agent_added", onSuccess);
128
+ this.off("agent_room:add_error", onError);
129
+ };
130
+
131
+ this.once("agent_room:agent_added", onSuccess);
132
+ this.once("agent_room:add_error", onError);
133
+
134
+ this.wsClient.sendMessage(message);
135
+ });
136
+ }
137
+
138
+ /**
139
+ * Removes an agent from a room.
140
+ * User must own the room to perform this operation.
141
+ *
142
+ * @param roomId - ID of the room to remove agent from
143
+ * @param agentId - ID of the agent to remove
144
+ * @returns Promise that resolves when agent is removed
145
+ * @throws {SDKError} If not connected, not room owner, or operation fails
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * await sdk.removeAgentFromRoom('room-123', 'agent-456');
150
+ * console.log('Agent removed from room');
151
+ * ```
152
+ */
153
+ public async removeAgentFromRoom(roomId: string, agentId: string): Promise<void> {
154
+ if (!this.wsClient.isConnected) {
155
+ throw new SDKError("Not connected to Teneo network", ErrorCode.NOT_CONNECTED);
156
+ }
157
+
158
+ // Validate inputs
159
+ this.validateRoomId(roomId);
160
+ this.validateAgentId(agentId);
161
+
162
+ // Verify user owns room
163
+ if (!this.verifyOwnership(roomId)) {
164
+ throw new SDKError(
165
+ "Cannot remove agent from room: You don't own this room",
166
+ ErrorCode.PERMISSION_DENIED
167
+ );
168
+ }
169
+
170
+ this.logger.debug("AgentRoomManager: Removing agent from room", { roomId, agentId });
171
+
172
+ const message = {
173
+ type: "remove_agent_from_room" as const,
174
+ data: {
175
+ room_id: roomId,
176
+ agent_id: agentId
177
+ }
178
+ };
179
+
180
+ return new Promise((resolve, reject) => {
181
+ const timeout = setTimeout(() => {
182
+ cleanup();
183
+ reject(new SDKError("Remove agent from room timeout", ErrorCode.TIMEOUT));
184
+ }, 30000);
185
+
186
+ const onSuccess = (responseRoomId: string, responseAgentId: string) => {
187
+ if (responseRoomId === roomId && responseAgentId === agentId) {
188
+ cleanup();
189
+ // Invalidate caches for this room
190
+ this.invalidateRoomCaches(roomId);
191
+ resolve();
192
+ }
193
+ };
194
+
195
+ const onError = (error: Error, responseRoomId?: string) => {
196
+ if (!responseRoomId || responseRoomId === roomId) {
197
+ cleanup();
198
+ reject(error);
199
+ }
200
+ };
201
+
202
+ const cleanup = () => {
203
+ clearTimeout(timeout);
204
+ this.off("agent_room:agent_removed", onSuccess);
205
+ this.off("agent_room:remove_error", onError);
206
+ };
207
+
208
+ this.once("agent_room:agent_removed", onSuccess);
209
+ this.once("agent_room:remove_error", onError);
210
+
211
+ this.wsClient.sendMessage(message);
212
+ });
213
+ }
214
+
215
+ /**
216
+ * Lists all agents currently in a room.
217
+ * Results are cached for 5 minutes for performance.
218
+ *
219
+ * @param roomId - ID of the room to list agents for
220
+ * @param useCache - Whether to use cached data (default: true)
221
+ * @returns Promise that resolves with array of agents
222
+ * @throws {SDKError} If not connected or operation fails
223
+ *
224
+ * @example
225
+ * ```typescript
226
+ * const agents = await sdk.listRoomAgents('room-123');
227
+ * console.log(`Room has ${agents.length} agents`);
228
+ *
229
+ * // Force fresh data
230
+ * const freshAgents = await sdk.listRoomAgents('room-123', false);
231
+ * ```
232
+ */
233
+ public async listRoomAgents(
234
+ roomId: string,
235
+ useCache: boolean = true
236
+ ): Promise<AgentRoomInfo[]> {
237
+ if (!this.wsClient.isConnected) {
238
+ throw new SDKError("Not connected to Teneo network", ErrorCode.NOT_CONNECTED);
239
+ }
240
+
241
+ // Validate input
242
+ this.validateRoomId(roomId);
243
+
244
+ // Check cache if enabled
245
+ if (useCache && this.isCacheValid(this.roomAgentsCacheTime, roomId)) {
246
+ const cached = this.roomAgentsCache.get(roomId);
247
+ if (cached) {
248
+ this.logger.debug("AgentRoomManager: Returning cached room agents", {
249
+ roomId,
250
+ count: cached.length
251
+ });
252
+ return cached.map((agent) => ({ ...agent })); // Return deep copy
253
+ }
254
+ }
255
+
256
+ this.logger.debug("AgentRoomManager: Listing room agents", { roomId });
257
+
258
+ const message = {
259
+ type: "list_room_agents" as const,
260
+ data: {
261
+ room_id: roomId
262
+ }
263
+ };
264
+
265
+ return new Promise((resolve, reject) => {
266
+ const timeout = setTimeout(() => {
267
+ cleanup();
268
+ reject(new SDKError("List room agents timeout", ErrorCode.TIMEOUT));
269
+ }, 30000);
270
+
271
+ const onSuccess = (responseRoomId: string, agents: AgentRoomInfo[]) => {
272
+ if (responseRoomId === roomId) {
273
+ cleanup();
274
+ resolve(agents);
275
+ }
276
+ };
277
+
278
+ const onError = (error: Error, responseRoomId?: string) => {
279
+ if (!responseRoomId || responseRoomId === roomId) {
280
+ cleanup();
281
+ reject(error);
282
+ }
283
+ };
284
+
285
+ const cleanup = () => {
286
+ clearTimeout(timeout);
287
+ this.off("agent_room:agents_listed", onSuccess);
288
+ this.off("agent_room:list_error", onError);
289
+ };
290
+
291
+ this.once("agent_room:agents_listed", onSuccess);
292
+ this.once("agent_room:list_error", onError);
293
+
294
+ this.wsClient.sendMessage(message);
295
+ });
296
+ }
297
+
298
+ /**
299
+ * Lists all agents available to add to a room (not yet in the room).
300
+ * Results are cached for 5 minutes for performance.
301
+ *
302
+ * @param roomId - ID of the room to check available agents for
303
+ * @param useCache - Whether to use cached data (default: true)
304
+ * @returns Promise that resolves with array of available agents
305
+ * @throws {SDKError} If not connected or operation fails
306
+ *
307
+ * @example
308
+ * ```typescript
309
+ * const availableAgents = await sdk.listAvailableAgents('room-123');
310
+ * console.log(`${availableAgents.length} agents can be added`);
311
+ * ```
312
+ */
313
+ public async listAvailableAgents(
314
+ roomId: string,
315
+ useCache: boolean = true
316
+ ): Promise<AgentRoomInfo[]> {
317
+ if (!this.wsClient.isConnected) {
318
+ throw new SDKError("Not connected to Teneo network", ErrorCode.NOT_CONNECTED);
319
+ }
320
+
321
+ // Validate input
322
+ this.validateRoomId(roomId);
323
+
324
+ // Check cache if enabled
325
+ if (useCache && this.isCacheValid(this.availableAgentsCacheTime, roomId)) {
326
+ const cached = this.availableAgentsCache.get(roomId);
327
+ if (cached) {
328
+ this.logger.debug("AgentRoomManager: Returning cached available agents", {
329
+ roomId,
330
+ count: cached.length
331
+ });
332
+ return cached.map((agent) => ({ ...agent })); // Return deep copy
333
+ }
334
+ }
335
+
336
+ this.logger.debug("AgentRoomManager: Listing available agents", { roomId });
337
+
338
+ const message = {
339
+ type: "list_available_agents" as const,
340
+ data: {
341
+ room_id: roomId
342
+ }
343
+ };
344
+
345
+ return new Promise((resolve, reject) => {
346
+ const timeout = setTimeout(() => {
347
+ cleanup();
348
+ reject(new SDKError("List available agents timeout", ErrorCode.TIMEOUT));
349
+ }, 30000);
350
+
351
+ const onSuccess = (agents: AgentRoomInfo[]) => {
352
+ cleanup();
353
+ resolve(agents);
354
+ };
355
+
356
+ const onError = (error: Error) => {
357
+ cleanup();
358
+ reject(error);
359
+ };
360
+
361
+ const cleanup = () => {
362
+ clearTimeout(timeout);
363
+ this.off("agent_room:available_agents_listed", onSuccess);
364
+ this.off("agent_room:list_available_error", onError);
365
+ };
366
+
367
+ this.once("agent_room:available_agents_listed", onSuccess);
368
+ this.once("agent_room:list_available_error", onError);
369
+
370
+ this.wsClient.sendMessage(message);
371
+ });
372
+ }
373
+
374
+ // ============================================================================
375
+ // QUERY METHODS (Synchronous, from cache)
376
+ // ============================================================================
377
+
378
+ /**
379
+ * Gets agents currently in a room from cache (synchronous).
380
+ * Returns undefined if not cached or cache expired.
381
+ *
382
+ * @param roomId - Room ID to query
383
+ * @returns Array of agents or undefined if not cached
384
+ *
385
+ * @example
386
+ * ```typescript
387
+ * const agents = sdk.getRoomAgents('room-123');
388
+ * if (agents) {
389
+ * console.log(`Found ${agents.length} agents in cache`);
390
+ * }
391
+ * ```
392
+ */
393
+ public getRoomAgents(roomId: string): AgentRoomInfo[] | undefined {
394
+ if (!this.isCacheValid(this.roomAgentsCacheTime, roomId)) {
395
+ return undefined;
396
+ }
397
+ const cached = this.roomAgentsCache.get(roomId);
398
+ return cached ? cached.map((agent) => ({ ...agent })) : undefined;
399
+ }
400
+
401
+ /**
402
+ * Gets available agents for a room from cache (synchronous).
403
+ * Returns undefined if not cached or cache expired.
404
+ *
405
+ * @param roomId - Room ID to query
406
+ * @returns Array of available agents or undefined if not cached
407
+ *
408
+ * @example
409
+ * ```typescript
410
+ * const available = sdk.getAvailableAgents('room-123');
411
+ * if (available) {
412
+ * console.log(`${available.length} agents can be added`);
413
+ * }
414
+ * ```
415
+ */
416
+ public getAvailableAgents(roomId: string): AgentRoomInfo[] | undefined {
417
+ if (!this.isCacheValid(this.availableAgentsCacheTime, roomId)) {
418
+ return undefined;
419
+ }
420
+ const cached = this.availableAgentsCache.get(roomId);
421
+ return cached ? cached.map((agent) => ({ ...agent })) : undefined;
422
+ }
423
+
424
+ /**
425
+ * Checks if an agent is currently in a room (from cache).
426
+ * Returns undefined if cache is invalid.
427
+ *
428
+ * @param roomId - Room ID to check
429
+ * @param agentId - Agent ID to check
430
+ * @returns True if agent in room, false if not, undefined if cache invalid
431
+ *
432
+ * @example
433
+ * ```typescript
434
+ * const isInRoom = sdk.isAgentInRoom('room-123', 'agent-456');
435
+ * if (isInRoom === true) {
436
+ * console.log('Agent is in this room');
437
+ * }
438
+ * ```
439
+ */
440
+ public isAgentInRoom(roomId: string, agentId: string): boolean | undefined {
441
+ const agents = this.getRoomAgents(roomId);
442
+ if (!agents) return undefined;
443
+ return agents.some((agent) => agent.agent_id === agentId);
444
+ }
445
+
446
+ /**
447
+ * Gets the count of agents in a room (from cache).
448
+ * Returns undefined if cache is invalid.
449
+ *
450
+ * @param roomId - Room ID to count agents for
451
+ * @returns Number of agents or undefined if cache invalid
452
+ *
453
+ * @example
454
+ * ```typescript
455
+ * const count = sdk.getRoomAgentCount('room-123');
456
+ * if (count !== undefined) {
457
+ * console.log(`Room has ${count} agents`);
458
+ * }
459
+ * ```
460
+ */
461
+ public getRoomAgentCount(roomId: string): number | undefined {
462
+ const agents = this.getRoomAgents(roomId);
463
+ return agents ? agents.length : undefined;
464
+ }
465
+
466
+ // ============================================================================
467
+ // CACHE MANAGEMENT (Public methods)
468
+ // ============================================================================
469
+
470
+ /**
471
+ * Manually invalidates all caches for a specific room.
472
+ * Useful after operations that might have changed agent assignments.
473
+ *
474
+ * @param roomId - Room ID to invalidate cache for
475
+ *
476
+ * @example
477
+ * ```typescript
478
+ * // After bulk operations
479
+ * sdk.invalidateCache('room-123');
480
+ * const freshAgents = await sdk.listRoomAgents('room-123', false);
481
+ * ```
482
+ */
483
+ public invalidateCache(roomId: string): void {
484
+ this.invalidateRoomCaches(roomId);
485
+ this.logger.debug("AgentRoomManager: Cache invalidated", { roomId });
486
+ }
487
+
488
+ /**
489
+ * Clears all caches for all rooms.
490
+ * Called automatically on disconnect.
491
+ * @internal
492
+ */
493
+ public clearAllCaches(): void {
494
+ this.roomAgentsCache.clear();
495
+ this.availableAgentsCache.clear();
496
+ this.roomAgentsCacheTime.clear();
497
+ this.availableAgentsCacheTime.clear();
498
+ this.logger.debug("AgentRoomManager: All caches cleared");
499
+ }
500
+
501
+ // ============================================================================
502
+ // INTERNAL METHODS
503
+ // ============================================================================
504
+
505
+ /**
506
+ * Handles agent status updates from server.
507
+ * Invalidates cache when agent status changes.
508
+ * @internal
509
+ */
510
+ public handleStatusUpdate(roomId: string, agentId: string, status: string): void {
511
+ this.logger.debug("AgentRoomManager: Agent status update", {
512
+ roomId,
513
+ agentId,
514
+ status
515
+ });
516
+
517
+ // Invalidate cache for this room as agent list may have changed
518
+ this.invalidateRoomCaches(roomId);
519
+ }
520
+
521
+ /**
522
+ * Called by handlers to cache room agents.
523
+ * @internal
524
+ */
525
+ public cacheRoomAgents(roomId: string, agents: AgentRoomInfo[]): void {
526
+ this.roomAgentsCache.set(roomId, agents);
527
+ this.roomAgentsCacheTime.set(roomId, Date.now());
528
+ this.logger.debug("AgentRoomManager: Cached room agents", {
529
+ roomId,
530
+ count: agents.length
531
+ });
532
+ }
533
+
534
+ /**
535
+ * Called by handlers to cache available agents.
536
+ * @internal
537
+ */
538
+ public cacheAvailableAgents(roomId: string, agents: AgentRoomInfo[]): void {
539
+ this.availableAgentsCache.set(roomId, agents);
540
+ this.availableAgentsCacheTime.set(roomId, Date.now());
541
+ this.logger.debug("AgentRoomManager: Cached available agents", {
542
+ roomId,
543
+ count: agents.length
544
+ });
545
+ }
546
+
547
+ // ============================================================================
548
+ // PRIVATE HELPERS
549
+ // ============================================================================
550
+
551
+ /**
552
+ * Checks if cache is still valid (within TTL)
553
+ */
554
+ private isCacheValid(cacheTimeMap: Map<string, number>, roomId: string): boolean {
555
+ const cacheTime = cacheTimeMap.get(roomId);
556
+ if (!cacheTime) return false;
557
+
558
+ const age = Date.now() - cacheTime;
559
+ return age < CACHE_TTL_MS;
560
+ }
561
+
562
+ /**
563
+ * Invalidates all caches for a specific room
564
+ */
565
+ private invalidateRoomCaches(roomId: string): void {
566
+ this.roomAgentsCache.delete(roomId);
567
+ this.availableAgentsCache.delete(roomId);
568
+ this.roomAgentsCacheTime.delete(roomId);
569
+ this.availableAgentsCacheTime.delete(roomId);
570
+ }
571
+
572
+ /**
573
+ * Verifies user owns the room
574
+ */
575
+ private validateRoomId(roomId: string): void {
576
+ if (!roomId || roomId.trim() === "") {
577
+ throw new SDKError("Room ID cannot be empty", ErrorCode.VALIDATION_ERROR);
578
+ }
579
+ }
580
+
581
+ private validateAgentId(agentId: string): void {
582
+ if (!agentId || agentId.trim() === "") {
583
+ throw new SDKError("Agent ID cannot be empty", ErrorCode.VALIDATION_ERROR);
584
+ }
585
+ }
586
+
587
+ private roomExists(roomId: string): boolean {
588
+ if (!this.roomManagementManager) return true; // Skip check if manager not available
589
+
590
+ // If getRoomById method doesn't exist, skip check
591
+ if (typeof this.roomManagementManager.getRoomById !== "function") {
592
+ return true;
593
+ }
594
+
595
+ // Check if room exists (in owned or shared rooms)
596
+ const room = this.roomManagementManager.getRoomById(roomId);
597
+ return room !== undefined;
598
+ }
599
+
600
+ private verifyOwnership(roomId: string): boolean {
601
+ if (!this.roomManagementManager) return true; // Skip check if manager not available
602
+
603
+ // Check if room is in owned rooms
604
+ const ownedRooms = this.roomManagementManager.getOwnedRooms?.();
605
+ if (!ownedRooms) return true; // Skip check if method not available
606
+
607
+ return ownedRooms.some((room: any) => room.id === roomId);
608
+ }
609
+ }
@@ -5,5 +5,7 @@
5
5
 
6
6
  export { ConnectionManager } from "./connection-manager";
7
7
  export { RoomManager } from "./room-manager";
8
+ export { RoomManagementManager } from "./room-management-manager";
9
+ export { AgentRoomManager, type AgentRoomInfo } from "./agent-room-manager";
8
10
  export { AgentRegistry } from "./agent-registry";
9
11
  export { MessageRouter, type SendMessageOptions, type AgentCommand } from "./message-router";