@teneo-protocol/sdk 1.0.0 → 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 (211) 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 +13 -0
  5. package/dist/core/websocket-client.d.ts.map +1 -1
  6. package/dist/core/websocket-client.js +34 -3
  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/regular-message-handler.js +1 -0
  53. package/dist/handlers/message-handlers/regular-message-handler.js.map +1 -1
  54. package/dist/handlers/message-handlers/room-operation-response-handler.d.ts +328 -0
  55. package/dist/handlers/message-handlers/room-operation-response-handler.d.ts.map +1 -0
  56. package/dist/handlers/message-handlers/room-operation-response-handler.js +92 -0
  57. package/dist/handlers/message-handlers/room-operation-response-handler.js.map +1 -0
  58. package/dist/handlers/message-handlers/subscribe-response-handler.d.ts +53 -31
  59. package/dist/handlers/message-handlers/subscribe-response-handler.d.ts.map +1 -1
  60. package/dist/handlers/message-handlers/types.d.ts +2 -0
  61. package/dist/handlers/message-handlers/types.d.ts.map +1 -1
  62. package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts +53 -31
  63. package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts.map +1 -1
  64. package/dist/managers/agent-room-manager.d.ts +222 -0
  65. package/dist/managers/agent-room-manager.d.ts.map +1 -0
  66. package/dist/managers/agent-room-manager.js +508 -0
  67. package/dist/managers/agent-room-manager.js.map +1 -0
  68. package/dist/managers/index.d.ts +2 -0
  69. package/dist/managers/index.d.ts.map +1 -1
  70. package/dist/managers/index.js +5 -1
  71. package/dist/managers/index.js.map +1 -1
  72. package/dist/managers/message-router.d.ts +1 -1
  73. package/dist/managers/message-router.d.ts.map +1 -1
  74. package/dist/managers/message-router.js +41 -4
  75. package/dist/managers/message-router.js.map +1 -1
  76. package/dist/managers/room-management-manager.d.ts +213 -0
  77. package/dist/managers/room-management-manager.d.ts.map +1 -0
  78. package/dist/managers/room-management-manager.js +440 -0
  79. package/dist/managers/room-management-manager.js.map +1 -0
  80. package/dist/managers/room-manager.d.ts +4 -4
  81. package/dist/managers/room-manager.d.ts.map +1 -1
  82. package/dist/managers/room-manager.js +1 -1
  83. package/dist/managers/room-manager.js.map +1 -1
  84. package/dist/teneo-sdk.d.ts +362 -14
  85. package/dist/teneo-sdk.d.ts.map +1 -1
  86. package/dist/teneo-sdk.js +497 -7
  87. package/dist/teneo-sdk.js.map +1 -1
  88. package/dist/types/config.d.ts +63 -54
  89. package/dist/types/config.d.ts.map +1 -1
  90. package/dist/types/config.js +9 -5
  91. package/dist/types/config.js.map +1 -1
  92. package/dist/types/error-codes.d.ts +2 -0
  93. package/dist/types/error-codes.d.ts.map +1 -1
  94. package/dist/types/error-codes.js +3 -0
  95. package/dist/types/error-codes.js.map +1 -1
  96. package/dist/types/events.d.ts +132 -68
  97. package/dist/types/events.d.ts.map +1 -1
  98. package/dist/types/events.js.map +1 -1
  99. package/dist/types/index.d.ts +1 -1
  100. package/dist/types/index.d.ts.map +1 -1
  101. package/dist/types/index.js +27 -2
  102. package/dist/types/index.js.map +1 -1
  103. package/dist/types/messages.d.ts +11396 -2559
  104. package/dist/types/messages.d.ts.map +1 -1
  105. package/dist/types/messages.js +294 -27
  106. package/dist/types/messages.js.map +1 -1
  107. package/dist/types/validation.d.ts.map +1 -1
  108. package/dist/types/validation.js +1 -1
  109. package/dist/types/validation.js.map +1 -1
  110. package/dist/utils/bounded-queue.d.ts +1 -1
  111. package/dist/utils/bounded-queue.js +6 -6
  112. package/dist/utils/circuit-breaker.d.ts.map +1 -1
  113. package/dist/utils/circuit-breaker.js.map +1 -1
  114. package/dist/utils/event-waiter.d.ts.map +1 -1
  115. package/dist/utils/event-waiter.js +2 -1
  116. package/dist/utils/event-waiter.js.map +1 -1
  117. package/dist/utils/rate-limiter.d.ts.map +1 -1
  118. package/dist/utils/rate-limiter.js +4 -6
  119. package/dist/utils/rate-limiter.js.map +1 -1
  120. package/dist/utils/secure-private-key.d.ts.map +1 -1
  121. package/dist/utils/secure-private-key.js +9 -15
  122. package/dist/utils/secure-private-key.js.map +1 -1
  123. package/dist/utils/signature-verifier.d.ts +2 -2
  124. package/dist/utils/signature-verifier.d.ts.map +1 -1
  125. package/dist/utils/signature-verifier.js +5 -5
  126. package/dist/utils/signature-verifier.js.map +1 -1
  127. package/examples/.env.example +1 -1
  128. package/examples/agent-room-management-example.ts +334 -0
  129. package/examples/claude-agent-x-follower/.env.example +117 -0
  130. package/examples/claude-agent-x-follower/QUICKSTART.md +243 -0
  131. package/examples/claude-agent-x-follower/README.md +540 -0
  132. package/examples/claude-agent-x-follower/index.ts +248 -0
  133. package/examples/claude-agent-x-follower/package.json +37 -0
  134. package/examples/claude-agent-x-follower/tsconfig.json +20 -0
  135. package/examples/n8n-teneo/.env.example +127 -0
  136. package/examples/n8n-teneo/Dockerfile +42 -0
  137. package/examples/n8n-teneo/README.md +564 -0
  138. package/examples/n8n-teneo/docker-compose.yml +71 -0
  139. package/examples/n8n-teneo/index.ts +177 -0
  140. package/examples/n8n-teneo/package.json +22 -0
  141. package/examples/n8n-teneo/tsconfig.json +12 -0
  142. package/examples/n8n-teneo/workflows/x-timeline.json +66 -0
  143. package/examples/openai-teneo/.env.example +130 -0
  144. package/examples/openai-teneo/README.md +635 -0
  145. package/examples/openai-teneo/index.ts +280 -0
  146. package/examples/openai-teneo/package.json +24 -0
  147. package/examples/openai-teneo/tsconfig.json +16 -0
  148. package/examples/production-dashboard/.env.example +5 -3
  149. package/examples/production-dashboard/README.md +839 -0
  150. package/examples/production-dashboard/pnpm-lock.yaml +92 -0
  151. package/examples/production-dashboard/public/dashboard.html +1150 -504
  152. package/examples/production-dashboard/server.ts +428 -12
  153. package/examples/room-management-example.ts +285 -0
  154. package/examples/usage/.env.example +17 -0
  155. package/examples/usage/01-connect.ts +116 -0
  156. package/examples/usage/02-list-agents.ts +153 -0
  157. package/examples/usage/03-pick-agent.ts +201 -0
  158. package/examples/usage/04-find-by-capability.ts +237 -0
  159. package/examples/usage/05-webhook-example.ts +319 -0
  160. package/examples/usage/06-simple-api-server.ts +396 -0
  161. package/examples/usage/07-event-listener.ts +402 -0
  162. package/examples/usage/README.md +383 -0
  163. package/examples/usage/package.json +42 -0
  164. package/package.json +13 -3
  165. package/src/core/websocket-client.ts +43 -9
  166. package/src/formatters/response-formatter.test.ts +8 -2
  167. package/src/handlers/message-handlers/agent-room-operation-response-handler.ts +83 -0
  168. package/src/handlers/message-handlers/agent-status-update-handler.ts +58 -0
  169. package/src/handlers/message-handlers/auth-message-handler.ts +73 -5
  170. package/src/handlers/message-handlers/auth-success-handler.ts +58 -6
  171. package/src/handlers/message-handlers/index.ts +19 -0
  172. package/src/handlers/message-handlers/list-available-agents-handler.ts +41 -0
  173. package/src/handlers/message-handlers/list-room-agents-handler.ts +61 -0
  174. package/src/handlers/message-handlers/regular-message-handler.ts +1 -0
  175. package/src/handlers/message-handlers/room-operation-response-handler.ts +105 -0
  176. package/src/handlers/message-handlers/types.ts +6 -0
  177. package/src/handlers/webhook-handler.test.ts +13 -10
  178. package/src/managers/agent-room-manager.ts +609 -0
  179. package/src/managers/index.ts +2 -0
  180. package/src/managers/message-router.ts +48 -6
  181. package/src/managers/room-management-manager.ts +523 -0
  182. package/src/managers/room-manager.ts +12 -6
  183. package/src/teneo-sdk.ts +543 -10
  184. package/src/types/config.ts +13 -6
  185. package/src/types/error-codes.ts +4 -0
  186. package/src/types/events.ts +24 -0
  187. package/src/types/index.ts +55 -0
  188. package/src/types/messages.ts +374 -41
  189. package/src/types/validation.ts +4 -1
  190. package/src/utils/bounded-queue.ts +9 -9
  191. package/src/utils/circuit-breaker.ts +4 -1
  192. package/src/utils/deduplication-cache.test.ts +2 -6
  193. package/src/utils/event-waiter.test.ts +4 -1
  194. package/src/utils/event-waiter.ts +5 -7
  195. package/src/utils/rate-limiter.test.ts +5 -17
  196. package/src/utils/rate-limiter.ts +6 -9
  197. package/src/utils/secure-private-key.test.ts +66 -59
  198. package/src/utils/secure-private-key.ts +10 -16
  199. package/src/utils/signature-verifier.test.ts +75 -70
  200. package/src/utils/signature-verifier.ts +7 -8
  201. package/src/utils/ssrf-validator.test.ts +3 -3
  202. package/tests/integration/room-management.test.ts +514 -0
  203. package/tests/integration/websocket.test.ts +1 -1
  204. package/tests/unit/handlers/agent-room-operation-response-handler.test.ts +394 -0
  205. package/tests/unit/handlers/agent-status-update-handler.test.ts +407 -0
  206. package/tests/unit/handlers/auth-success-handler-rooms.test.ts +699 -0
  207. package/tests/unit/handlers/list-available-agents-handler.test.ts +256 -0
  208. package/tests/unit/handlers/list-room-agents-handler.test.ts +294 -0
  209. package/tests/unit/handlers/room-operation-response-handler.test.ts +527 -0
  210. package/tests/unit/managers/agent-room-manager.test.ts +534 -0
  211. package/tests/unit/managers/room-management-manager.test.ts +438 -0
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Handler for agent_status_update messages (v2.0.0)
3
+ * Processes real-time agent status updates for rooms
4
+ */
5
+
6
+ import {
7
+ AgentStatusUpdateMessage,
8
+ AgentStatusUpdateMessageSchema
9
+ } from "../../types";
10
+ import { BaseMessageHandler } from "./base-handler";
11
+ import { HandlerContext } from "./types";
12
+
13
+ export class AgentStatusUpdateHandler extends BaseMessageHandler<AgentStatusUpdateMessage> {
14
+ readonly type = "agent_status_update" as const;
15
+ readonly schema = AgentStatusUpdateMessageSchema;
16
+
17
+ protected handleValidated(
18
+ message: AgentStatusUpdateMessage,
19
+ context: HandlerContext
20
+ ): void {
21
+ const { room_id, agent_id, status, agent } = message.data;
22
+
23
+ context.logger.debug("Handling agent_status_update", {
24
+ roomId: room_id,
25
+ agentId: agent_id,
26
+ status,
27
+ hasAgent: !!agent
28
+ });
29
+
30
+ // Invalidate cache for this room via agent room manager
31
+ const agentRoomManager = (context as any).agentRoomManager;
32
+ if (agentRoomManager && typeof agentRoomManager.handleStatusUpdate === "function") {
33
+ agentRoomManager.handleStatusUpdate(room_id, agent_id, status);
34
+ }
35
+
36
+ context.logger.info("Agent status updated", {
37
+ roomId: room_id,
38
+ agentId: agent_id,
39
+ status
40
+ });
41
+
42
+ // Emit status update event
43
+ this.emit(context, "agent_room:status_update", {
44
+ roomId: room_id,
45
+ agentId: agent_id,
46
+ status,
47
+ agent
48
+ });
49
+
50
+ // Send webhook
51
+ this.sendWebhook(context, "agent_status_update", {
52
+ room_id,
53
+ agent_id,
54
+ status,
55
+ agent
56
+ });
57
+ }
58
+ }
@@ -26,8 +26,27 @@ export class AuthMessageHandler extends BaseMessageHandler<AuthMessage> {
26
26
  isCachedAuth ? "Using cached authentication" : "Authentication successful"
27
27
  );
