@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,1022 @@
1
+ /**
2
+ * Main Teneo Protocol SDK class
3
+ * Provides a unified interface for external platforms to interact with Teneo agents
4
+ * Uses manager classes to follow Single Responsibility Principle
5
+ */
6
+
7
+ import { EventEmitter } from "eventemitter3";
8
+ import { z } from "zod";
9
+ import {
10
+ SDKConfig,
11
+ PartialSDKConfig,
12
+ PartialSDKConfigSchema,
13
+ SDKConfigBuilder,
14
+ Agent,
15
+ Room,
16
+ RoomInfo,
17
+ Logger,
18
+ validateConfig,
19
+ DEFAULT_CONFIG,
20
+ ResponseFormatSchema,
21
+ type HealthStatus
22
+ } from "./types";
23
+ import { SDKEvents, SDKError } from "./types/events";
24
+ import { ErrorCode } from "./types/error-codes";
25
+ import { WebSocketClient } from "./core/websocket-client";
26
+ import { WebhookHandler } from "./handlers/webhook-handler";
27
+ import {
28
+ ResponseFormatter,
29
+ FormattedResponse,
30
+ ResponseFormatOptions
31
+ } from "./formatters/response-formatter";
32
+ import {
33
+ ConnectionManager,
34
+ RoomManager,
35
+ AgentRegistry,
36
+ MessageRouter,
37
+ SendMessageOptions,
38
+ AgentCommand
39
+ } from "./managers";
40
+ import { createPinoLogger } from "./utils/logger";
41
+ import { RoomIdSchema, AgentIdSchema, AgentCommandContentSchema } from "./types/validation";
42
+
43
+ // Re-export types for external use
44
+ export type { SendMessageOptions, AgentCommand };
45
+
46
+ // Zod schemas for SDK-specific interfaces
47
+ export const SendMessageOptionsSchema = z.object({
48
+ room: RoomIdSchema.optional(),
49
+ from: z.string().optional(),
50
+ waitForResponse: z.boolean().optional(),
51
+ timeout: z.number().min(1000).max(300000).optional(),
52
+ format: z.union([ResponseFormatSchema, z.literal("raw"), z.literal("humanized")]).optional()
53
+ });
54
+
55
+ export const AgentCommandSchema = z.object({
56
+ agent: AgentIdSchema,
57
+ command: AgentCommandContentSchema,
58
+ room: RoomIdSchema.optional()
59
+ });
60
+
61
+ export class TeneoSDK extends EventEmitter<SDKEvents> {
62
+ private config: SDKConfig;
63
+ private readonly logger: Logger;
64
+ private isDestroyed = false;
65
+
66
+ // Core components
67
+ private readonly wsClient: WebSocketClient;
68
+ private readonly webhookHandler: WebhookHandler;
69
+ private readonly responseFormatter: ResponseFormatter;
70
+
71
+ // Managers
72
+ private readonly connection: ConnectionManager;
73
+ private readonly rooms: RoomManager;
74
+ private readonly agents: AgentRegistry;
75
+ private readonly messages: MessageRouter;
76
+
77
+ /**
78
+ * Creates a new instance of the Teneo Protocol SDK.
79
+ * Initializes all core components, managers, and validates the provided configuration.
80
+ * The SDK handles WebSocket connections, authentication, message routing, and webhook delivery.
81
+ *
82
+ * @param config - Partial SDK configuration object (only wsUrl is required)
83
+ * @param config.wsUrl - WebSocket URL to connect to (e.g., 'wss://teneo.example.com')
84
+ * @param config.privateKey - Optional Ethereum private key for wallet-based authentication
85
+ * @param config.walletAddress - Optional wallet address (derived from privateKey if not provided)
86
+ * @param config.autoJoinRooms - Optional array of room IDs to subscribe to automatically on connection
87
+ * @param config.webhookUrl - Optional webhook URL for receiving event notifications
88
+ * @param config.reconnect - Enable automatic reconnection (default: true)
89
+ * @param config.logLevel - Logging level: 'debug', 'info', 'warn', 'error', 'silent' (default: 'info')
90
+ * @param config.responseFormat - Response format: 'raw', 'humanized', 'both' (default: 'humanized')
91
+ *
92
+ * @throws {SDKError} If configuration is invalid (ErrorCode.INVALID_CONFIG)
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * // Minimal configuration
97
+ * const sdk = new TeneoSDK({
98
+ * wsUrl: 'wss://teneo.example.com',
99
+ * privateKey: '0x...'
100
+ * });
101
+ *
102
+ * // Full configuration
103
+ * const sdk = new TeneoSDK({
104
+ * wsUrl: 'wss://teneo.example.com',
105
+ * privateKey: '0x...',
106
+ * autoJoinRooms: ['general', 'announcements'],
107
+ * webhookUrl: 'https://api.example.com/webhooks',
108
+ * logLevel: 'debug',
109
+ * responseFormat: 'both',
110
+ * reconnect: true,
111
+ * maxReconnectAttempts: 10
112
+ * });
113
+ *
114
+ * // Using builder pattern (recommended for complex configs)
115
+ * const sdk = TeneoSDK.builder()
116
+ * .wsUrl('wss://teneo.example.com')
117
+ * .privateKey('0x...')
118
+ * .withAutoJoinRooms(['general'])
119
+ * .build();
120
+ * ```
121
+ *
122
+ * @see {@link SDKConfigBuilder} for fluent configuration API
123
+ * @see {@link TeneoSDK.builder} for creating a configuration builder
124
+ */
125
+ constructor(config: PartialSDKConfig) {
126
+ super();
127
+
128
+ try {
129
+ // Validate partial config first
130
+ const partialConfig = PartialSDKConfigSchema.parse(config);
131
+
132
+ // Merge with defaults
133
+ const fullConfig = { ...DEFAULT_CONFIG, ...partialConfig };
134
+
135
+ // Validate full configuration
136
+ this.config = validateConfig(fullConfig);
137
+
138
+ // Initialize logger
139
+ this.logger = this.config.logger ?? this.createDefaultLogger();
140
+
141
+ // Initialize core components
142
+ this.wsClient = new WebSocketClient(this.config);
143
+ this.webhookHandler = new WebhookHandler(this.config, this.logger);
144
+ this.responseFormatter = new ResponseFormatter({
145
+ format: this.config.responseFormat ?? "humanized",
146
+ includeMetadata: this.config.includeMetadata ?? false
147
+ });
148
+
149
+ // Initialize managers
150
+ this.connection = new ConnectionManager(this.wsClient, this.logger);
151
+ this.rooms = new RoomManager(this.wsClient, this.logger);
152
+ this.wsClient.setRoomManager(this.rooms); // Enable subscription tracking in handlers
153
+ this.agents = new AgentRegistry(this.logger);
154
+ this.messages = new MessageRouter(
155
+ this.wsClient,
156
+ this.webhookHandler,
157
+ this.responseFormatter,
158
+ this.logger,
159
+ {
160
+ messageTimeout: this.config.messageTimeout,
161
+ responseFormat: this.config.responseFormat
162
+ }
163
+ );
164
+
165
+ // Set up event forwarding
166
+ this.setupEventForwarding();
167
+
168
+ this.logger.info("TeneoSDK initialized", { wsUrl: this.config.wsUrl });
169
+ } catch (error) {
170
+ if (error instanceof z.ZodError) {
171
+ throw new SDKError("Invalid SDK configuration", ErrorCode.INVALID_CONFIG, error, false);
172
+ }
173
+ throw error;
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Establishes a connection to the Teneo network via WebSocket.
179
+ * Handles authentication automatically and joins any configured auto-join rooms.
180
+ * Emits 'connection:open', 'auth:success', and 'ready' events on successful connection.
181
+ *
182
+ * @returns Promise that resolves when connection and authentication are complete
183
+ * @throws {SDKError} If the SDK has been destroyed (ErrorCode.SDK_DESTROYED)
184
+ * @throws {ConnectionError} If WebSocket connection fails
185
+ * @throws {AuthenticationError} If authentication fails
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * const sdk = new TeneoSDK({ wsUrl: 'wss://example.com', privateKey: '0x...' });
190
+ * await sdk.connect();
191
+ * console.log('Connected to Teneo network');
192
+ * ```
193
+ */
194
+ public async connect(): Promise<void> {
195
+ if (this.isDestroyed) {
196
+ throw new SDKError("SDK has been destroyed", ErrorCode.SDK_DESTROYED, null, false);
197
+ }
198
+
199
+ try {
200
+ this.logger.info("Connecting to Teneo network");
201
+ await this.connection.connect();
202
+
203
+ // Auto-join rooms if configured
204
+ if (this.config.autoJoinRooms && this.config.autoJoinRooms.length > 0) {
205
+ for (const room of this.config.autoJoinRooms) {
206
+ await this.rooms.subscribeToRoom(room);
207
+ }
208
+ }
209
+
210
+ this.logger.info("Successfully connected to Teneo network");
211
+ } catch (error) {
212
+ this.logger.error("Failed to connect to Teneo network", error);
213
+ throw error;
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Disconnects from the Teneo network and cleans up all active connections.
219
+ * Clears all timers, pending messages, and stops automatic reconnection attempts.
220
+ * Emits 'disconnect' event after disconnection is complete.
221
+ *
222
+ * @example
223
+ * ```typescript
224
+ * sdk.disconnect();
225
+ * console.log('Disconnected from Teneo network');
226
+ * ```
227
+ */
228
+ public disconnect(): void {
229
+ this.logger.info("Disconnecting from Teneo network");
230
+ this.connection.disconnect();
231
+ }
232
+
233
+ /**
234
+ * Sends a message to agents via the coordinator, which intelligently selects
235
+ * the most appropriate agent based on the message content and agent capabilities.
236
+ * Can optionally wait for and return the agent's response.
237
+ *
238
+ * @param content - The message content to send to agents
239
+ * @param options - Optional message configuration
240
+ * @param options.room - Room to send message to (defaults to configured default room)
241
+ * @param options.from - Sender address (defaults to authenticated wallet address)
242
+ * @param options.waitForResponse - Whether to wait for agent response (default: false)
243
+ * @param options.timeout - Response timeout in milliseconds (default: 60000, max: 300000)
244
+ * @param options.format - Response format: 'raw', 'humanized', or 'both'
245
+ * @returns Promise that resolves to FormattedResponse if waitForResponse is true, void otherwise
246
+ * @throws {SDKError} If not connected to the network (ErrorCode.NOT_CONNECTED)
247
+ * @throws {ValidationError} If content is empty or options are invalid
248
+ * @throws {TimeoutError} If waitForResponse is true and timeout is exceeded
249
+ *
250
+ * @example
251
+ * ```typescript
252
+ * // Fire-and-forget message
253
+ * await sdk.sendMessage('What is the weather today?');
254
+ *
255
+ * // Wait for response
256
+ * const response = await sdk.sendMessage('What is 2+2?', {
257
+ * waitForResponse: true,
258
+ * timeout: 30000
259
+ * });
260
+ * console.log(response.humanized); // Agent's response in human-readable format
261
+ * ```
262
+ */
263
+ public async sendMessage(
264
+ content: string,
265
+ options: SendMessageOptions
266
+ ): Promise<FormattedResponse | void> {
267
+ return this.messages.sendMessage(content, options);
268
+ }
269
+
270
+ /**
271
+ * Sends a direct command to a specific agent, bypassing the coordinator.
272
+ * Use this when you know exactly which agent should handle the request.
273
+ * The command is formatted as "@agentName command" internally.
274
+ *
275
+ * @param command - The direct agent command configuration
276
+ * @param command.agent - The agent ID or name to send the command to
277
+ * @param command.command - The command text to send to the agent
278
+ * @param command.room - Room to send command to (defaults to configured default room)
279
+ * @returns Promise that resolves when the command is sent
280
+ * @throws {SDKError} If not connected to the network (ErrorCode.NOT_CONNECTED)
281
+ * @throws {ValidationError} If agent or command are empty, or room is not configured
282
+ *
283
+ * @example
284
+ * ```typescript
285
+ * // Send command to specific agent
286
+ * await sdk.sendDirectCommand({
287
+ * agent: 'weather-agent',
288
+ * command: 'Get forecast for New York',
289
+ * room: 'general'
290
+ * });
291
+ * ```
292
+ */
293
+ public async sendDirectCommand(command: AgentCommand): Promise<FormattedResponse | void> {
294
+ return this.messages.sendDirectCommand(command);
295
+ }
296
+
297
+ /**
298
+ * Subscribes to a specified room in the Teneo network.
299
+ * Agents in the room will be able to see and respond to your messages.
300
+ * Emits 'room:subscribed' event when successfully subscribed.
301
+ *
302
+ * @param roomId - The ID of the room to subscribe to
303
+ * @returns Promise that resolves when the room has been subscribed
304
+ * @throws {SDKError} If not connected to the network (ErrorCode.NOT_CONNECTED)
305
+ * @throws {ValidationError} If roomId is empty or invalid
306
+ *
307
+ * @example
308
+ * ```typescript
309
+ * await sdk.subscribeToRoom('general');
310
+ * console.log('Subscribed to general room');
311
+ * ```
312
+ */
313
+ public async subscribeToRoom(roomId: string): Promise<void> {
314
+ return this.rooms.subscribeToRoom(roomId);
315
+ }
316
+
317
+ /**
318
+ * Unsubscribes from a specified room in the Teneo network.
319
+ * You will no longer receive messages from agents in this room.
320
+ * Emits 'room:unsubscribed' event when successfully unsubscribed.
321
+ *
322
+ * @param roomId - The ID of the room to unsubscribe from
323
+ * @returns Promise that resolves when the room has been unsubscribed
324
+ * @throws {SDKError} If not connected to the network (ErrorCode.NOT_CONNECTED)
325
+ * @throws {ValidationError} If roomId is empty or invalid
326
+ *
327
+ * @example
328
+ * ```typescript
329
+ * await sdk.unsubscribeFromRoom('general');
330
+ * console.log('Unsubscribed from general room');
331
+ * ```
332
+ */
333
+ public async unsubscribeFromRoom(roomId: string): Promise<void> {
334
+ return this.rooms.unsubscribeFromRoom(roomId);
335
+ }
336
+
337
+ /**
338
+ * Lists all rooms available to the user.
339
+ * Fetches room list from the server including owned and shared rooms.
340
+ * Emits 'room:list' event when the list is received.
341
+ *
342
+ * @returns Promise that resolves to array of room information
343
+ * @throws {SDKError} If not connected to the network (ErrorCode.NOT_CONNECTED)
344
+ *
345
+ * @example
346
+ * ```typescript
347
+ * const rooms = await sdk.listRooms();
348
+ * rooms.forEach(room => {
349
+ * console.log(`${room.name} (${room.is_public ? 'public' : 'private'})`);
350
+ * console.log(`Owner: ${room.is_owner}`);
351
+ * });
352
+ * ```
353
+ */
354
+ public async listRooms(): Promise<RoomInfo[]> {
355
+ return this.rooms.listRooms();
356
+ }
357
+
358
+ /**
359
+ * Gets all rooms currently subscribed to.
360
+ * Returns array of room IDs that you're actively listening to for messages.
361
+ *
362
+ * @returns Array of subscribed room IDs
363
+ *
364
+ * @example
365
+ * ```typescript
366
+ * const rooms = sdk.getSubscribedRooms();
367
+ * console.log(`Subscribed to ${rooms.length} rooms:`, rooms);
368
+ * // Example output: Subscribed to 3 rooms: ['general', 'support', 'trading']
369
+ * ```
370
+ */
371
+ public getSubscribedRooms(): string[] {
372
+ return this.rooms.getSubscribedRooms();
373
+ }
374
+
375
+ /**
376
+ * Gets a list of all available agents in the Teneo network.
377
+ * The list is automatically updated when new agents join or leave.
378
+ * Returns a read-only array to prevent external modification.
379
+ *
380
+ * @returns Read-only array of all available agents
381
+ *
382
+ * @example
383
+ * ```typescript
384
+ * const agents = sdk.getAgents();
385
+ * console.log(`Found ${agents.length} agents:`);
386
+ * agents.forEach(agent => {
387
+ * console.log(`- ${agent.name}: ${agent.description}`);
388
+ * });
389
+ * ```
390
+ */
391
+ public getAgents(): ReadonlyArray<Agent> {
392
+ return this.agents.getAgents();
393
+ }
394
+
395
+ /**
396
+ * Gets a specific agent by its unique ID.
397
+ * Returns undefined if no agent with the specified ID exists.
398
+ *
399
+ * @param agentId - The unique identifier of the agent to retrieve
400
+ * @returns The agent object if found, undefined otherwise
401
+ *
402
+ * @example
403
+ * ```typescript
404
+ * const agent = sdk.getAgent('weather-agent-001');
405
+ * if (agent) {
406
+ * console.log(`Found agent: ${agent.name}`);
407
+ * console.log(`Status: ${agent.status}`);
408
+ * } else {
409
+ * console.log('Agent not found');
410
+ * }
411
+ * ```
412
+ */
413
+ public getAgent(agentId: string): Agent | undefined {
414
+ return this.agents.getAgent(agentId);
415
+ }
416
+
417
+ /**
418
+ * Finds all agents that have a specific capability using O(1) indexed lookup (PERF-3).
419
+ * Much faster than filtering through all agents manually.
420
+ * Uses capability index for constant-time lookups regardless of agent count.
421
+ *
422
+ * @param capability - The capability name to search for (case-insensitive)
423
+ * @returns Read-only array of agents with the specified capability
424
+ * @throws {ValidationError} If capability name is invalid
425
+ *
426
+ * @example
427
+ * ```typescript
428
+ * // Find all weather-capable agents
429
+ * const weatherAgents = sdk.findAgentsByCapability('weather-forecast');
430
+ * console.log(`Found ${weatherAgents.length} weather agents`);
431
+ *
432
+ * weatherAgents.forEach(agent => {
433
+ * console.log(`- ${agent.name}: ${agent.description}`);
434
+ * });
435
+ * ```
436
+ */
437
+ public findAgentsByCapability(capability: string): ReadonlyArray<Agent> {
438
+ return this.agents.findByCapability(capability);
439
+ }
440
+
441
+ /**
442
+ * Finds agents by name using O(k) token-based search (PERF-3).
443
+ * Supports partial matching - searches for tokens within agent names.
444
+ * Tokenizes both the search query and agent names for flexible matching.
445
+ *
446
+ * @param name - Name or partial name to search for (case-insensitive)
447
+ * @returns Read-only array of agents matching the search
448
+ * @throws {ValidationError} If name is invalid
449
+ *
450
+ * @example
451
+ * ```typescript
452
+ * // Find all agents with "weather" in their name
453
+ * const agents = sdk.findAgentsByName('weather');
454
+ * // Matches: "Weather Agent", "Weather Forecast Bot", "Advanced Weather API", etc.
455
+ *
456
+ * console.log(`Found ${agents.length} agents matching 'weather'`);
457
+ * ```
458
+ */
459
+ public findAgentsByName(name: string): ReadonlyArray<Agent> {
460
+ return this.agents.findByName(name);
461
+ }
462
+
463
+ /**
464
+ * Finds all agents with a specific status using O(1) indexed lookup (PERF-3).
465
+ * Uses status index for constant-time lookups regardless of agent count.
466
+ *
467
+ * @param status - Agent status: 'online' or 'offline' (case-insensitive)
468
+ * @returns Read-only array of agents with the specified status
469
+ * @throws {ValidationError} If status is invalid
470
+ *
471
+ * @example
472
+ * ```typescript
473
+ * // Get all online agents
474
+ * const onlineAgents = sdk.findAgentsByStatus('online');
475
+ * console.log(`${onlineAgents.length} agents are currently online`);
476
+ *
477
+ * // Get offline agents
478
+ * const offlineAgents = sdk.findAgentsByStatus('offline');
479
+ * ```
480
+ */
481
+ public findAgentsByStatus(status: string): ReadonlyArray<Agent> {
482
+ return this.agents.findByStatus(status);
483
+ }
484
+
485
+ /**
486
+ * Gets a list of all available rooms in the Teneo network.
487
+ * Includes rooms you have access to based on your authentication.
488
+ * Returns a read-only array to prevent external modification.
489
+ *
490
+ * @returns Read-only array of all available rooms
491
+ *
492
+ * @example
493
+ * ```typescript
494
+ * const rooms = sdk.getRooms();
495
+ * console.log(`Available rooms: ${rooms.length}`);
496
+ * rooms.forEach(room => {
497
+ * console.log(`- ${room.id} (${room.name})`);
498
+ * });
499
+ * ```
500
+ */
501
+ public getRooms(): ReadonlyArray<Room> {
502
+ return this.rooms.getRooms();
503
+ }
504
+
505
+ /**
506
+ * Gets a specific room by its unique ID.
507
+ * Returns undefined if no room with the specified ID exists or if you don't have access.
508
+ *
509
+ * @param roomId - The unique identifier of the room to retrieve
510
+ * @returns The room object if found, undefined otherwise
511
+ *
512
+ * @example
513
+ * ```typescript
514
+ * const room = sdk.getRoom('general');
515
+ * if (room) {
516
+ * console.log(`Found room: ${room.name}`);
517
+ * console.log(`Members: ${room.members?.length ?? 0}`);
518
+ * } else {
519
+ * console.log('Room not found or no access');
520
+ * }
521
+ * ```
522
+ */
523
+ public getRoom(roomId: string): Room | undefined {
524
+ return this.rooms.getRoom(roomId);
525
+ }
526
+
527
+ /**
528
+ * Configures webhook URL and headers for receiving real-time event notifications.
529
+ * Webhooks allow you to receive events at your server endpoint via HTTP POST requests.
530
+ * Events include messages, agent responses, errors, and connection state changes.
531
+ *
532
+ * @param url - The webhook URL endpoint to receive events (must be HTTPS unless localhost)
533
+ * @param headers - Optional custom HTTP headers to include with webhook requests
534
+ * @throws {WebhookError} If URL is invalid or insecure (non-HTTPS and not localhost)
535
+ *
536
+ * @example
537
+ * ```typescript
538
+ * sdk.configureWebhook('https://api.example.com/webhooks/teneo', {
539
+ * 'Authorization': 'Bearer your-token',
540
+ * 'X-Custom-Header': 'value'
541
+ * });
542
+ *
543
+ * // Listen for webhook events
544
+ * sdk.on('webhook:sent', (payload, url) => {
545
+ * console.log('Webhook sent:', payload.event);
546
+ * });
547
+ * ```
548
+ */
549
+ public configureWebhook(url: string, headers?: Record<string, string>): void {
550
+ this.webhookHandler.configure({
551
+ url,
552
+ headers,
553
+ retries: this.config.webhookRetries,
554
+ timeout: this.config.webhookTimeout
555
+ });
556
+ }
557
+
558
+ /**
559
+ * Gets the current WebSocket connection state including connection status,
560
+ * authentication status, reconnection attempts, and timestamps.
561
+ *
562
+ * @returns Object containing detailed connection state information
563
+ * @returns {boolean} returns.connected - Whether WebSocket is currently connected
564
+ * @returns {boolean} returns.authenticated - Whether authentication is complete
565
+ * @returns {boolean} returns.reconnecting - Whether currently attempting to reconnect
566
+ * @returns {number} returns.reconnectAttempts - Number of reconnection attempts made
567
+ * @returns {Date} returns.lastConnectedAt - Timestamp of last successful connection
568
+ * @returns {Date} returns.lastDisconnectedAt - Timestamp of last disconnection
569
+ * @returns {Error} returns.lastError - Last error that occurred
570
+ *
571
+ * @example
572
+ * ```typescript
573
+ * const state = sdk.getConnectionState();
574
+ * console.log(`Connected: ${state.connected}`);
575
+ * console.log(`Authenticated: ${state.authenticated}`);
576
+ * if (state.reconnecting) {
577
+ * console.log(`Reconnection attempts: ${state.reconnectAttempts}`);
578
+ * }
579
+ * ```
580
+ */
581
+ public getConnectionState() {
582
+ return this.connection.getConnectionState();
583
+ }
584
+
585
+ /**
586
+ * Gets the current authentication state including wallet address, rooms, and permissions.
587
+ * Updated after successful authentication and includes user profile information.
588
+ *
589
+ * @returns Object containing detailed authentication state information
590
+ * @returns {boolean} returns.authenticated - Whether authentication is complete
591
+ * @returns {string} returns.walletAddress - Authenticated wallet address
592
+ * @returns {string} returns.challenge - Authentication challenge string
593
+ * @returns {string[]} returns.rooms - Array of room IDs the user has access to
594
+ * @returns {Room[]} returns.roomObjects - Full room objects with details
595
+ *
596
+ * @example
597
+ * ```typescript
598
+ * const authState = sdk.getAuthState();
599
+ * if (authState.authenticated) {
600
+ * console.log(`Authenticated as: ${authState.walletAddress}`);
601
+ * console.log(`Access to ${authState.rooms?.length ?? 0} rooms`);
602
+ * } else {
603
+ * console.log('Not authenticated');
604
+ * }
605
+ * ```
606
+ */
607
+ public getAuthState() {
608
+ return this.connection.getAuthState();
609
+ }
610
+
611
+ /**
612
+ * Quick check for whether the WebSocket connection is currently active.
613
+ * This is a convenience getter that returns only the connection status.
614
+ * For detailed state information, use getConnectionState().
615
+ *
616
+ * @returns True if connected to the Teneo network, false otherwise
617
+ *
618
+ * @example
619
+ * ```typescript
620
+ * if (sdk.isConnected) {
621
+ * await sdk.sendMessage('Hello!');
622
+ * } else {
623
+ * console.log('Not connected');
624
+ * await sdk.connect();
625
+ * }
626
+ * ```
627
+ */
628
+ public get isConnected(): boolean {
629
+ return this.connection.isConnected;
630
+ }
631
+
632
+ /**
633
+ * Quick check for whether authentication is complete.
634
+ * This is a convenience getter that returns only the authentication status.
635
+ * For detailed auth information, use getAuthState().
636
+ *
637
+ * @returns True if authenticated with the Teneo network, false otherwise
638
+ *
639
+ * @example
640
+ * ```typescript
641
+ * if (sdk.isAuthenticated) {
642
+ * console.log('Ready to send messages');
643
+ * } else {
644
+ * console.log('Waiting for authentication...');
645
+ * }
646
+ * ```
647
+ */
648
+ public get isAuthenticated(): boolean {
649
+ return this.connection.isAuthenticated;
650
+ }
651
+
652
+ /**
653
+ * Configures how agent responses are formatted when received.
654
+ * Supports raw JSON, humanized text, or both formats simultaneously.
655
+ * Also controls metadata inclusion and pretty-printing options.
656
+ *
657
+ * @param options - Response formatting configuration options
658
+ * @param options.format - Format type: 'raw' (JSON), 'humanized' (text), or 'both'
659
+ * @param options.includeMetadata - Whether to include metadata in responses (timestamps, agent info, etc.)
660
+ * @param options.includeTimestamps - Whether to include timestamps in formatted output
661
+ * @param options.prettyPrint - Whether to pretty-print JSON output
662
+ *
663
+ * @example
664
+ * ```typescript
665
+ * // Get both raw JSON and humanized text
666
+ * sdk.setResponseFormat({
667
+ * format: 'both',
668
+ * includeMetadata: true
669
+ * });
670
+ *
671
+ * const response = await sdk.sendMessage('Hello', { waitForResponse: true });
672
+ * console.log(response.humanized); // Human-readable text
673
+ * console.log(response.raw); // Original JSON
674
+ * console.log(response.metadata); // Timestamp, agent info, etc.
675
+ * ```
676
+ */
677
+ public setResponseFormat(options: ResponseFormatOptions): void {
678
+ // Update formatter with new options
679
+ this.responseFormatter.setFormatOptions(options);
680
+
681
+ // Update config if format is specified
682
+ if (options.format !== undefined) {
683
+ this.config.responseFormat = options.format;
684
+ }
685
+ if (options.includeMetadata !== undefined) {
686
+ this.config.includeMetadata = options.includeMetadata;
687
+ }
688
+ }
689
+
690
+ /**
691
+ * Gets the current status of the webhook system including configuration,
692
+ * queue status, and pending/failed webhook deliveries.
693
+ *
694
+ * @returns Object containing webhook status information
695
+ * @returns {boolean} returns.configured - Whether a webhook URL is configured
696
+ * @returns {WebhookConfig} returns.config - Current webhook configuration (URL, headers, retries, etc.)
697
+ * @returns {Object} returns.queue - Webhook delivery queue status
698
+ * @returns {number} returns.queue.pending - Number of webhooks pending delivery
699
+ * @returns {boolean} returns.queue.processing - Whether webhooks are currently being processed
700
+ * @returns {number} returns.queue.failed - Number of failed webhook deliveries in queue
701
+ *
702
+ * @example
703
+ * ```typescript
704
+ * const status = sdk.getWebhookStatus();
705
+ * if (status.configured) {
706
+ * console.log(`Webhook URL: ${status.config.url}`);
707
+ * console.log(`Pending: ${status.queue.pending}`);
708
+ * console.log(`Failed: ${status.queue.failed}`);
709
+ * } else {
710
+ * console.log('Webhook not configured');
711
+ * }
712
+ * ```
713
+ */
714
+ public getWebhookStatus() {
715
+ return {
716
+ configured: this.webhookHandler.isConfigured,
717
+ config: this.webhookHandler.getConfig(),
718
+ queue: this.webhookHandler.getQueueStatus()
719
+ };
720
+ }
721
+
722
+ /**
723
+ * Retries all failed webhook deliveries in the queue.
724
+ * Resets attempt counters and immediately attempts to deliver all failed webhooks.
725
+ * Useful for recovering from temporary network issues or webhook endpoint downtime.
726
+ *
727
+ * @example
728
+ * ```typescript
729
+ * const status = sdk.getWebhookStatus();
730
+ * if (status.queue.failed > 0) {
731
+ * console.log(`Retrying ${status.queue.failed} failed webhooks...`);
732
+ * sdk.retryFailedWebhooks();
733
+ * }
734
+ * ```
735
+ */
736
+ public retryFailedWebhooks(): void {
737
+ this.webhookHandler.retryFailed();
738
+ }
739
+
740
+ /**
741
+ * Clears all pending and failed webhooks from the delivery queue.
742
+ * Use this to discard webhooks that are no longer relevant or to recover from queue issues.
743
+ * Warning: This will permanently discard all queued webhook events.
744
+ *
745
+ * @example
746
+ * ```typescript
747
+ * // Clear stale webhooks after reconfiguration
748
+ * sdk.clearWebhookQueue();
749
+ * sdk.configureWebhook('https://api.example.com/new-endpoint');
750
+ * console.log('Webhook queue cleared and reconfigured');
751
+ * ```
752
+ */
753
+ public clearWebhookQueue(): void {
754
+ this.webhookHandler.clearQueue();
755
+ }
756
+
757
+ /**
758
+ * Gets comprehensive health status of all SDK components.
759
+ * Useful for monitoring, debugging, and operational dashboards.
760
+ * Returns status of connection, webhooks, rate limiting, agents, and rooms.
761
+ *
762
+ * Overall health status calculation:
763
+ * - healthy: All components operational
764
+ * - degraded: Some components have issues but SDK is functional (e.g., webhook failures, circuit open)
765
+ * - unhealthy: Critical components are not operational (e.g., disconnected, authentication failed)
766
+ *
767
+ * @returns Complete health status object with all component states
768
+ * @returns {string} returns.status - Overall health: 'healthy', 'degraded', or 'unhealthy'
769
+ * @returns {string} returns.timestamp - ISO timestamp of health check
770
+ * @returns {Object} returns.connection - WebSocket connection health
771
+ * @returns {Object} returns.webhook - Webhook delivery health including circuit breaker state
772
+ * @returns {Object} returns.rateLimit - Rate limiter status (if configured)
773
+ * @returns {Object} returns.agents - Agent registry health
774
+ * @returns {Object} returns.rooms - Room management health
775
+ *
776
+ * @example
777
+ * ```typescript
778
+ * const health = sdk.getHealth();
779
+ * console.log(`SDK Status: ${health.status}`);
780
+ * console.log(`Connected: ${health.connection.status}`);
781
+ * console.log(`Agents: ${health.agents.count}`);
782
+ * console.log(`Webhook Circuit: ${health.webhook.circuitState}`);
783
+ *
784
+ * if (health.status !== 'healthy') {
785
+ * console.warn('SDK is degraded or unhealthy');
786
+ * if (!health.connection.authenticated) {
787
+ * console.log('Authentication issue');
788
+ * }
789
+ * if (health.webhook.failed > 0) {
790
+ * console.log(`${health.webhook.failed} failed webhooks`);
791
+ * }
792
+ * }
793
+ * ```
794
+ */
795
+ public getHealth(): HealthStatus {
796
+ const connectionState = this.connection.getConnectionState();
797
+ const webhookStatus = this.getWebhookStatus();
798
+ const rateLimitStatus = this.wsClient.getRateLimiterStatus();
799
+
800
+ // Determine connection status
801
+ let connectionStatus: "connected" | "disconnected" | "reconnecting";
802
+ if (connectionState.reconnecting) {
803
+ connectionStatus = "reconnecting";
804
+ } else if (connectionState.connected) {
805
+ connectionStatus = "connected";
806
+ } else {
807
+ connectionStatus = "disconnected";
808
+ }
809
+
810
+ // Determine webhook health
811
+ let webhookHealth: "healthy" | "degraded" | "unhealthy" = "healthy";
812
+ if (!webhookStatus.configured) {
813
+ // Webhook not configured is not unhealthy
814
+ webhookHealth = "healthy";
815
+ } else if (webhookStatus.queue.circuitState === "OPEN") {
816
+ webhookHealth = "unhealthy";
817
+ } else if (webhookStatus.queue.failed > 0 || webhookStatus.queue.circuitState === "HALF_OPEN") {
818
+ webhookHealth = "degraded";
819
+ }
820
+
821
+ // Determine overall health
822
+ let overallStatus: "healthy" | "degraded" | "unhealthy";
823
+ if (!connectionState.connected && !connectionState.reconnecting) {
824
+ overallStatus = "unhealthy";
825
+ } else if (!connectionState.authenticated && connectionState.reconnecting) {
826
+ overallStatus = "degraded";
827
+ } else if (webhookHealth === "unhealthy") {
828
+ overallStatus = "degraded";
829
+ } else if (webhookHealth === "degraded") {
830
+ overallStatus = "degraded";
831
+ } else {
832
+ overallStatus = "healthy";
833
+ }
834
+
835
+ return {
836
+ status: overallStatus,
837
+ timestamp: new Date().toISOString(),
838
+ connection: {
839
+ status: connectionStatus,
840
+ authenticated: connectionState.authenticated,
841
+ reconnectAttempts: connectionState.reconnectAttempts
842
+ },
843
+ webhook: {
844
+ configured: webhookStatus.configured,
845
+ status: webhookHealth,
846
+ pending: webhookStatus.queue.pending,
847
+ failed: webhookStatus.queue.failed,
848
+ circuitState: webhookStatus.queue.circuitState
849
+ },
850
+ rateLimit: rateLimitStatus,
851
+ agents: {
852
+ count: this.agents.getAgents().length
853
+ },
854
+ rooms: {
855
+ count: this.rooms.getRooms().length,
856
+ subscribedRooms: this.rooms.getSubscribedRooms()
857
+ }
858
+ };
859
+ }
860
+
861
+ /**
862
+ * Destroys the SDK instance and cleans up all resources.
863
+ * Disconnects from the network, clears all managers, removes event listeners,
864
+ * and marks the SDK as destroyed. After calling destroy(), the SDK instance
865
+ * cannot be reused - create a new instance instead.
866
+ * Emits 'destroy' event before completion.
867
+ *
868
+ * @example
869
+ * ```typescript
870
+ * // Clean up when shutting down
871
+ * sdk.destroy();
872
+ * console.log('SDK destroyed and resources cleaned up');
873
+ *
874
+ * // Create new instance if needed
875
+ * const newSdk = new TeneoSDK(config);
876
+ * ```
877
+ */
878
+ public destroy(): void {
879
+ if (this.isDestroyed) return;
880
+
881
+ this.logger.info("Destroying TeneoSDK");
882
+ this.isDestroyed = true;
883
+
884
+ // Destroy managers
885
+ this.connection.destroy();
886
+ this.rooms.destroy();
887
+ this.agents.destroy();
888
+ this.messages.destroy();
889
+
890
+ // Destroy other components
891
+ this.webhookHandler.destroy();
892
+ this.removeAllListeners();
893
+
894
+ this.emit("destroy");
895
+ }
896
+
897
+ /**
898
+ * Set up event forwarding from managers
899
+ */
900
+ private setupEventForwarding(): void {
901
+ // Forward connection events from ConnectionManager
902
+ this.connection.on("connection:open", () => this.emit("connection:open"));
903
+ this.connection.on("connection:close", (code, reason) =>
904
+ this.emit("connection:close", code, reason)
905
+ );
906
+ this.connection.on("connection:error", (error) => this.emit("connection:error", error));
907
+ this.connection.on("connection:reconnecting", (attempt) =>
908
+ this.emit("connection:reconnecting", attempt)
909
+ );
910
+ this.connection.on("connection:reconnected", () => this.emit("connection:reconnected"));
911
+ this.connection.on("connection:state", (state) => this.emit("connection:state", state));
912
+
913
+ // Forward auth events from ConnectionManager
914
+ this.connection.on("auth:challenge", (challenge) => this.emit("auth:challenge", challenge));
915
+ this.connection.on("auth:success", (state) => {
916
+ this.logger.debug("Received auth:success event in SDK", {
917
+ authenticated: state?.authenticated,
918
+ hasRooms: !!state?.roomObjects
919
+ });
920
+
921
+ // Update rooms from auth state
922
+ if (state.roomObjects) {
923
+ this.rooms.updateRoomsFromAuth(state.roomObjects);
924
+ }
925
+
926
+ this.emit("auth:success", state);
927
+ });
928
+ this.connection.on("auth:error", (error) => this.emit("auth:error", error));
929
+ this.connection.on("auth:state", (state) => this.emit("auth:state", state));
930
+
931
+ // Forward message events from MessageRouter
932
+ this.messages.on("message:sent", (message) => this.emit("message:sent", message));
933
+ this.messages.on("message:received", (message) => this.emit("message:received", message));
934
+ this.messages.on("message:error", (error, message) =>
935
+ this.emit("message:error", error, message)
936
+ );
937
+
938
+ // Forward agent events from MessageRouter
939
+ this.messages.on("agent:selected", (data) => this.emit("agent:selected", data));
940
+ this.messages.on("agent:response", (response) => this.emit("agent:response", response));
941
+
942
+ // Forward coordinator events from MessageRouter
943
+ this.messages.on("coordinator:processing", (request) =>
944
+ this.emit("coordinator:processing", request)
945
+ );
946
+ this.messages.on("coordinator:selected", (agentId, reasoning) =>
947
+ this.emit("coordinator:selected", agentId, reasoning)
948
+ );
949
+ this.messages.on("coordinator:error", (error) => this.emit("coordinator:error", error));
950
+
951
+ // Handle agent list updates from WebSocketClient
952
+ this.wsClient.on("agent:list", (agents) => {
953
+ this.agents.updateAgents(agents);
954
+ });
955
+
956
+ // Forward room events from WebSocketClient (emitted by handlers)
957
+ this.wsClient.on("room:subscribed", (data) => this.emit("room:subscribed", data));
958
+ this.wsClient.on("room:unsubscribed", (data) => this.emit("room:unsubscribed", data));
959
+
960
+ // Forward room events from RoomManager (if any direct emissions are added later)
961
+ this.rooms.on("room:subscribed", (data) => this.emit("room:subscribed", data));
962
+ this.rooms.on("room:unsubscribed", (data) => this.emit("room:unsubscribed", data));
963
+
964
+ // Forward webhook events from WebhookHandler
965
+ this.webhookHandler.on("webhook:sent", (payload, url) =>
966
+ this.emit("webhook:sent", payload, url)
967
+ );
968
+ this.webhookHandler.on("webhook:success", (response, url) =>
969
+ this.emit("webhook:success", response, url)
970
+ );
971
+ this.webhookHandler.on("webhook:error", (error, url) => this.emit("webhook:error", error, url));
972
+ this.webhookHandler.on("webhook:retry", (attempt, url) =>
973
+ this.emit("webhook:retry", attempt, url)
974
+ );
975
+
976
+ // Forward error events from ConnectionManager
977
+ this.connection.on("error", (error) => {
978
+ this.emit("error", error);
979
+ // Fire and forget - don't block event emission
980
+ this.webhookHandler
981
+ .sendWebhook("error", error, { code: error.code })
982
+ .catch((webhookError) => {
983
+ this.logger.error("Failed to send webhook for error event", webhookError);
984
+ });
985
+ });
986
+
987
+ // Forward lifecycle events from ConnectionManager
988
+ this.connection.on("ready", () => this.emit("ready"));
989
+ this.connection.on("disconnect", () => this.emit("disconnect"));
990
+ }
991
+
992
+ /**
993
+ * Create default logger using pino
994
+ */
995
+ private createDefaultLogger(): Logger {
996
+ return createPinoLogger(this.config.logLevel ?? "info", "TeneoSDK");
997
+ }
998
+
999
+ /**
1000
+ * Creates a new SDK configuration builder for fluent configuration.
1001
+ * The builder pattern provides a more intuitive way to configure the SDK
1002
+ * with method chaining and validation at each step.
1003
+ *
1004
+ * @returns A new SDKConfigBuilder instance for fluent configuration
1005
+ *
1006
+ * @example
1007
+ * ```typescript
1008
+ * const sdk = TeneoSDK.builder()
1009
+ * .wsUrl('wss://teneo.example.com')
1010
+ * .privateKey('0x...')
1011
+ * .withAutoJoinRooms(['general'])
1012
+ * .logLevel('debug')
1013
+ * .webhookUrl('https://api.example.com/webhooks')
1014
+ * .build();
1015
+ *
1016
+ * await sdk.connect();
1017
+ * ```
1018
+ */
1019
+ public static builder(): SDKConfigBuilder {
1020
+ return new SDKConfigBuilder();
1021
+ }
1022
+ }