@teneo-protocol/sdk 1.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 (281) hide show
  1. package/.dockerignore +14 -0
  2. package/.env.test.example +14 -0
  3. package/.eslintrc.json +26 -0
  4. package/.github/workflows/claude-code-review.yml +78 -0
  5. package/.github/workflows/claude-reviewer.yml +64 -0
  6. package/.github/workflows/publish-npm.yml +38 -0
  7. package/.github/workflows/push-to-main.yml +23 -0
  8. package/.node-version +1 -0
  9. package/.prettierrc +11 -0
  10. package/Dockerfile +25 -0
  11. package/LICENCE +661 -0
  12. package/README.md +709 -0
  13. package/dist/constants.d.ts +42 -0
  14. package/dist/constants.d.ts.map +1 -0
  15. package/dist/constants.js +45 -0
  16. package/dist/constants.js.map +1 -0
  17. package/dist/core/websocket-client.d.ts +261 -0
  18. package/dist/core/websocket-client.d.ts.map +1 -0
  19. package/dist/core/websocket-client.js +875 -0
  20. package/dist/core/websocket-client.js.map +1 -0
  21. package/dist/formatters/response-formatter.d.ts +354 -0
  22. package/dist/formatters/response-formatter.d.ts.map +1 -0
  23. package/dist/formatters/response-formatter.js +575 -0
  24. package/dist/formatters/response-formatter.js.map +1 -0
  25. package/dist/handlers/message-handler-registry.d.ts +155 -0
  26. package/dist/handlers/message-handler-registry.d.ts.map +1 -0
  27. package/dist/handlers/message-handler-registry.js +216 -0
  28. package/dist/handlers/message-handler-registry.js.map +1 -0
  29. package/dist/handlers/message-handlers/agent-selected-handler.d.ts +112 -0
  30. package/dist/handlers/message-handlers/agent-selected-handler.d.ts.map +1 -0
  31. package/dist/handlers/message-handlers/agent-selected-handler.js +40 -0
  32. package/dist/handlers/message-handlers/agent-selected-handler.js.map +1 -0
  33. package/dist/handlers/message-handlers/agents-list-handler.d.ts +14 -0
  34. package/dist/handlers/message-handlers/agents-list-handler.d.ts.map +1 -0
  35. package/dist/handlers/message-handlers/agents-list-handler.js +25 -0
  36. package/dist/handlers/message-handlers/agents-list-handler.js.map +1 -0
  37. package/dist/handlers/message-handlers/auth-error-handler.d.ts +71 -0
  38. package/dist/handlers/message-handlers/auth-error-handler.d.ts.map +1 -0
  39. package/dist/handlers/message-handlers/auth-error-handler.js +30 -0
  40. package/dist/handlers/message-handlers/auth-error-handler.js.map +1 -0
  41. package/dist/handlers/message-handlers/auth-message-handler.d.ts +18 -0
  42. package/dist/handlers/message-handlers/auth-message-handler.d.ts.map +1 -0
  43. package/dist/handlers/message-handlers/auth-message-handler.js +60 -0
  44. package/dist/handlers/message-handlers/auth-message-handler.js.map +1 -0
  45. package/dist/handlers/message-handlers/auth-required-handler.d.ts +76 -0
  46. package/dist/handlers/message-handlers/auth-required-handler.d.ts.map +1 -0
  47. package/dist/handlers/message-handlers/auth-required-handler.js +23 -0
  48. package/dist/handlers/message-handlers/auth-required-handler.js.map +1 -0
  49. package/dist/handlers/message-handlers/auth-success-handler.d.ts +18 -0
  50. package/dist/handlers/message-handlers/auth-success-handler.d.ts.map +1 -0
  51. package/dist/handlers/message-handlers/auth-success-handler.js +51 -0
  52. package/dist/handlers/message-handlers/auth-success-handler.js.map +1 -0
  53. package/dist/handlers/message-handlers/base-handler.d.ts +55 -0
  54. package/dist/handlers/message-handlers/base-handler.d.ts.map +1 -0
  55. package/dist/handlers/message-handlers/base-handler.js +83 -0
  56. package/dist/handlers/message-handlers/base-handler.js.map +1 -0
  57. package/dist/handlers/message-handlers/challenge-handler.d.ts +73 -0
  58. package/dist/handlers/message-handlers/challenge-handler.d.ts.map +1 -0
  59. package/dist/handlers/message-handlers/challenge-handler.js +47 -0
  60. package/dist/handlers/message-handlers/challenge-handler.js.map +1 -0
  61. package/dist/handlers/message-handlers/error-message-handler.d.ts +76 -0
  62. package/dist/handlers/message-handlers/error-message-handler.d.ts.map +1 -0
  63. package/dist/handlers/message-handlers/error-message-handler.js +29 -0
  64. package/dist/handlers/message-handlers/error-message-handler.js.map +1 -0
  65. package/dist/handlers/message-handlers/index.d.ts +28 -0
  66. package/dist/handlers/message-handlers/index.d.ts.map +1 -0
  67. package/dist/handlers/message-handlers/index.js +100 -0
  68. package/dist/handlers/message-handlers/index.js.map +1 -0
  69. package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts +122 -0
  70. package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts.map +1 -0
  71. package/dist/handlers/message-handlers/list-rooms-response-handler.js +30 -0
  72. package/dist/handlers/message-handlers/list-rooms-response-handler.js.map +1 -0
  73. package/dist/handlers/message-handlers/ping-pong-handler.d.ts +104 -0
  74. package/dist/handlers/message-handlers/ping-pong-handler.d.ts.map +1 -0
  75. package/dist/handlers/message-handlers/ping-pong-handler.js +36 -0
  76. package/dist/handlers/message-handlers/ping-pong-handler.js.map +1 -0
  77. package/dist/handlers/message-handlers/regular-message-handler.d.ts +56 -0
  78. package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -0
  79. package/dist/handlers/message-handlers/regular-message-handler.js +59 -0
  80. package/dist/handlers/message-handlers/regular-message-handler.js.map +1 -0
  81. package/dist/handlers/message-handlers/subscribe-response-handler.d.ts +81 -0
  82. package/dist/handlers/message-handlers/subscribe-response-handler.d.ts.map +1 -0
  83. package/dist/handlers/message-handlers/subscribe-response-handler.js +48 -0
  84. package/dist/handlers/message-handlers/subscribe-response-handler.js.map +1 -0
  85. package/dist/handlers/message-handlers/task-response-handler.d.ts +14 -0
  86. package/dist/handlers/message-handlers/task-response-handler.d.ts.map +1 -0
  87. package/dist/handlers/message-handlers/task-response-handler.js +44 -0
  88. package/dist/handlers/message-handlers/task-response-handler.js.map +1 -0
  89. package/dist/handlers/message-handlers/types.d.ts +51 -0
  90. package/dist/handlers/message-handlers/types.d.ts.map +1 -0
  91. package/dist/handlers/message-handlers/types.js +7 -0
  92. package/dist/handlers/message-handlers/types.js.map +1 -0
  93. package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts +81 -0
  94. package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts.map +1 -0
  95. package/dist/handlers/message-handlers/unsubscribe-response-handler.js +48 -0
  96. package/dist/handlers/message-handlers/unsubscribe-response-handler.js.map +1 -0
  97. package/dist/handlers/webhook-handler.d.ts +202 -0
  98. package/dist/handlers/webhook-handler.d.ts.map +1 -0
  99. package/dist/handlers/webhook-handler.js +511 -0
  100. package/dist/handlers/webhook-handler.js.map +1 -0
  101. package/dist/index.d.ts +71 -0
  102. package/dist/index.d.ts.map +1 -0
  103. package/dist/index.js +217 -0
  104. package/dist/index.js.map +1 -0
  105. package/dist/managers/agent-registry.d.ts +173 -0
  106. package/dist/managers/agent-registry.d.ts.map +1 -0
  107. package/dist/managers/agent-registry.js +310 -0
  108. package/dist/managers/agent-registry.js.map +1 -0
  109. package/dist/managers/connection-manager.d.ts +134 -0
  110. package/dist/managers/connection-manager.d.ts.map +1 -0
  111. package/dist/managers/connection-manager.js +176 -0
  112. package/dist/managers/connection-manager.js.map +1 -0
  113. package/dist/managers/index.d.ts +9 -0
  114. package/dist/managers/index.d.ts.map +1 -0
  115. package/dist/managers/index.js +16 -0
  116. package/dist/managers/index.js.map +1 -0
  117. package/dist/managers/message-router.d.ts +112 -0
  118. package/dist/managers/message-router.d.ts.map +1 -0
  119. package/dist/managers/message-router.js +260 -0
  120. package/dist/managers/message-router.js.map +1 -0
  121. package/dist/managers/room-manager.d.ts +165 -0
  122. package/dist/managers/room-manager.d.ts.map +1 -0
  123. package/dist/managers/room-manager.js +227 -0
  124. package/dist/managers/room-manager.js.map +1 -0
  125. package/dist/teneo-sdk.d.ts +703 -0
  126. package/dist/teneo-sdk.d.ts.map +1 -0
  127. package/dist/teneo-sdk.js +907 -0
  128. package/dist/teneo-sdk.js.map +1 -0
  129. package/dist/types/config.d.ts +1047 -0
  130. package/dist/types/config.d.ts.map +1 -0
  131. package/dist/types/config.js +720 -0
  132. package/dist/types/config.js.map +1 -0
  133. package/dist/types/error-codes.d.ts +29 -0
  134. package/dist/types/error-codes.d.ts.map +1 -0
  135. package/dist/types/error-codes.js +41 -0
  136. package/dist/types/error-codes.js.map +1 -0
  137. package/dist/types/events.d.ts +616 -0
  138. package/dist/types/events.d.ts.map +1 -0
  139. package/dist/types/events.js +261 -0
  140. package/dist/types/events.js.map +1 -0
  141. package/dist/types/health.d.ts +40 -0
  142. package/dist/types/health.d.ts.map +1 -0
  143. package/dist/types/health.js +6 -0
  144. package/dist/types/health.js.map +1 -0
  145. package/dist/types/index.d.ts +10 -0
  146. package/dist/types/index.d.ts.map +1 -0
  147. package/dist/types/index.js +123 -0
  148. package/dist/types/index.js.map +1 -0
  149. package/dist/types/messages.d.ts +3734 -0
  150. package/dist/types/messages.d.ts.map +1 -0
  151. package/dist/types/messages.js +482 -0
  152. package/dist/types/messages.js.map +1 -0
  153. package/dist/types/validation.d.ts +81 -0
  154. package/dist/types/validation.d.ts.map +1 -0
  155. package/dist/types/validation.js +115 -0
  156. package/dist/types/validation.js.map +1 -0
  157. package/dist/utils/bounded-queue.d.ts +127 -0
  158. package/dist/utils/bounded-queue.d.ts.map +1 -0
  159. package/dist/utils/bounded-queue.js +181 -0
  160. package/dist/utils/bounded-queue.js.map +1 -0
  161. package/dist/utils/circuit-breaker.d.ts +141 -0
  162. package/dist/utils/circuit-breaker.d.ts.map +1 -0
  163. package/dist/utils/circuit-breaker.js +215 -0
  164. package/dist/utils/circuit-breaker.js.map +1 -0
  165. package/dist/utils/deduplication-cache.d.ts +110 -0
  166. package/dist/utils/deduplication-cache.d.ts.map +1 -0
  167. package/dist/utils/deduplication-cache.js +177 -0
  168. package/dist/utils/deduplication-cache.js.map +1 -0
  169. package/dist/utils/event-waiter.d.ts +101 -0
  170. package/dist/utils/event-waiter.d.ts.map +1 -0
  171. package/dist/utils/event-waiter.js +118 -0
  172. package/dist/utils/event-waiter.js.map +1 -0
  173. package/dist/utils/index.d.ts +51 -0
  174. package/dist/utils/index.d.ts.map +1 -0
  175. package/dist/utils/index.js +72 -0
  176. package/dist/utils/index.js.map +1 -0
  177. package/dist/utils/logger.d.ts +22 -0
  178. package/dist/utils/logger.d.ts.map +1 -0
  179. package/dist/utils/logger.js +91 -0
  180. package/dist/utils/logger.js.map +1 -0
  181. package/dist/utils/rate-limiter.d.ts +122 -0
  182. package/dist/utils/rate-limiter.d.ts.map +1 -0
  183. package/dist/utils/rate-limiter.js +190 -0
  184. package/dist/utils/rate-limiter.js.map +1 -0
  185. package/dist/utils/retry-policy.d.ts +191 -0
  186. package/dist/utils/retry-policy.d.ts.map +1 -0
  187. package/dist/utils/retry-policy.js +225 -0
  188. package/dist/utils/retry-policy.js.map +1 -0
  189. package/dist/utils/secure-private-key.d.ts +113 -0
  190. package/dist/utils/secure-private-key.d.ts.map +1 -0
  191. package/dist/utils/secure-private-key.js +188 -0
  192. package/dist/utils/secure-private-key.js.map +1 -0
  193. package/dist/utils/signature-verifier.d.ts +143 -0
  194. package/dist/utils/signature-verifier.d.ts.map +1 -0
  195. package/dist/utils/signature-verifier.js +238 -0
  196. package/dist/utils/signature-verifier.js.map +1 -0
  197. package/dist/utils/ssrf-validator.d.ts +36 -0
  198. package/dist/utils/ssrf-validator.d.ts.map +1 -0
  199. package/dist/utils/ssrf-validator.js +195 -0
  200. package/dist/utils/ssrf-validator.js.map +1 -0
  201. package/examples/.env.example +17 -0
  202. package/examples/basic-usage.ts +211 -0
  203. package/examples/production-dashboard/.env.example +153 -0
  204. package/examples/production-dashboard/package.json +39 -0
  205. package/examples/production-dashboard/public/dashboard.html +642 -0
  206. package/examples/production-dashboard/server.ts +753 -0
  207. package/examples/webhook-integration.ts +239 -0
  208. package/examples/x-influencer-battle-redesign.html +1065 -0
  209. package/examples/x-influencer-battle-server.ts +217 -0
  210. package/examples/x-influencer-battle.html +787 -0
  211. package/package.json +65 -0
  212. package/src/constants.ts +43 -0
  213. package/src/core/websocket-client.test.ts +512 -0
  214. package/src/core/websocket-client.ts +1056 -0
  215. package/src/formatters/response-formatter.test.ts +571 -0
  216. package/src/formatters/response-formatter.ts +677 -0
  217. package/src/handlers/message-handler-registry.ts +239 -0
  218. package/src/handlers/message-handlers/agent-selected-handler.ts +40 -0
  219. package/src/handlers/message-handlers/agents-list-handler.ts +26 -0
  220. package/src/handlers/message-handlers/auth-error-handler.ts +31 -0
  221. package/src/handlers/message-handlers/auth-message-handler.ts +66 -0
  222. package/src/handlers/message-handlers/auth-required-handler.ts +23 -0
  223. package/src/handlers/message-handlers/auth-success-handler.ts +57 -0
  224. package/src/handlers/message-handlers/base-handler.ts +101 -0
  225. package/src/handlers/message-handlers/challenge-handler.ts +57 -0
  226. package/src/handlers/message-handlers/error-message-handler.ts +27 -0
  227. package/src/handlers/message-handlers/index.ts +77 -0
  228. package/src/handlers/message-handlers/list-rooms-response-handler.ts +28 -0
  229. package/src/handlers/message-handlers/ping-pong-handler.ts +30 -0
  230. package/src/handlers/message-handlers/regular-message-handler.ts +65 -0
  231. package/src/handlers/message-handlers/subscribe-response-handler.ts +47 -0
  232. package/src/handlers/message-handlers/task-response-handler.ts +45 -0
  233. package/src/handlers/message-handlers/types.ts +77 -0
  234. package/src/handlers/message-handlers/unsubscribe-response-handler.ts +47 -0
  235. package/src/handlers/webhook-handler.test.ts +789 -0
  236. package/src/handlers/webhook-handler.ts +576 -0
  237. package/src/index.ts +269 -0
  238. package/src/managers/agent-registry.test.ts +466 -0
  239. package/src/managers/agent-registry.ts +347 -0
  240. package/src/managers/connection-manager.ts +195 -0
  241. package/src/managers/index.ts +9 -0
  242. package/src/managers/message-router.ts +349 -0
  243. package/src/managers/room-manager.ts +248 -0
  244. package/src/teneo-sdk.ts +1022 -0
  245. package/src/types/config.test.ts +325 -0
  246. package/src/types/config.ts +799 -0
  247. package/src/types/error-codes.ts +44 -0
  248. package/src/types/events.test.ts +302 -0
  249. package/src/types/events.ts +382 -0
  250. package/src/types/health.ts +46 -0
  251. package/src/types/index.ts +199 -0
  252. package/src/types/messages.test.ts +660 -0
  253. package/src/types/messages.ts +570 -0
  254. package/src/types/validation.ts +123 -0
  255. package/src/utils/bounded-queue.test.ts +356 -0
  256. package/src/utils/bounded-queue.ts +205 -0
  257. package/src/utils/circuit-breaker.test.ts +394 -0
  258. package/src/utils/circuit-breaker.ts +262 -0
  259. package/src/utils/deduplication-cache.test.ts +380 -0
  260. package/src/utils/deduplication-cache.ts +198 -0
  261. package/src/utils/event-waiter.test.ts +381 -0
  262. package/src/utils/event-waiter.ts +172 -0
  263. package/src/utils/index.ts +74 -0
  264. package/src/utils/logger.ts +87 -0
  265. package/src/utils/rate-limiter.test.ts +341 -0
  266. package/src/utils/rate-limiter.ts +211 -0
  267. package/src/utils/retry-policy.test.ts +558 -0
  268. package/src/utils/retry-policy.ts +272 -0
  269. package/src/utils/secure-private-key.test.ts +356 -0
  270. package/src/utils/secure-private-key.ts +205 -0
  271. package/src/utils/signature-verifier.test.ts +464 -0
  272. package/src/utils/signature-verifier.ts +298 -0
  273. package/src/utils/ssrf-validator.test.ts +372 -0
  274. package/src/utils/ssrf-validator.ts +224 -0
  275. package/tests/integration/real-server.test.ts +740 -0
  276. package/tests/integration/websocket.test.ts +381 -0
  277. package/tests/integration-setup.ts +16 -0
  278. package/tests/setup.ts +34 -0
  279. package/tsconfig.json +32 -0
  280. package/vitest.config.ts +42 -0
  281. package/vitest.integration.config.ts +23 -0
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Message Handler Registry
3
+ * Central registry for message handlers - eliminates shotgun surgery when adding new message types
4
+ */
5
+
6
+ import { BaseMessage, MessageType, Logger } from "../types";
7
+ import {
8
+ MessageHandler,
9
+ HandlerContext,
10
+ HandlerRegistrationOptions
11
+ } from "./message-handlers/types";
12
+ import { ValidationError } from "../types/events";
13
+
14
+ /**
15
+ * Registry for message handlers
16
+ * Stores handlers by message type and routes messages to appropriate handlers
17
+ */
18
+ export class MessageHandlerRegistry {
19
+ private readonly handlers = new Map<MessageType, MessageHandler>();
20
+ private readonly logger: Logger;
21
+
22
+ constructor(logger: Logger) {
23
+ this.logger = logger;
24
+ }
25
+
26
+ /**
27
+ * Registers a message handler for a specific message type.
28
+ * Prevents duplicate registration unless replace option is true.
29
+ * Enables extensible message processing following Open/Closed Principle.
30
+ *
31
+ * @param handler - The handler to register (must have unique type)
32
+ * @param options - Registration options
33
+ * @param options.replace - Whether to replace existing handler (default: false)
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const customHandler = new CustomMessageHandler();
38
+ * registry.register(customHandler);
39
+ *
40
+ * // Replace existing handler
41
+ * registry.register(newHandler, { replace: true });
42
+ * ```
43
+ */
44
+ public register(handler: MessageHandler, options: HandlerRegistrationOptions = {}): void {
45
+ const { replace = false } = options;
46
+
47
+ // Check if handler already exists
48
+ if (this.handlers.has(handler.type) && !replace) {
49
+ this.logger.warn(
50
+ `Handler for ${handler.type} already registered, skipping. Use replace:true to override.`
51
+ );
52
+ return;
53
+ }
54
+
55
+ this.handlers.set(handler.type, handler);
56
+ this.logger.debug(`Registered handler for message type: ${handler.type}`);
57
+ }
58
+
59
+ /**
60
+ * Registers multiple message handlers at once.
61
+ * Convenient for bulk registration of default or custom handlers.
62
+ * Uses same registration logic as register() for each handler.
63
+ *
64
+ * @param handlers - Array of handlers to register
65
+ * @param options - Registration options applied to all handlers
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * const handlers = [
70
+ * new TaskResponseHandler(),
71
+ * new AgentSelectedHandler(),
72
+ * new ErrorHandler()
73
+ * ];
74
+ * registry.registerAll(handlers);
75
+ * ```
76
+ */
77
+ public registerAll(handlers: MessageHandler[], options: HandlerRegistrationOptions = {}): void {
78
+ for (const handler of handlers) {
79
+ this.register(handler, options);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Unregisters a handler for a specific message type.
85
+ * Returns whether a handler was actually removed.
86
+ *
87
+ * @param type - The message type to unregister
88
+ * @returns True if handler was removed, false if no handler existed
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * const removed = registry.unregister('task_response');
93
+ * if (removed) {
94
+ * console.log('Handler removed successfully');
95
+ * }
96
+ * ```
97
+ */
98
+ public unregister(type: MessageType): boolean {
99
+ const result = this.handlers.delete(type);
100
+ if (result) {
101
+ this.logger.debug(`Unregistered handler for message type: ${type}`);
102
+ }
103
+ return result;
104
+ }
105
+
106
+ /**
107
+ * Checks if a handler is registered for a specific message type.
108
+ * Useful for conditional logic based on handler availability.
109
+ *
110
+ * @param type - The message type to check
111
+ * @returns True if handler exists, false otherwise
112
+ *
113
+ * @example
114
+ * ```typescript
115
+ * if (registry.has('task_response')) {
116
+ * console.log('Task response handler is registered');
117
+ * }
118
+ * ```
119
+ */
120
+ public has(type: MessageType): boolean {
121
+ return this.handlers.has(type);
122
+ }
123
+
124
+ /**
125
+ * Gets a registered handler for a specific message type.
126
+ * Returns undefined if no handler is registered for the type.
127
+ *
128
+ * @param type - The message type to get handler for
129
+ * @returns The handler if found, undefined otherwise
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * const handler = registry.get('task_response');
134
+ * if (handler) {
135
+ * console.log(`Handler found: ${handler.type}`);
136
+ * }
137
+ * ```
138
+ */
139
+ public get(type: MessageType): MessageHandler | undefined {
140
+ return this.handlers.get(type);
141
+ }
142
+
143
+ /**
144
+ * Routes a message to its appropriate handler based on message type.
145
+ * Validates handler can process the message before delegating.
146
+ * Catches and logs handler errors, wrapping them in ValidationError.
147
+ *
148
+ * @param message - The message to handle
149
+ * @param context - Handler context with dependencies (logger, emit, etc.)
150
+ * @returns Promise that resolves when message is handled
151
+ * @throws {ValidationError} If handler throws an error during processing
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * const message = { type: 'task_response', ... };
156
+ * const context = createHandlerContext();
157
+ * await registry.handle(message, context);
158
+ * ```
159
+ */
160
+ public async handle(message: BaseMessage, context: HandlerContext): Promise<void> {
161
+ // Check for pending message responses first (handled by caller)
162
+ if (message.id) {
163
+ // Let caller handle response matching
164
+ this.logger.debug(
165
+ `Message ${message.id} may be a response - caller should check pending messages`
166
+ );
167
+ }
168
+
169
+ // Find handler for this message type
170
+ const handler = this.handlers.get(message.type);
171
+
172
+ if (!handler) {
173
+ this.logger.debug(`No handler registered for message type: ${message.type}`);
174
+ // Not an error - some message types don't need handling
175
+ return;
176
+ }
177
+
178
+ // Verify handler can handle this message
179
+ if (!handler.canHandle(message)) {
180
+ this.logger.warn(`Handler for ${message.type} returned false for canHandle()`);
181
+ return;
182
+ }
183
+
184
+ // Delegate to handler
185
+ this.logger.debug(`Routing ${message.type} message to handler`);
186
+ try {
187
+ await handler.handle(message, context);
188
+ } catch (error) {
189
+ this.logger.error(`Handler for ${message.type} threw error`, error);
190
+ throw new ValidationError(`Handler for ${message.type} failed`, error);
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Gets an array of all currently registered message types.
196
+ * Useful for introspection and debugging handler configuration.
197
+ *
198
+ * @returns Array of message types that have registered handlers
199
+ *
200
+ * @example
201
+ * ```typescript
202
+ * const types = registry.getRegisteredTypes();
203
+ * console.log(`Registered handlers: ${types.join(', ')}`);
204
+ * ```
205
+ */
206
+ public getRegisteredTypes(): MessageType[] {
207
+ return Array.from(this.handlers.keys());
208
+ }
209
+
210
+ /**
211
+ * Gets the total number of registered handlers.
212
+ * Convenience getter for handler count.
213
+ *
214
+ * @returns Number of registered handlers
215
+ *
216
+ * @example
217
+ * ```typescript
218
+ * console.log(`${registry.size} handlers registered`);
219
+ * ```
220
+ */
221
+ public get size(): number {
222
+ return this.handlers.size;
223
+ }
224
+
225
+ /**
226
+ * Clears all registered handlers from the registry.
227
+ * Useful for testing or resetting handler configuration.
228
+ *
229
+ * @example
230
+ * ```typescript
231
+ * registry.clear();
232
+ * console.log('All handlers cleared');
233
+ * ```
234
+ */
235
+ public clear(): void {
236
+ this.handlers.clear();
237
+ this.logger.debug("Cleared all message handlers");
238
+ }
239
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Handler for agent_selected messages
3
+ * Processes coordinator's agent selection
4
+ */
5
+
6
+ import { AgentSelectedMessage, AgentSelectedMessageSchema } from "../../types";
7
+ import { AgentSelectedData } from "../../types/events";
8
+ import { BaseMessageHandler } from "./base-handler";
9
+ import { HandlerContext } from "./types";
10
+
11
+ export class AgentSelectedHandler extends BaseMessageHandler<AgentSelectedMessage> {
12
+ readonly type = "agent_selected" as const;
13
+ readonly schema = AgentSelectedMessageSchema;
14
+
15
+ protected handleValidated(message: AgentSelectedMessage, context: HandlerContext): void {
16
+ context.logger.debug("Handling agent_selected message", {
17
+ agentId: message.data.agent_id,
18
+ agentName: message.data.agent_name
19
+ });
20
+
21
+ // Build agent selected data
22
+ const data: AgentSelectedData = {
23
+ agentId: message.data.agent_id,
24
+ agentName: message.data.agent_name,
25
+ reasoning: message.reasoning || "",
26
+ userRequest: message.data.user_request,
27
+ command: message.data.command,
28
+ commandReasoning: message.data.command_reasoning,
29
+ capabilities: message.data.capabilities
30
+ };
31
+
32
+ // Emit agent:selected event
33
+ this.emit(context, "agent:selected", data);
34
+
35
+ // Send webhook (fire-and-forget)
36
+ this.sendWebhook(context, "agent_selected", data, {
37
+ agentId: data.agentId
38
+ });
39
+ }
40
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Handler for agents list messages
3
+ * Processes agent list updates from the server
4
+ */
5
+
6
+ import { z } from "zod";
7
+ import { AgentsListMessage, AgentsListMessageSchema } from "../../types";
8
+ import { BaseMessageHandler } from "./base-handler";
9
+ import { HandlerContext } from "./types";
10
+
11
+ export class AgentsListHandler extends BaseMessageHandler<AgentsListMessage> {
12
+ readonly type = "agents" as const;
13
+ readonly schema = AgentsListMessageSchema as z.ZodSchema<AgentsListMessage>;
14
+
15
+ protected async handleValidated(
16
+ message: AgentsListMessage,
17
+ context: HandlerContext
18
+ ): Promise<void> {
19
+ context.logger.debug("Handling agents list message", {
20
+ count: message.data.length
21
+ });
22
+
23
+ // Emit agent:list event with the agents array
24
+ this.emit(context, "agent:list", message.data);
25
+ }
26
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Handler for auth_error messages
3
+ * Handles authentication errors
4
+ */
5
+
6
+ import { AuthErrorMessage, AuthErrorMessageSchema } from "../../types";
7
+ import { AuthenticationError } from "../../types/events";
8
+ import { BaseMessageHandler } from "./base-handler";
9
+ import { HandlerContext } from "./types";
10
+
11
+ export class AuthErrorHandler extends BaseMessageHandler<AuthErrorMessage> {
12
+ readonly type = "auth_error" as const;
13
+ readonly schema = AuthErrorMessageSchema;
14
+
15
+ protected async handleValidated(
16
+ message: AuthErrorMessage,
17
+ context: HandlerContext
18
+ ): Promise<void> {
19
+ const error = message.data.error || "Authentication failed";
20
+ context.logger.error("Authentication failed", { error });
21
+
22
+ // Update states
23
+ this.updateAuthState(context, { authenticated: false });
24
+ this.updateConnectionState(context, {
25
+ lastError: new AuthenticationError(error)
26
+ });
27
+
28
+ // Emit error event
29
+ this.emit(context, "auth:error", error);
30
+ }
31
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Handler for auth messages
3
+ * Handles authentication response from server (both fresh and cached)
4
+ */
5
+
6
+ import { z } from "zod";
7
+ import { AuthMessage, AuthMessageSchema, Room } from "../../types";
8
+ import { BaseMessageHandler } from "./base-handler";
9
+ import { HandlerContext } from "./types";
10
+
11
+ export class AuthMessageHandler extends BaseMessageHandler<AuthMessage> {
12
+ readonly type = "auth" as const;
13
+ readonly schema = AuthMessageSchema as z.ZodSchema<AuthMessage>;
14
+
15
+ protected async handleValidated(message: AuthMessage, context: HandlerContext): Promise<void> {
16
+ context.logger.info("Handling auth message", {
17
+ hasData: !!message.data,
18
+ dataKeys: message.data ? Object.keys(message.data) : [],
19
+ to: message.to
20
+ });
21
+
22
+ // Check if this is a successful auth response by looking for required fields
23
+ if (message.data?.id || message.data?.address || message.data?.cached_auth || message.to) {
24
+ const isCachedAuth = !!message.data?.cached_auth;
25
+ context.logger.info(
26
+ isCachedAuth ? "Using cached authentication" : "Authentication successful"
27
+ );
28
+
29
+ // Extract rooms
30
+ const rooms = this.extractRooms(message.data?.rooms);
31
+
32
+ // Update connection state
33
+ this.updateConnectionState(context, { authenticated: true });
34
+
35
+ // Update auth state
36
+ this.updateAuthState(context, {
37
+ authenticated: true,
38
+ clientId: message.data?.id || message.to || "",
39
+ walletAddress: message.data?.address || "",
40
+ isWhitelisted: message.data?.is_whitelisted,
41
+ isAdmin: message.data?.is_admin_whitelisted,
42
+ nftVerified: message.data?.nft_verified,
43
+ rooms: rooms.map((r) => r.id),
44
+ roomObjects: rooms,
45
+ privateRoomId: message.data?.private_room_id
46
+ });
47
+
48
+ // Get updated auth state
49
+ const authState = context.getAuthState();
50
+
51
+ // Emit events
52
+ this.emit(context, "auth:success", authState);
53
+ this.emit(context, "ready");
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Extract and normalize rooms from auth data
59
+ */
60
+ private extractRooms(rooms?: Room[]): Room[] {
61
+ if (!rooms || !Array.isArray(rooms)) {
62
+ return [];
63
+ }
64
+ return rooms;
65
+ }
66
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Handler for auth_required messages
3
+ * Server is requesting authentication
4
+ */
5
+
6
+ import { AuthRequiredMessage, AuthRequiredMessageSchema } from "../../types";
7
+ import { BaseMessageHandler } from "./base-handler";
8
+ import { HandlerContext } from "./types";
9
+
10
+ export class AuthRequiredHandler extends BaseMessageHandler<AuthRequiredMessage> {
11
+ readonly type = "auth_required" as const;
12
+ readonly schema = AuthRequiredMessageSchema;
13
+
14
+ protected async handleValidated(
15
+ _message: AuthRequiredMessage,
16
+ context: HandlerContext
17
+ ): Promise<void> {
18
+ context.logger.debug("Server requesting authentication");
19
+
20
+ // Emit auth:required event
21
+ this.emit(context, "auth:required");
22
+ }
23
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Handler for auth_success messages
3
+ * Legacy support for explicit auth_success message type
4
+ */
5
+
6
+ import { z } from "zod";
7
+ import { AuthSuccessMessage, AuthSuccessMessageSchema, Room } from "../../types";
8
+ import { BaseMessageHandler } from "./base-handler";
9
+ import { HandlerContext } from "./types";
10
+
11
+ export class AuthSuccessHandler extends BaseMessageHandler<AuthSuccessMessage> {
12
+ readonly type = "auth_success" as const;
13
+ readonly schema = AuthSuccessMessageSchema as z.ZodSchema<AuthSuccessMessage>;
14
+
15
+ protected async handleValidated(
16
+ message: AuthSuccessMessage,
17
+ context: HandlerContext
18
+ ): Promise<void> {
19
+ context.logger.info("Authentication successful");
20
+
21
+ // Extract rooms
22
+ const rooms = this.extractRooms(message.data.rooms);
23
+
24
+ // Update connection state
25
+ this.updateConnectionState(context, { authenticated: true });
26
+
27
+ // Update auth state
28
+ this.updateAuthState(context, {
29
+ authenticated: true,
30
+ clientId: message.data.id,
31
+ walletAddress: message.data.address,
32
+ isWhitelisted: message.data.is_whitelisted,
33
+ isAdmin: message.data.is_admin_whitelisted,
34
+ nftVerified: message.data.nft_verified,
35
+ rooms: rooms.map((r) => r.id),
36
+ roomObjects: rooms,
37
+ privateRoomId: message.data.private_room_id
38
+ });
39
+
40
+ // Get updated auth state
41
+ const authState = context.getAuthState();
42
+
43
+ // Emit events
44
+ this.emit(context, "auth:success", authState);
45
+ this.emit(context, "ready");
46
+ }
47
+
48
+ /**
49
+ * Extract and normalize rooms from auth data
50
+ */
51
+ private extractRooms(rooms?: Room[]): Room[] {
52
+ if (!rooms || !Array.isArray(rooms)) {
53
+ return [];
54
+ }
55
+ return rooms;
56
+ }
57
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Base abstract class for message handlers
3
+ * Implements the template method pattern for consistent message handling
4
+ */
5
+
6
+ import { z } from "zod";
7
+ import { BaseMessage, MessageType } from "../../types";
8
+ import { MessageHandler, HandlerContext } from "./types";
9
+
10
+ /**
11
+ * Abstract base class for message handlers
12
+ * Provides common functionality and enforces consistent structure
13
+ */
14
+ export abstract class BaseMessageHandler<T extends BaseMessage = BaseMessage>
15
+ implements MessageHandler<T>
16
+ {
17
+ // Subclasses must define these
18
+ abstract readonly type: MessageType;
19
+ abstract readonly schema: z.ZodSchema<T>;
20
+
21
+ /**
22
+ * Check if this handler can handle the given message
23
+ */
24
+ public canHandle(message: BaseMessage): boolean {
25
+ return message.type === this.type;
26
+ }
27
+
28
+ /**
29
+ * Main handle method - implements template method pattern
30
+ * 1. Validates message
31
+ * 2. Calls handleValidated (implemented by subclasses)
32
+ * 3. Handles errors
33
+ */
34
+ public async handle(message: BaseMessage, context: HandlerContext): Promise<void> {
35
+ try {
36
+ // Validate message with Zod schema
37
+ const validated = this.validate(message);
38
+
39
+ // Call subclass implementation
40
+ await this.handleValidated(validated, context);
41
+ } catch (error) {
42
+ context.logger.error(`Error handling ${this.type} message`, error);
43
+ this.onError(error, message, context);
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Validate message against schema
49
+ */
50
+ protected validate(message: BaseMessage): T {
51
+ const result = this.schema.safeParse(message);
52
+ if (!result.success) {
53
+ throw new Error(`Invalid ${this.type} message: ${result.error.message}`);
54
+ }
55
+ return result.data;
56
+ }
57
+
58
+ /**
59
+ * Handle validated message - implemented by subclasses
60
+ */
61
+ protected abstract handleValidated(message: T, context: HandlerContext): Promise<void> | void;
62
+
63
+ /**
64
+ * Handle errors - can be overridden by subclasses
65
+ */
66
+ protected onError(error: unknown, message: BaseMessage, context: HandlerContext): void {
67
+ // Default: emit error event
68
+ context.emit("message:error", error, message);
69
+ }
70
+
71
+ /**
72
+ * Helper: emit event
73
+ */
74
+ protected emit(context: HandlerContext, event: string, ...args: any[]): void {
75
+ context.emit(event, ...args);
76
+ }
77
+
78
+ /**
79
+ * Helper: send webhook (fire-and-forget pattern)
80
+ */
81
+ protected sendWebhook(context: HandlerContext, type: string, data: any, metadata?: any): void {
82
+ // Fire and forget - don't block event emission
83
+ context.sendWebhook(type as any, data, metadata).catch((error) => {
84
+ context.logger.error(`Failed to send webhook for ${this.type}`, error);
85
+ });
86
+ }
87
+
88
+ /**
89
+ * Helper: update connection state
90
+ */
91
+ protected updateConnectionState(context: HandlerContext, update: any): void {
92
+ context.updateConnectionState(update);
93
+ }
94
+
95
+ /**
96
+ * Helper: update auth state
97
+ */
98
+ protected updateAuthState(context: HandlerContext, update: any): void {
99
+ context.updateAuthState(update);
100
+ }
101
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Handler for challenge messages
3
+ * Handles authentication challenge-response flow
4
+ */
5
+
6
+ import { ChallengeMessage, ChallengeMessageSchema, createAuth } from "../../types";
7
+ import { BaseMessageHandler } from "./base-handler";
8
+ import { HandlerContext } from "./types";
9
+
10
+ export class ChallengeHandler extends BaseMessageHandler<ChallengeMessage> {
11
+ readonly type = "challenge" as const;
12
+ readonly schema = ChallengeMessageSchema;
13
+ private clientType: "user" | "agent" | "coordinator";
14
+
15
+ constructor(clientType: "user" | "agent" | "coordinator" = "user") {
16
+ super();
17
+ this.clientType = clientType;
18
+ }
19
+
20
+ protected async handleValidated(
21
+ message: ChallengeMessage,
22
+ context: HandlerContext
23
+ ): Promise<void> {
24
+ if (!context.account) {
25
+ context.logger.error("Received challenge but no account configured");
26
+ return;
27
+ }
28
+
29
+ const challenge = message.data.challenge;
30
+ context.logger.debug("Received authentication challenge");
31
+
32
+ // Update auth state with challenge
33
+ this.updateAuthState(context, {
34
+ challenge,
35
+ challengeTimestamp: message.data.timestamp
36
+ });
37
+
38
+ // Emit challenge event
39
+ this.emit(context, "auth:challenge", challenge);
40
+
41
+ try {
42
+ // Sign challenge
43
+ const messageToSign = `Teneo authentication challenge: ${challenge}`;
44
+ const signature = await context.account.signMessage({
45
+ message: messageToSign
46
+ });
47
+
48
+ // Send authentication
49
+ await context.sendMessage(
50
+ createAuth(context.account.address, signature, messageToSign, this.clientType)
51
+ );
52
+ } catch (error) {
53
+ context.logger.error("Failed to sign challenge", error);
54
+ this.emit(context, "auth:error", "Failed to sign challenge");
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Handler for error messages
3
+ * Processes error messages from the server
4
+ */
5
+
6
+ import { ErrorMessage, ErrorMessageSchema } from "../../types";
7
+ import { MessageError } from "../../types/events";
8
+ import { BaseMessageHandler } from "./base-handler";
9
+ import { HandlerContext } from "./types";
10
+
11
+ export class ErrorMessageHandler extends BaseMessageHandler<ErrorMessage> {
12
+ readonly type = "error" as const;
13
+ readonly schema = ErrorMessageSchema;
14
+
15
+ protected async handleValidated(message: ErrorMessage, context: HandlerContext): Promise<void> {
16
+ context.logger.error("Received error message from server", {
17
+ code: message.data.code,
18
+ message: message.data.message
19
+ });
20
+
21
+ // Create error object
22
+ const error = new MessageError(message.content || message.data.message, message.data);
23
+
24
+ // Emit error event
25
+ this.emit(context, "error", error);
26
+ }
27
+ }