28
28
 
29
- // Extract rooms
30
- const rooms = this.extractRooms(message.data?.rooms);
29
+ // Extract rooms from both 'rooms' (public) and 'private_rooms' (owned private) arrays
30
+ const publicRooms = this.extractRooms(message.data?.rooms);
31
+ const privateRooms = this.extractRooms(message.data?.private_rooms);
32
+
33
+ // Combine all rooms, ensuring correct ownership flags
34
+ // Public rooms from 'rooms' array are NOT owned by the user
35
+ // Private rooms from 'private_rooms' array ARE owned by the user
36
+ const allRooms = [
37
+ ...publicRooms.map((r) => ({
38
+ ...r,
39
+ is_owner: false, // Explicitly set to false - public rooms are not owned
40
+ is_public: r.is_public !== undefined ? r.is_public : true // Ensure is_public is set
41
+ })),
42
+ ...privateRooms.map((r) => ({
43
+ ...r,
44
+ is_owner: true, // Explicitly set to true - private_rooms array means owned
45
+ is_public: r.is_public !== undefined ? r.is_public : false // Ensure is_public is set
46
+ }))
47
+ ];
48
+
49
+ const { privateRoomIds, sharedRoomIds } = this.categorizeRooms(allRooms);
31
50
 
32
51
  // Update connection state
