@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,347 @@
1
+ /**
2
+ * AgentRegistry - Manages agent state and lookup
3
+ * Handles agent caching and queries
4
+ */
5
+
6
+ import { EventEmitter } from "eventemitter3";
7
+ import { Agent, Logger } from "../types";
8
+ import { SDKEvents } from "../types/events";
9
+ import { AgentIdSchema, SearchQuerySchema } from "../types/validation";
10
+
11
+ export class AgentRegistry extends EventEmitter<SDKEvents> {
12
+ private readonly logger: Logger;
13
+ private readonly agents = new Map<string, Agent>();
14
+ private cachedAgents?: Readonly<Agent>[];
15
+ private isAgentsCacheDirty = true;
16
+
17
+ // PERF-3: Search indices for O(1) lookups
18
+ private capabilityIndex = new Map<string, Set<string>>();
19
+ private nameTokenIndex = new Map<string, Set<string>>();
20
+ private statusIndex = new Map<string, Set<string>>();
21
+
22
+ constructor(logger: Logger) {
23
+ super();
24
+ this.logger = logger;
25
+ }
26
+
27
+ /**
28
+ * Gets a cached list of all available agents in the network.
29
+ * Uses lazy caching with dirty flag for optimal performance.
30
+ * Returns a read-only array with defensive copies to prevent external modification.
31
+ *
32
+ * @returns Read-only array of agent copies
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const agents = agentRegistry.getAgents();
37
+ * console.log(`${agents.length} agents available`);
38
+ * agents.forEach(agent => console.log(agent.name));
39
+ * ```
40
+ */
41
+ public getAgents(): ReadonlyArray<Readonly<Agent>> {
42
+ if (this.isAgentsCacheDirty || !this.cachedAgents) {
43
+ this.rebuildCache();
44
+ }
45
+ return this.cachedAgents!.map((agent) => ({ ...agent }));
46
+ }
47
+
48
+ /**
49
+ * Gets a specific agent by its unique identifier.
50
+ * Performs O(1) lookup using internal Map structure.
51
+ * Returns a defensive copy to prevent external modification of agent state.
52
+ *
53
+ * @param agentId - The unique identifier of the agent
54
+ * @returns Copy of the agent object if found, undefined otherwise
55
+ * @throws {ValidationError} If agentId is invalid
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * const agent = agentRegistry.getAgent('weather-agent-001');
60
+ * if (agent) {
61
+ * console.log(`Found: ${agent.name}`);
62
+ * console.log(`Capabilities: ${agent.capabilities?.length}`);
63
+ * }
64
+ * ```
65
+ */
66
+ public getAgent(agentId: string): Readonly<Agent> | undefined {
67
+ // Validate agent ID
68
+ const validatedAgentId = AgentIdSchema.parse(agentId);
69
+
70
+ const agent = this.agents.get(validatedAgentId);
71
+ return agent ? { ...agent } : undefined;
72
+ }
73
+
74
+ /**
75
+ * Finds all agents that have a specific capability.
76
+ * PERF-3: Uses O(1) capability index lookup instead of O(n) filtering.
77
+ *
78
+ * @param capabilityName - The name of the capability to search for (case-insensitive)
79
+ * @returns Read-only array of agents with the specified capability
80
+ * @throws {ValidationError} If capabilityName is invalid
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * const weatherAgents = agentRegistry.findByCapability('weather-forecast');
85
+ * console.log(`Found ${weatherAgents.length} agents with weather-forecast capability`);
86
+ * ```
87
+ */
88
+ public findByCapability(capabilityName: string): ReadonlyArray<Agent> {
89
+ // Validate capability name
90
+ const validatedCapabilityName = SearchQuerySchema.parse(capabilityName);
91
+
92
+ // Ensure cache is up to date
93
+ if (this.isAgentsCacheDirty || !this.cachedAgents) {
94
+ this.rebuildCache();
95
+ }
96
+
97
+ // O(1) index lookup instead of O(n) filter
98
+ const normalizedCapName = validatedCapabilityName.toLowerCase();
99
+ const agentIds = this.capabilityIndex.get(normalizedCapName);
100
+
101
+ if (!agentIds || agentIds.size === 0) {
102
+ return [];
103
+ }
104
+
105
+ // Map agent IDs to agent objects with defensive copies
106
+ return Array.from(agentIds)
107
+ .map((id) => this.agents.get(id))
108
+ .filter((agent): agent is Agent => agent !== undefined)
109
+ .map((agent) => ({ ...agent }));
110
+ }
111
+
112
+ /**
113
+ * Finds agents by name using case-insensitive partial matching.
114
+ * PERF-3: Uses O(k) token index lookups instead of O(n) substring search,
115
+ * where k is the number of tokens in the search query.
116
+ *
117
+ * @param name - The name or partial name to search for (case-insensitive)
118
+ * @returns Read-only array of agents matching the name search
119
+ * @throws {ValidationError} If name is invalid
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * // Find all agents with "weather" in their name
124
+ * const weatherAgents = agentRegistry.findByName('weather');
125
+ * weatherAgents.forEach(agent => console.log(agent.name));
126
+ * ```
127
+ */
128
+ public findByName(name: string): ReadonlyArray<Agent> {
129
+ // Validate name
130
+ const validatedName = SearchQuerySchema.parse(name);
131
+
132
+ // Ensure cache is up to date
133
+ if (this.isAgentsCacheDirty || !this.cachedAgents) {
134
+ this.rebuildCache();
135
+ }
136
+
137
+ // Tokenize search query
138
+ const searchTokens = this.tokenizeString(validatedName);
139
+
140
+ if (searchTokens.length === 0) {
141
+ return [];
142
+ }
143
+
144
+ // Find agents that match ANY token (union)
145
+ const matchingAgentIds = new Set<string>();
146
+
147
+ for (const token of searchTokens) {
148
+ const agentIds = this.nameTokenIndex.get(token);
149
+ if (agentIds) {
150
+ agentIds.forEach((id) => matchingAgentIds.add(id));
151
+ }
152
+ }
153
+
154
+ if (matchingAgentIds.size === 0) {
155
+ return [];
156
+ }
157
+
158
+ // Map agent IDs to agent objects with defensive copies
159
+ return Array.from(matchingAgentIds)
160
+ .map((id) => this.agents.get(id))
161
+ .filter((agent): agent is Agent => agent !== undefined)
162
+ .map((agent) => ({ ...agent }));
163
+ }
164
+
165
+ /**
166
+ * Finds all agents with a specific status.
167
+ * PERF-3: Uses O(1) status index lookup instead of O(n) filtering.
168
+ *
169
+ * @param status - The status to search for: 'online' or 'offline' (case-insensitive)
170
+ * @returns Read-only array of agents with the specified status
171
+ * @throws {ValidationError} If status is invalid
172
+ *
173
+ * @example
174
+ * ```typescript
175
+ * const onlineAgents = agentRegistry.findByStatus('online');
176
+ * console.log(`Found ${onlineAgents.length} online agents`);
177
+ * ```
178
+ */
179
+ public findByStatus(status: string): ReadonlyArray<Agent> {
180
+ // Validate status
181
+ const validatedStatus = SearchQuerySchema.parse(status);
182
+
183
+ // Ensure cache is up to date
184
+ if (this.isAgentsCacheDirty || !this.cachedAgents) {
185
+ this.rebuildCache();
186
+ }
187
+
188
+ // O(1) index lookup instead of O(n) filter
189
+ const normalizedStatus = validatedStatus.toLowerCase();
190
+ const agentIds = this.statusIndex.get(normalizedStatus);
191
+
192
+ if (!agentIds || agentIds.size === 0) {
193
+ return [];
194
+ }
195
+
196
+ // Map agent IDs to agent objects with defensive copies
197
+ return Array.from(agentIds)
198
+ .map((id) => this.agents.get(id))
199
+ .filter((agent): agent is Agent => agent !== undefined)
200
+ .map((agent) => ({ ...agent }));
201
+ }
202
+
203
+ /**
204
+ * Updates the registry with a new list of agents.
205
+ * Merges with existing agents and marks cache as dirty.
206
+ * Emits 'agent:list' event with the new agents.
207
+ *
208
+ * @internal This method is for internal SDK use
209
+ * @param agents - Array of agents to add or update in the registry
210
+ *
211
+ * @example
212
+ * ```typescript
213
+ * // Internal SDK usage
214
+ * agentRegistry.updateAgents(newAgentList);
215
+ * ```
216
+ */
217
+ public updateAgents(agents: Agent[]): void {
218
+ this.logger.debug("AgentRegistry: Updating agents", { count: agents.length });
219
+
220
+ for (const agent of agents) {
221
+ this.agents.set(agent.id, agent);
222
+ }
223
+
224
+ this.isAgentsCacheDirty = true;
225
+ this.emit("agent:list", agents);
226
+ }
227
+
228
+ /**
229
+ * Updates a single agent in the registry.
230
+ * Adds new agent or updates existing one, then marks cache as dirty.
231
+ *
232
+ * @internal This method is for internal SDK use
233
+ * @param agent - The agent to add or update
234
+ *
235
+ * @example
236
+ * ```typescript
237
+ * // Internal SDK usage
238
+ * agentRegistry.updateAgent(updatedAgent);
239
+ * ```
240
+ */
241
+ public updateAgent(agent: Agent): void {
242
+ this.logger.debug("AgentRegistry: Updating agent", { id: agent.id, name: agent.name });
243
+ this.agents.set(agent.id, agent);
244
+ this.isAgentsCacheDirty = true;
245
+ }
246
+
247
+ /**
248
+ * Clears all agents from the registry.
249
+ * Removes all cached agents and marks cache as dirty.
250
+ *
251
+ * @example
252
+ * ```typescript
253
+ * agentRegistry.clear();
254
+ * console.log('All agents cleared');
255
+ * ```
256
+ */
257
+ public clear(): void {
258
+ this.agents.clear();
259
+ this.isAgentsCacheDirty = true;
260
+ }
261
+
262
+ /**
263
+ * Destroys the agent registry and cleans up resources.
264
+ * Clears all agents and removes all event listeners.
265
+ * After destruction, the registry cannot be reused.
266
+ *
267
+ * @example
268
+ * ```typescript
269
+ * agentRegistry.destroy();
270
+ * console.log('Agent registry destroyed');
271
+ * ```
272
+ */
273
+ public destroy(): void {
274
+ this.logger.info("AgentRegistry: Destroying");
275
+ this.clear();
276
+ this.removeAllListeners();
277
+ }
278
+
279
+ /**
280
+ * Rebuilds the agent cache and all search indices.
281
+ * This is called automatically when the cache becomes dirty.
282
+ * PERF-3: Builds capability, name token, and status indices for O(1) lookups.
283
+ *
284
+ * @private
285
+ */
286
+ private rebuildCache(): void {
287
+ // Rebuild agent array
288
+ this.cachedAgents = Array.from(this.agents.values()).map((agent) => ({ ...agent }));
289
+
290
+ // Clear indices
291
+ this.capabilityIndex.clear();
292
+ this.nameTokenIndex.clear();
293
+ this.statusIndex.clear();
294
+
295
+ // Populate indices
296
+ for (const agent of this.agents.values()) {
297
+ // Index capabilities
298
+ if (agent.capabilities) {
299
+ for (const capability of agent.capabilities) {
300
+ const capName = capability.name.toLowerCase();
301
+ if (!this.capabilityIndex.has(capName)) {
302
+ this.capabilityIndex.set(capName, new Set());
303
+ }
304
+ this.capabilityIndex.get(capName)!.add(agent.id);
305
+ }
306
+ }
307
+
308
+ // Index name tokens (for partial matching)
309
+ const nameTokens = this.tokenizeString(agent.name);
310
+ for (const token of nameTokens) {
311
+ if (!this.nameTokenIndex.has(token)) {
312
+ this.nameTokenIndex.set(token, new Set());
313
+ }
314
+ this.nameTokenIndex.get(token)!.add(agent.id);
315
+ }
316
+
317
+ // Index status
318
+ const status = agent.status.toLowerCase();
319
+ if (!this.statusIndex.has(status)) {
320
+ this.statusIndex.set(status, new Set());
321
+ }
322
+ this.statusIndex.get(status)!.add(agent.id);
323
+ }
324
+
325
+ this.isAgentsCacheDirty = false;
326
+ }
327
+
328
+ /**
329
+ * Tokenizes a string into searchable tokens.
330
+ * Splits on whitespace and special characters, converts to lowercase.
331
+ *
332
+ * @param str - String to tokenize
333
+ * @returns Array of lowercase tokens
334
+ * @private
335
+ *
336
+ * @example
337
+ * ```typescript
338
+ * tokenizeString("Weather API v2.0") => ["weather", "api", "v2", "0"]
339
+ * ```
340
+ */
341
+ private tokenizeString(str: string): string[] {
342
+ return str
343
+ .toLowerCase()
344
+ .split(/[\s\-_.,;:()[\]{}]+/)
345
+ .filter((token) => token.length > 0);
346
+ }
347
+ }
@@ -0,0 +1,195 @@
1
+ /**
2
+ * ConnectionManager - Manages WebSocket connection lifecycle
3
+ * Handles connection, disconnection, and state management
4
+ */
5
+
6
+ import { EventEmitter } from "eventemitter3";
7
+ import { WebSocketClient } from "../core/websocket-client";
8
+ import { ConnectionState, AuthenticationState, Logger } from "../types";
9
+ import { SDKEvents } from "../types/events";
10
+
11
+ export class ConnectionManager extends EventEmitter<SDKEvents> {
12
+ private readonly wsClient: WebSocketClient;
13
+ private readonly logger: Logger;
14
+
15
+ constructor(wsClient: WebSocketClient, logger: Logger) {
16
+ super();
17
+ this.wsClient = wsClient;
18
+ this.logger = logger;
19
+ this.setupEventForwarding();
20
+ }
21
+
22
+ /**
23
+ * Initiates WebSocket connection through the underlying WebSocket client.
24
+ * Delegates all connection logic including authentication and reconnection.
25
+ *
26
+ * @returns Promise that resolves when connection and authentication complete
27
+ * @throws {TimeoutError} If connection times out
28
+ * @throws {ConnectionError} If WebSocket connection fails
29
+ * @throws {AuthenticationError} If authentication fails
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const connectionMgr = new ConnectionManager(wsClient, logger);
34
+ * await connectionMgr.connect();
35
+ * console.log('Connected via connection manager');
36
+ * ```
37
+ */
38
+ public async connect(): Promise<void> {
39
+ this.logger.info("ConnectionManager: Initiating connection");
40
+ await this.wsClient.connect();
41
+ }
42
+
43
+ /**
44
+ * Disconnects from the WebSocket server through the underlying WebSocket client.
45
+ * Cleans up all connection resources and stops reconnection attempts.
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * connectionMgr.disconnect();
50
+ * console.log('Disconnected');
51
+ * ```
52
+ */
53
+ public disconnect(): void {
54
+ this.logger.info("ConnectionManager: Disconnecting");
55
+ this.wsClient.disconnect();
56
+ }
57
+
58
+ /**
59
+ * Gets the current WebSocket connection state with detailed status information.
60
+ * Includes connection status, reconnection attempts, and error information.
61
+ *
62
+ * @returns Connection state object with status, attempts, and timestamps
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const state = connectionMgr.getConnectionState();
67
+ * console.log(`Connected: ${state.connected}`);
68
+ * console.log(`Reconnecting: ${state.reconnecting}`);
69
+ * ```
70
+ */
71
+ public getConnectionState(): ConnectionState {
72
+ return this.wsClient.getConnectionState();
73
+ }
74
+
75
+ /**
76
+ * Gets the current authentication state with wallet and room information.
77
+ * Includes authentication status, wallet address, and accessible rooms.
78
+ *
79
+ * @returns Authentication state object with wallet, challenge, and room access
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * const authState = connectionMgr.getAuthState();
84
+ * if (authState.authenticated) {
85
+ * console.log(`Authenticated as: ${authState.walletAddress}`);
86
+ * }
87
+ * ```
88
+ */
89
+ public getAuthState(): AuthenticationState {
90
+ return this.wsClient.getAuthState();
91
+ }
92
+
93
+ /**
94
+ * Quick check for whether the WebSocket is currently connected.
95
+ * Convenience getter for immediate connection status.
96
+ *
97
+ * @returns True if connected, false otherwise
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * if (connectionMgr.isConnected) {
102
+ * console.log('Ready to send messages');
103
+ * }
104
+ * ```
105
+ */
106
+ public get isConnected(): boolean {
107
+ return this.wsClient.isConnected;
108
+ }
109
+
110
+ /**
111
+ * Quick check for whether authentication is complete.
112
+ * Convenience getter for immediate authentication status.
113
+ *
114
+ * @returns True if authenticated, false otherwise
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * if (connectionMgr.isAuthenticated) {
119
+ * console.log('Authenticated and ready');
120
+ * }
121
+ * ```
122
+ */
123
+ public get isAuthenticated(): boolean {
124
+ return this.wsClient.isAuthenticated;
125
+ }
126
+
127
+ /**
128
+ * Gets the underlying WebSocket client instance for advanced usage.
129
+ * Provides direct access to low-level WebSocket operations.
130
+ *
131
+ * ⚠️ **WARNING:** This method returns a direct reference to the internal WebSocketClient.
132
+ * Modifying the client's state or calling methods directly may lead to unexpected behavior
133
+ * or break SDK functionality. Use this only for read-only operations like event listening.
134
+ *
135
+ * @internal This method is for internal SDK use and advanced scenarios
136
+ * @returns The WebSocketClient instance (direct reference - use with caution)
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * // Advanced usage - listen to low-level events (read-only)
141
+ * const wsClient = connectionMgr.getWebSocketClient();
142
+ * wsClient.on('message:received', (msg) => console.log(msg));
143
+ * ```
144
+ */
145
+ public getWebSocketClient(): WebSocketClient {
146
+ return this.wsClient;
147
+ }
148
+
149
+ /**
150
+ * Set up event forwarding from WebSocket client
151
+ */
152
+ private setupEventForwarding(): void {
153
+ // Forward connection events
154
+ this.wsClient.on("connection:open", () => this.emit("connection:open"));
155
+ this.wsClient.on("connection:close", (code, reason) =>
156
+ this.emit("connection:close", code, reason)
157
+ );
158
+ this.wsClient.on("connection:error", (error) => this.emit("connection:error", error));
159
+ this.wsClient.on("connection:reconnecting", (attempt) =>
160
+ this.emit("connection:reconnecting", attempt)
161
+ );
162
+ this.wsClient.on("connection:reconnected", () => this.emit("connection:reconnected"));
163
+ this.wsClient.on("connection:state", (state) => this.emit("connection:state", state));
164
+
165
+ // Forward auth events
166
+ this.wsClient.on("auth:challenge", (challenge) => this.emit("auth:challenge", challenge));
167
+ this.wsClient.on("auth:success", (state) => this.emit("auth:success", state));
168
+ this.wsClient.on("auth:error", (error) => this.emit("auth:error", error));
169
+ this.wsClient.on("auth:state", (state) => this.emit("auth:state", state));
170
+
171
+ // Forward lifecycle events
172
+ this.wsClient.on("ready", () => this.emit("ready"));
173
+ this.wsClient.on("disconnect", () => this.emit("disconnect"));
174
+
175
+ // Forward error events
176
+ this.wsClient.on("error", (error) => this.emit("error", error));
177
+ }
178
+
179
+ /**
180
+ * Destroys the connection manager and cleans up all resources.
181
+ * Disconnects from the server and removes all event listeners.
182
+ * After destruction, the manager cannot be reused.
183
+ *
184
+ * @example
185
+ * ```typescript
186
+ * connectionMgr.destroy();
187
+ * console.log('Connection manager destroyed');
188
+ * ```
189
+ */
190
+ public destroy(): void {
191
+ this.logger.info("ConnectionManager: Destroying");
192
+ this.disconnect();
193
+ this.removeAllListeners();
194
+ }
195
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Manager exports
3
+ * Provides focused classes for different SDK responsibilities
4
+ */
5
+
6
+ export { ConnectionManager } from "./connection-manager";
7
+ export { RoomManager } from "./room-manager";
8
+ export { AgentRegistry } from "./agent-registry";
9
+ export { MessageRouter, type SendMessageOptions, type AgentCommand } from "./message-router";