33
52
  this.updateConnectionState(context, { authenticated: true });
@@ -40,11 +59,37 @@ export class AuthMessageHandler extends BaseMessageHandler<AuthMessage> {
40
59
  isWhitelisted: message.data?.is_whitelisted,
41
60
  isAdmin: message.data?.is_admin_whitelisted,
42
61
  nftVerified: message.data?.nft_verified,
43
- rooms: rooms.map((r) => r.id),
44
- roomObjects: rooms,
45
- privateRoomId: message.data?.private_room_id
62
+ rooms: allRooms.map((r) => r.id),
63
+ roomObjects: allRooms,
64
+ privateRoomId: message.data?.private_room_id,
65
+ // v2.0.0: New fields
66
+ privateRoomIds, // Rooms user owns
67
+ sharedRoomIds, // Rooms user is member of
68
+ maxPrivateRooms: message.data?.max_private_rooms // Max rooms user can create
46
69
  });
47
70
 
71
+ // Initialize room management manager with room data (v2.0.0)
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ const roomMgmt = (context as any).roomManagementManager;
74
+ if (roomMgmt) {
75
+ // Set room limit
76
+ if (message.data?.max_private_rooms) {
77
+ roomMgmt.setRoomLimit(message.data.max_private_rooms);
78
+ }
79
+
80
+ // Categorize and cache rooms
81
+ const ownedRooms = allRooms.filter((r) => r.is_owner === true);
82
+ const sharedRooms = allRooms.filter((r) => r.is_owner === false);
83
+ roomMgmt.setOwnedRooms(ownedRooms);
84
+ roomMgmt.setSharedRooms(sharedRooms);
85
+
86
+ context.logger.debug("Room management initialized from auth message", {
87
+ owned: ownedRooms.length,
88
+ shared: sharedRooms.length,
89
+ limit: message.data?.max_private_rooms
90
+ });
91
+ }
92
+
48
93
  // Get updated auth state
49
94
  const authState = context.getAuthState();
50
95
 
@@ -63,4 +108,27 @@ export class AuthMessageHandler extends BaseMessageHandler<AuthMessage> {
63
108
  }
64
109
  return rooms;
65
110
  }
111
+
112
+ /**
113
+ * Categorize rooms into owned vs member rooms based on is_owner flag
114
+ * @param rooms - Array of room info objects
115
+ * @returns Object with privateRoomIds (owned) and sharedRoomIds (member)
116
+ */
117
+ private categorizeRooms(rooms: Room[]): {
118
+ privateRoomIds: string[];
119
+ sharedRoomIds: string[];
120
+ } {
121
+ const privateRoomIds: string[] = [];
122
+ const sharedRoomIds: string[] = [];
123
+
124
+ for (const room of rooms) {
125
+ if (room.is_owner) {
126
+ if (room.id) privateRoomIds.push(room.id);
127
+ } else {
128
+ if (room.id) sharedRoomIds.push(room.id);
129
+ }
130
+ }
131
+
132
+ return { privateRoomIds, sharedRoomIds };
133
+ }
66
134
  }
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { z } from "zod";
7
- import { AuthSuccessMessage, AuthSuccessMessageSchema, Room } from "../../types";
7
+ import { AuthSuccessMessage, AuthSuccessMessageSchema, RoomInfo } from "../../types";
8
8
  import { BaseMessageHandler } from "./base-handler";
9
9
  import { HandlerContext } from "./types";
10
10
 
@@ -18,8 +18,9 @@ export class AuthSuccessHandler extends BaseMessageHandler<AuthSuccessMessage> {
18
18
  ): Promise<void> {
19
19
  context.logger.info("Authentication successful");
20
20
 
21
- // Extract rooms
21
+ // Extract and categorize rooms
22
22
  const rooms = this.extractRooms(message.data.rooms);
23
+ const { privateRoomIds, sharedRoomIds } = this.categorizeRooms(rooms);
23
24
 
24
25
  // Update connection state
25
26
  this.updateConnectionState(context, { authenticated: true });
@@ -32,14 +33,42 @@ export class AuthSuccessHandler extends BaseMessageHandler<AuthSuccessMessage> {
32
33
  isWhitelisted: message.data.is_whitelisted,
33
34
  isAdmin: message.data.is_admin_whitelisted,
34
35
  nftVerified: message.data.nft_verified,
35
- rooms: rooms.map((r) => r.id),
36
- roomObjects: rooms,
37
- privateRoomId: message.data.private_room_id
36
+
37
+ // Backward compatibility: deprecated fields
38
+ rooms: rooms.map((r) => r.id), // All room IDs
39
+ privateRoomId: message.data.private_room_id, // DEPRECATED: single private room ID
40
+
41
+ // v2.0.0: New fields
42
+ roomObjects: rooms, // Full room objects with is_owner field
43
+ privateRoomIds, // Rooms user owns
44
+ sharedRoomIds, // Rooms user is member of
45
+ maxPrivateRooms: message.data.max_private_rooms // Max rooms user can create
38
46
  });
39
47
 
40
48
  // Get updated auth state
41
49
  const authState = context.getAuthState();
42
50
 
51
+ // Initialize room management manager with room data (v2.0.0)
52
+ const roomMgmt = (context as any).roomManagementManager;
53
+ if (roomMgmt) {
54
+ // Set room limit
55
+ if (message.data.max_private_rooms) {
56
+ roomMgmt.setRoomLimit(message.data.max_private_rooms);
57
+ }
58
+
59
+ // Categorize and cache rooms
60
+ const ownedRooms = rooms.filter((r) => r.is_owner);
61
+ const sharedRooms = rooms.filter((r) => !r.is_owner);
62
+ roomMgmt.setOwnedRooms(ownedRooms);
63
+ roomMgmt.setSharedRooms(sharedRooms);
64
+
65
+ context.logger.debug("Room management initialized", {
66
+ owned: ownedRooms.length,
67
+ shared: sharedRooms.length,
68
+ limit: message.data.max_private_rooms
69
+ });
70
+ }
71
+
43
72
  // Emit events
44
73
  this.emit(context, "auth:success", authState);
45
74
  this.emit(context, "ready");
@@ -48,10 +77,33 @@ export class AuthSuccessHandler extends BaseMessageHandler<AuthSuccessMessage> {
48
77
  /**
49
78
  * Extract and normalize rooms from auth data
50
79
  */
51
- private extractRooms(rooms?: Room[]): Room[] {
80
+ private extractRooms(rooms?: RoomInfo[] | null): RoomInfo[] {
52
81
  if (!rooms || !Array.isArray(rooms)) {
53
82
  return [];
54
83
  }
55
84
  return rooms;
56
85
  }
86
+
87
+ /**
88
+ * Categorize rooms into owned vs member rooms based on is_owner flag
89
+ * @param rooms - Array of room info objects
90
+ * @returns Object with privateRoomIds (owned) and sharedRoomIds (member)
91
+ */
92
+ private categorizeRooms(rooms: RoomInfo[]): {
93
+ privateRoomIds: string[];
94
+ sharedRoomIds: string[];
95
+ } {
96
+ const privateRoomIds: string[] = [];
97
+ const sharedRoomIds: string[] = [];
98
+
99
+ for (const room of rooms) {
100
+ if (room.is_owner) {
101
+ privateRoomIds.push(room.id);
102
+ } else {
103
+ sharedRoomIds.push(room.id);
104
+ }
105
+ }
106
+
107
+ return { privateRoomIds, sharedRoomIds };
108
+ }
57
109
  }
@@ -22,6 +22,11 @@ export { PingHandler, PongHandler } from "./ping-pong-handler";
22
22
  export { SubscribeResponseHandler } from "./subscribe-response-handler";
23
23
  export { UnsubscribeResponseHandler } from "./unsubscribe-response-handler";
24
24
  export { ListRoomsResponseHandler } from "./list-rooms-response-handler";
25
+ export { RoomOperationResponseHandler } from "./room-operation-response-handler";
26
+ export { AgentRoomOperationResponseHandler } from "./agent-room-operation-response-handler";
27
+ export { ListRoomAgentsHandler } from "./list-room-agents-handler";
28
+ export { ListAvailableAgentsHandler } from "./list-available-agents-handler";
29
+ export { AgentStatusUpdateHandler } from "./agent-status-update-handler";
25
30
 
26
31
  // Import for convenience function
27
32
  import { TaskResponseHandler } from "./task-response-handler";
@@ -38,6 +43,11 @@ import { PingHandler, PongHandler } from "./ping-pong-handler";
38
43
  import { SubscribeResponseHandler } from "./subscribe-response-handler";
39
44
  import { UnsubscribeResponseHandler } from "./unsubscribe-response-handler";
40
45
  import { ListRoomsResponseHandler } from "./list-rooms-response-handler";
46
+ import { RoomOperationResponseHandler } from "./room-operation-response-handler";
47
+ import { AgentRoomOperationResponseHandler } from "./agent-room-operation-response-handler";
48
+ import { ListRoomAgentsHandler } from "./list-room-agents-handler";
49
+ import { ListAvailableAgentsHandler } from "./list-available-agents-handler";
50
+ import { AgentStatusUpdateHandler } from "./agent-status-update-handler";
41
51
  import { MessageHandler } from "./types";
42
52
 
43
53
  /**
@@ -70,6 +80,15 @@ export function getDefaultHandlers(
70
80
  new UnsubscribeResponseHandler(),
71
81
  new ListRoomsResponseHandler(),
72
82
 
83
+ // Room Management handlers (v2.0.0)
84
+ new RoomOperationResponseHandler(),
85
+
86
+ // Agent Room Management handlers (v2.0.0)
87
+ new AgentRoomOperationResponseHandler(),
88
+ new ListRoomAgentsHandler(),
89
+ new ListAvailableAgentsHandler(),
90
+ new AgentStatusUpdateHandler(),
91
+
73
92
  // Keepalive handlers
74
93
  new PingHandler(),
75
94
  new PongHandler()
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Handler for available_agents_response messages (v2.0.0)
3
+ * Processes responses from list_available_agents requests
4
+ */
5
+
6
+ import { AvailableAgentsResponse, AvailableAgentsResponseSchema } from "../../types";
7
+ import { BaseMessageHandler } from "./base-handler";
8
+ import { HandlerContext } from "./types";
9
+ import { AgentRoomInfo } from "../../managers/agent-room-manager";
10
+
11
+ export class ListAvailableAgentsHandler extends BaseMessageHandler<AvailableAgentsResponse> {
12
+ readonly type = "available_agents_response" as const;
13
+ readonly schema = AvailableAgentsResponseSchema;
14
+
15
+ protected handleValidated(message: AvailableAgentsResponse, context: HandlerContext): void {
16
+ const { agents } = message.data;
17
+
18
+ context.logger.debug("Handling available_agents_response", {
19
+ agentCount: agents?.length || 0
20
+ });
21
+
22
+ // Parse agents array (handle undefined as empty array)
23
+ const agentList: AgentRoomInfo[] = agents || [];
24
+
25
+ context.logger.info("Available agents listed", {
26
+ count: agentList.length
27
+ });
28
+
29
+ // Note: We don't cache this globally since it's room-specific
30
+ // The AgentRoomManager will cache it with the room context
31
+
32
+ // Emit success event
33
+ this.emit(context, "agent_room:available_agents_listed", agentList);
34
+
35
+ // Send webhook
36
+ this.sendWebhook(context, "available_agents_listed", {
37
+ agents: agentList,
38
+ count: agentList.length
39
+ });
40
+ }
41
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Handler for room_agents_response messages (v2.0.0)
3
+ * Processes responses from list_room_agents requests
4
+ */
5
+
6
+ import { RoomAgentsResponse, RoomAgentsResponseSchema } from "../../types";
7
+ import { BaseMessageHandler } from "./base-handler";
8
+ import { HandlerContext } from "./types";
9
+ import { SDKError } from "../../types/events";
10
+ import { ErrorCode } from "../../types/error-codes";
11
+ import { AgentRoomInfo } from "../../managers/agent-room-manager";
12
+
13
+ export class ListRoomAgentsHandler extends BaseMessageHandler<RoomAgentsResponse> {
14
+ readonly type = "room_agents_response" as const;
15
+ readonly schema = RoomAgentsResponseSchema;
16
+
17
+ protected handleValidated(
18
+ message: RoomAgentsResponse,
19
+ context: HandlerContext
20
+ ): void {
21
+ const { room_id, agents } = message.data;
22
+
23
+ context.logger.debug("Handling room_agents_response", {
24
+ roomId: room_id,
25
+ agentCount: agents?.length || 0
26
+ });
27
+
28
+ if (!room_id) {
29
+ const error = new SDKError(
30
+ "Room agents response missing room_id",
31
+ ErrorCode.VALIDATION_ERROR
32
+ );
33
+ this.emit(context, "agent_room:list_error", error, undefined);
34
+ return;
35
+ }
36
+
37
+ // Parse agents array (handle undefined as empty array)
38
+ const agentList: AgentRoomInfo[] = agents || [];
39
+
40
+ context.logger.info("Room agents listed", {
41
+ roomId: room_id,
42
+ count: agentList.length
43
+ });
44
+
45
+ // Cache via agent room manager if available
46
+ const agentRoomManager = (context as any).agentRoomManager;
47
+ if (agentRoomManager && typeof agentRoomManager.cacheRoomAgents === "function") {
48
+ agentRoomManager.cacheRoomAgents(room_id, agentList);
49
+ }
50
+
51
+ // Emit success event
52
+ this.emit(context, "agent_room:agents_listed", room_id, agentList);
53
+
54
+ // Send webhook
55
+ this.sendWebhook(context, "room_agents_listed", {
56
+ room_id,
57
+ agents: agentList,
58
+ count: agentList.length
59
+ });
60
+ }
61
+ }
@@ -54,6 +54,7 @@ export class RegularMessageHandler extends BaseMessageHandler<UserMessage> {
54
54
  contentType: message.content_type || "text/plain",
55
55
  success: true,
56
56
  timestamp: new Date(),
57
+ raw: message as any, // Include raw message for request correlation
57
58
  humanized:
58
59
  typeof message.content === "string" ? message.content : JSON.stringify(message.content)
59
60
  };
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Handler for room_operation_response messages (v2.0.0)
3
+ * Processes responses from room CRUD operations (create, update, delete)
4
+ */
5
+
6
+ import { RoomOperationResponse, RoomOperationResponseSchema } from "../../types";
7
+ import { BaseMessageHandler } from "./base-handler";
8
+ import { HandlerContext } from "./types";
9
+ import { SDKError } from "../../types/events";
10
+ import { ErrorCode } from "../../types/error-codes";
11
+
12
+ export class RoomOperationResponseHandler extends BaseMessageHandler<RoomOperationResponse> {
13
+ readonly type = "room_operation_response" as const;
14
+ readonly schema = RoomOperationResponseSchema;
15
+
16
+ protected handleValidated(
17
+ message: RoomOperationResponse,
18
+ context: HandlerContext
19
+ ): void {
20
+ const { success, message: errorMessage, room_id, room } = message.data;
21
+
22
+ context.logger.debug("Handling room_operation_response", {
23
+ success,
24
+ roomId: room_id,
25
+ hasRoom: !!room
26
+ });
27
+
28
+ if (!success) {
29
+ // Operation failed - emit error event
30
+ const error = new SDKError(
31
+ errorMessage || "Room operation failed",
32
+ ErrorCode.OPERATION_FAILED
33
+ );
34
+
35
+ // Try to determine operation type from message
36
+ // Note: Server should ideally send operation type in response
37
+ // For now we emit generic errors and specific listeners will catch relevant ones
38
+ this.emit(context, "room:create_error", error);
39
+ this.emit(context, "room:update_error", error, room_id);
40
+ this.emit(context, "room:delete_error", error, room_id);
41
+
42
+ // Send webhook
43
+ this.sendWebhook(context, "room_operation_error", {
44
+ success: false,
45
+ message: errorMessage,
46
+ room_id
47
+ });
48
+
49
+ return;
50
+ }
51
+
52
+ // Operation succeeded
53
+ if (room) {
54
+ // Create or Update operation (includes room object)
55
+ context.logger.info("Room operation succeeded", {
56
+ roomId: room.id,
57
+ roomName: room.name
58
+ });
59
+
60
+ // Determine if this was create or update based on whether we have the room cached
61
+ // Note: Ideally server would send operation type in response
62
+ // For now, handlers will emit both and listeners filter by room_id
63
+
64
+ // Update cache via room management manager if available
65
+ const roomManager = (context as any).roomManagementManager;
66
+ if (roomManager && typeof roomManager.upsertRoom === "function") {
67
+ roomManager.upsertRoom(room);
68
+ }
69
+
70
+ // Emit success events
71
+ // The promise handlers in RoomManagementManager will filter by room_id
72
+ this.emit(context, "room:created", room);
73
+ this.emit(context, "room:updated", room);
74
+
75
+ // Send webhook
76
+ this.sendWebhook(context, "room_operation", {
77
+ success: true,
78
+ room,
79
+ message: "Room operation completed successfully"
80
+ });
81
+ } else if (room_id) {
82
+ // Delete operation (only has room_id, no room object)
83
+ context.logger.info("Room deleted", { roomId: room_id });
84
+
85
+ // Remove from cache
86
+ const roomManager = (context as any).roomManagementManager;
87
+ if (roomManager && typeof roomManager.removeRoom === "function") {
88
+ roomManager.removeRoom(room_id);
89
+ }
90
+
91
+ // Emit delete success
92
+ this.emit(context, "room:deleted", room_id);
93
+
94
+ // Send webhook
95
+ this.sendWebhook(context, "room_deleted", {
96
+ success: true,
97
+ room_id,
98
+ message: "Room deleted successfully"
99
+ });
100
+ } else {
101
+ // Unexpected: success but no room or room_id
102
+ context.logger.warn("Room operation succeeded but no room data provided");
103
+ }
104
+ }
105
+ }
@@ -34,6 +34,12 @@ export interface HandlerContext {
34
34
  // Room manager for subscription updates (optional)
35
35
  roomManager?: any;
36
36
 
37
+ // Room management manager for CRUD operations (v2.0.0, optional)
38
+ roomManagementManager?: any;
39
+
40
+ // Agent room manager for agent-room operations (v2.0.0, optional)
41
+ agentRoomManager?: any;
42
+
37
43
  // Account for signing (optional, for auth handlers)
38
44
  account?: PrivateKeyAccount;
39
45
 
@@ -118,7 +118,10 @@ describe("WebhookHandler", () => {
118
118
  expect(queue[0].payload.timestamp).toBeDefined();
119
119
  expect(queue[1].payload.timestamp).toBeDefined();
120
120
  // Each webhook gets unique timestamp
121
- expect(queue[0].payload.timestamp !== queue[1].payload.timestamp || queue[0].payload.data.content !== queue[1].payload.data.content).toBe(true);
121
+ expect(
122
+ queue[0].payload.timestamp !== queue[1].payload.timestamp ||
123
+ queue[0].payload.data.content !== queue[1].payload.data.content
124
+ ).toBe(true);
122
125
  });
123
126
 
124
127
  it("should include timestamp in webhook payload", async () => {
@@ -584,20 +587,17 @@ describe("WebhookHandler", () => {
584
587
 
585
588
  describe("Protocol Validation", () => {
586
589
  it("should reject HTTP for non-localhost without allowInsecureWebhooks", () => {
587
- const urls = [
588
- "http://example.com/webhook"
589
- ];
590
+ const urls = ["http://example.com/webhook"];
590
591
 
591
592
  urls.forEach((url) => {
592
593
  expect(() => (handler as any).validateWebhookUrl(url)).toThrow(WebhookError);
593
- expect(() => (handler as any).validateWebhookUrl(url)).toThrow(/must use HTTPS|Only HTTPS/i);
594
+ expect(() => (handler as any).validateWebhookUrl(url)).toThrow(
595
+ /must use HTTPS|Only HTTPS/i
596
+ );
594
597
  });
595
598
 
596
599
  // Private IPs are blocked for different reason (private IP, not HTTPS requirement)
597
- const privateUrls = [
598
- "http://192.168.1.1/webhook",
599
- "http://10.0.0.1/webhook"
600
- ];
600
+ const privateUrls = ["http://192.168.1.1/webhook", "http://10.0.0.1/webhook"];
601
601
  privateUrls.forEach((url) => {
602
602
  expect(() => (handler as any).validateWebhookUrl(url)).toThrow(WebhookError);
603
603
  expect(() => (handler as any).validateWebhookUrl(url)).toThrow(/private IP/i);
@@ -720,7 +720,10 @@ describe("WebhookHandler", () => {
720
720
  vi.advanceTimersByTime(10000);
721
721
  }
722
722
 
723
- expect(errorHandler).toHaveBeenCalledWith(expect.any(Error), "https://webhook.example.com/events");
723
+ expect(errorHandler).toHaveBeenCalledWith(
724
+ expect.any(Error),
725
+ "https://webhook.example.com/events"
726
+ );
724
727
  });
725
728
  });
726
729