@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,799 @@
1
+ /**
2
+ * Configuration schemas for Teneo Protocol SDK using Zod
3
+ * Provides runtime validation and TypeScript type inference
4
+ */
5
+
6
+ import { z } from "zod";
7
+ import { ClientTypeSchema, RoomSchema, MessageTypeSchema, MessageType } from "./messages";
8
+ import { RetryStrategySchema, type RetryStrategy } from "../utils/retry-policy";
9
+ import type { SecurePrivateKey } from "../utils/secure-private-key";
10
+
11
+ // Logger interface
12
+ export interface Logger {
13
+ debug: (message: string, data?: any) => void;
14
+ info: (message: string, data?: any) => void;
15
+ warn: (message: string, data?: any) => void;
16
+ error: (message: string, data?: any) => void;
17
+ }
18
+
19
+ // Logger schema - using loose function validation
20
+ export const LoggerSchema = z.object({
21
+ debug: z.function(),
22
+ info: z.function(),
23
+ warn: z.function(),
24
+ error: z.function()
25
+ }) as z.ZodType<Logger>;
26
+
27
+ // Log level schema
28
+ export const LogLevelSchema = z.enum(["debug", "info", "warn", "error", "silent"]);
29
+
30
+ // Response format schema
31
+ export const ResponseFormatSchema = z.enum(["raw", "humanized", "both"]);
32
+
33
+ // Webhook event type schema
34
+ export const WebhookEventTypeSchema = z.enum([
35
+ "message",
36
+ "task",
37
+ "task_response",
38
+ "agent_selected",
39
+ "error",
40
+ "connection_state",
41
+ "auth_state"
42
+ ]);
43
+
44
+ // Custom Zod schema for SecurePrivateKey or string
45
+ const PrivateKeySchema = z.union([
46
+ z.string(),
47
+ z.custom<SecurePrivateKey>(
48
+ (val) => {
49
+ // Check if it's a SecurePrivateKey instance
50
+ return val && typeof val === 'object' && 'use' in val && 'destroy' in val && 'isDestroyed' in val;
51
+ },
52
+ { message: "Must be a string or SecurePrivateKey instance" }
53
+ )
54
+ ]);
55
+
56
+ // SDK Configuration schema
57
+ export const SDKConfigSchema = z.object({
58
+ // WebSocket configuration
59
+ wsUrl: z
60
+ .string()
61
+ .url()
62
+ .refine((url) => url.startsWith("ws://") || url.startsWith("wss://"), {
63
+ message: "WebSocket URL must start with ws:// or wss://"
64
+ }),
65
+
66
+ // Authentication
67
+ privateKey: PrivateKeySchema.optional(),
68
+ walletAddress: z.string().optional(),
69
+
70
+ // Client identification
71
+ clientType: ClientTypeSchema.optional(),
72
+ clientName: z.string().optional(),
73
+
74
+ // Room configuration
75
+ autoJoinRooms: z.array(z.string()).optional(),
76
+
77
+ // Webhook configuration
78
+ webhookUrl: z.string().url().optional(),
79
+ webhookHeaders: z.record(z.string()).optional(),
80
+ webhookRetries: z.number().min(0).max(10).optional(),
81
+ webhookTimeout: z.number().min(1000).max(60000).optional(),
82
+ webhookRetryStrategy: RetryStrategySchema.optional(), // REL-3: Configurable retry strategy
83
+
84
+ // Connection settings
85
+ reconnect: z.boolean().optional(),
86
+ reconnectDelay: z.number().min(100).max(60000).optional(),
87
+ maxReconnectAttempts: z.number().min(0).max(100).optional(),
88
+ connectionTimeout: z.number().min(1000).max(120000).optional(),
89
+ reconnectStrategy: RetryStrategySchema.optional(), // REL-3: Configurable retry strategy
90
+
91
+ // Message settings
92
+ messageTimeout: z.number().min(1000).max(300000).optional(),
93
+ maxMessageSize: z.number().min(1024).max(10485760).optional(), // 1KB to 10MB
94
+ maxMessagesPerSecond: z.number().min(1).max(1000).optional(), // Rate limiting
95
+
96
+ // Response formatting
97
+ responseFormat: ResponseFormatSchema.optional(),
98
+ includeMetadata: z.boolean().optional(),
99
+
100
+ // Logging
101
+ logLevel: LogLevelSchema.optional(),
102
+ logger: LoggerSchema.optional(),
103
+
104
+ // Performance
105
+ enableCache: z.boolean().optional(),
106
+ cacheTimeout: z.number().min(1000).max(3600000).optional(),
107
+ maxCacheSize: z.number().min(1).max(10000).optional(),
108
+
109
+ // Security
110
+ validateSignatures: z.boolean().optional(),
111
+ trustedAgentAddresses: z.array(z.string()).optional(),
112
+ requireSignaturesFor: z.array(MessageTypeSchema).optional(),
113
+ strictSignatureValidation: z.boolean().optional(),
114
+ allowInsecureWebhooks: z.boolean().optional(),
115
+
116
+ // Message deduplication (CB-4)
117
+ enableMessageDeduplication: z.boolean().optional(),
118
+ messageDedupeTtl: z.number().min(1000).max(3600000).optional(), // 1s to 1 hour
119
+ messageDedupMaxSize: z.number().min(1).max(100000).optional()
120
+ });
121
+
122
+ // Partial config for constructor
123
+ export const PartialSDKConfigSchema = SDKConfigSchema.partial().refine(
124
+ (config) => config.wsUrl !== undefined,
125
+ { message: "WebSocket URL is required" }
126
+ );
127
+
128
+ // Connection state schema
129
+ export const ConnectionStateSchema = z.object({
130
+ connected: z.boolean(),
131
+ authenticated: z.boolean(),
132
+ reconnecting: z.boolean(),
133
+ reconnectAttempts: z.number(),
134
+ lastError: z.instanceof(Error).optional(),
135
+ lastConnectedAt: z.date().optional(),
136
+ lastDisconnectedAt: z.date().optional()
137
+ });
138
+
139
+ // Authentication state schema
140
+ export const AuthenticationStateSchema = z.object({
141
+ authenticated: z.boolean(),
142
+ clientId: z.string().optional(),
143
+ walletAddress: z.string().optional(),
144
+ isWhitelisted: z.boolean().optional(),
145
+ isAdmin: z.boolean().optional(),
146
+ nftVerified: z.boolean().optional(),
147
+ rooms: z.array(z.string()).optional(), // Room IDs for backward compatibility
148
+ roomObjects: z.array(RoomSchema).optional(), // Full room objects from auth
149
+ privateRoomId: z.string().optional(),
150
+ challenge: z.string().optional(),
151
+ challengeTimestamp: z.number().optional()
152
+ });
153
+
154
+ // Webhook config schema
155
+ export const WebhookConfigSchema = z.object({
156
+ url: z.string().url(),
157
+ headers: z.record(z.string()).optional(),
158
+ retries: z.number().min(0).max(10).optional(),
159
+ timeout: z.number().min(1000).max(60000).optional(),
160
+ events: z.array(WebhookEventTypeSchema).optional()
161
+ });
162
+
163
+ // Webhook payload schema
164
+ export const WebhookPayloadSchema = z.object({
165
+ event: WebhookEventTypeSchema,
166
+ timestamp: z.string(),
167
+ data: z.any(),
168
+ metadata: z
169
+ .object({
170
+ clientId: z.string().optional(),
171
+ roomId: z.string().optional(),
172
+ agentId: z.string().optional(),
173
+ taskId: z.string().optional()
174
+ })
175
+ .optional()
176
+ });
177
+
178
+ // Type inference from schemas
179
+ export type LogLevel = z.infer<typeof LogLevelSchema>;
180
+ export type ResponseFormat = z.infer<typeof ResponseFormatSchema>;
181
+ export type WebhookEventType = z.infer<typeof WebhookEventTypeSchema>;
182
+ export type SDKConfig = z.infer<typeof SDKConfigSchema>;
183
+ export type PartialSDKConfig = z.infer<typeof PartialSDKConfigSchema>;
184
+ export type ConnectionState = z.infer<typeof ConnectionStateSchema>;
185
+ export type AuthenticationState = z.infer<typeof AuthenticationStateSchema>;
186
+ export type WebhookConfig = z.infer<typeof WebhookConfigSchema>;
187
+ export type WebhookPayload = z.infer<typeof WebhookPayloadSchema>;
188
+
189
+ // Re-export RetryStrategy for convenience
190
+ export type { RetryStrategy };
191
+
192
+ // Default configuration with validation
193
+ export const DEFAULT_CONFIG: PartialSDKConfig = SDKConfigSchema.partial().parse({
194
+ wsUrl: "ws://localhost:8080/ws",
195
+ clientType: "user",
196
+ reconnect: true,
197
+ reconnectDelay: 5000,
198
+ maxReconnectAttempts: 10,
199
+ connectionTimeout: 30000,
200
+ messageTimeout: 30000,
201
+ maxMessageSize: 2 * 1024 * 1024, // 2MB
202
+ maxMessagesPerSecond: 10, // Rate limit: 10 messages per second
203
+ responseFormat: "humanized",
204
+ includeMetadata: false,
205
+ logLevel: "info",
206
+ enableCache: true,
207
+ cacheTimeout: 300000, // 5 minutes
208
+ maxCacheSize: 100,
209
+ validateSignatures: false,
210
+ trustedAgentAddresses: [],
211
+ requireSignaturesFor: ["task_response", "agent_selected"],
212
+ strictSignatureValidation: false,
213
+ allowInsecureWebhooks: false,
214
+ webhookRetries: 3,
215
+ webhookTimeout: 10000,
216
+ enableMessageDeduplication: true, // Enable by default to prevent duplicates
217
+ messageDedupeTtl: 60000, // 60 seconds (1 minute)
218
+ messageDedupMaxSize: 10000 // 10k messages
219
+ });
220
+
221
+ // Configuration validation with custom refinements
222
+ export function validateConfig(config: unknown): SDKConfig {
223
+ // First validate basic structure
224
+ const parsed = SDKConfigSchema.parse(config);
225
+
226
+ // Additional custom validations
227
+ if (parsed.webhookUrl && !parsed.allowInsecureWebhooks) {
228
+ const url = new URL(parsed.webhookUrl);
229
+ if (url.protocol === "http:" && !["localhost", "127.0.0.1", "::1"].includes(url.hostname)) {
230
+ throw new Error("Webhook URL must use HTTPS for non-localhost endpoints");
231
+ }
232
+ }
233
+
234
+ return parsed;
235
+ }
236
+
237
+ // Safe parse configuration
238
+ export function safeParseConfig(config: unknown): {
239
+ success: boolean;
240
+ data?: SDKConfig;
241
+ error?: z.ZodError | Error;
242
+ } {
243
+ try {
244
+ const data = validateConfig(config);
245
+ return { success: true, data };
246
+ } catch (error) {
247
+ if (error instanceof z.ZodError) {
248
+ return { success: false, error };
249
+ }
250
+ return { success: false, error: error as Error };
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Fluent API builder for creating Teneo SDK configurations with validation.
256
+ * Provides a chainable interface for configuring the SDK with runtime validation
257
+ * at each step. Call `.build()` to create the final validated configuration.
258
+ *
259
+ * This is the recommended way to configure the SDK for complex setups, as it provides
260
+ * better IDE intellisense, method chaining, and validates each configuration option
261
+ * as you set it.
262
+ *
263
+ * @example
264
+ * ```typescript
265
+ * // Basic configuration
266
+ * const config = new SDKConfigBuilder()
267
+ * .withWebSocketUrl('wss://teneo.example.com')
268
+ * .withAuthentication('0x...')
269
+ * .build();
270
+ *
271
+ * const sdk = new TeneoSDK(config);
272
+ *
273
+ * // Full configuration with all options
274
+ * const config = new SDKConfigBuilder()
275
+ * .withWebSocketUrl('wss://teneo.example.com')
276
+ * .withAuthentication('0x...', '0xYourWalletAddress')
277
+ * .withAutoJoinRooms(['general', 'announcements'])
278
+ * .withWebhook('https://api.example.com/webhooks', {
279
+ * 'Authorization': 'Bearer token'
280
+ * })
281
+ * .withReconnection(true, 5000, 10)
282
+ * .withResponseFormat({ format: 'both', includeMetadata: true })
283
+ * .withLogging('debug')
284
+ * .withCache(true, 300000, 100)
285
+ * .build();
286
+ *
287
+ * const sdk = new TeneoSDK(config);
288
+ *
289
+ * // Using via TeneoSDK.builder() (recommended)
290
+ * const sdk = TeneoSDK.builder()
291
+ * .withWebSocketUrl('wss://teneo.example.com')
292
+ * .withAuthentication('0x...')
293
+ * .withAutoJoinRooms(['general'])
294
+ * .build();
295
+ * ```
296
+ *
297
+ * @see {@link TeneoSDK} for the main SDK class
298
+ * @see {@link TeneoSDK.builder} for creating a builder instance
299
+ */
300
+ export class SDKConfigBuilder {
301
+ private config: PartialSDKConfig = { ...DEFAULT_CONFIG };
302
+
303
+ /**
304
+ * Sets the WebSocket URL for connecting to the Teneo network.
305
+ * URL must start with 'ws://' or 'wss://'. HTTPS (wss://) is recommended for production.
306
+ *
307
+ * @param url - WebSocket URL (e.g., 'wss://teneo.example.com')
308
+ * @returns this builder for method chaining
309
+ * @throws {z.ZodError} If URL is invalid or doesn't start with ws:// or wss://
310
+ *
311
+ * @example
312
+ * ```typescript
313
+ * builder.withWebSocketUrl('wss://teneo.example.com')
314
+ * ```
315
+ */
316
+ withWebSocketUrl(url: string): this {
317
+ const parsed = z
318
+ .string()
319
+ .url()
320
+ .refine((u) => u.startsWith("ws://") || u.startsWith("wss://"), {
321
+ message: "WebSocket URL must start with ws:// or wss://"
322
+ })
323
+ .parse(url);
324
+ this.config.wsUrl = parsed;
325
+ return this;
326
+ }
327
+
328
+ /**
329
+ * Configures Ethereum wallet-based authentication credentials.
330
+ * Private key is used to sign authentication challenges from the server.
331
+ * Wallet address is optional and will be derived from the private key if not provided.
332
+ *
333
+ * For enhanced security (SEC-3), you can pass a SecurePrivateKey instance to keep
334
+ * the private key encrypted in memory from the start.
335
+ *
336
+ * @param privateKey - Ethereum private key (hex string starting with 0x) or SecurePrivateKey instance
337
+ * @param walletAddress - Optional wallet address (will be derived if not provided)
338
+ * @returns this builder for method chaining
339
+ * @throws {z.ZodError} If privateKey or walletAddress is invalid
340
+ *
341
+ * @example
342
+ * ```typescript
343
+ * // With private key string only (address derived automatically)
344
+ * builder.withAuthentication('0x...')
345
+ *
346
+ * // With explicit wallet address
347
+ * builder.withAuthentication('0x...privatekey', '0x...address')
348
+ *
349
+ * // With SecurePrivateKey for enhanced security (SEC-3)
350
+ * const secureKey = new SecurePrivateKey('0x...');
351
+ * builder.withAuthentication(secureKey, '0x...address')
352
+ * ```
353
+ */
354
+ withAuthentication(privateKey: string | SecurePrivateKey, walletAddress?: string): this {
355
+ this.config.privateKey = PrivateKeySchema.parse(privateKey);
356
+ if (walletAddress) {
357
+ this.config.walletAddress = z.string().parse(walletAddress);
358
+ }
359
+ return this;
360
+ }
361
+
362
+ /**
363
+ * Configures webhook URL and optional HTTP headers for receiving real-time event notifications.
364
+ * Webhook URL must use HTTPS for non-localhost endpoints (security requirement).
365
+ * Events are sent via HTTP POST requests with JSON payloads.
366
+ *
367
+ * @param url - Webhook endpoint URL (must be HTTPS unless localhost)
368
+ * @param headers - Optional HTTP headers to include with webhook requests (e.g., Authorization)
369
+ * @returns this builder for method chaining
370
+ * @throws {z.ZodError} If URL is invalid
371
+ *
372
+ * @example
373
+ * ```typescript
374
+ * // Basic webhook
375
+ * builder.withWebhook('https://api.example.com/webhooks/teneo')
376
+ *
377
+ * // With authentication headers
378
+ * builder.withWebhook('https://api.example.com/webhooks', {
379
+ * 'Authorization': 'Bearer your-token',
380
+ * 'X-Custom-Header': 'value'
381
+ * })
382
+ * ```
383
+ */
384
+ withWebhook(url: string, headers?: Record<string, string>): this {
385
+ this.config.webhookUrl = z.string().url().parse(url);
386
+ if (headers) {
387
+ this.config.webhookHeaders = z.record(z.string()).parse(headers);
388
+ }
389
+ return this;
390
+ }
391
+
392
+ /**
393
+ * Configures rooms to automatically subscribe to after authentication.
394
+ * These rooms will be subscribed to automatically when connection is established.
395
+ *
396
+ * @param rooms - Array of room IDs to auto-subscribe to on connection
397
+ * @returns this builder for method chaining
398
+ * @throws {z.ZodError} If room IDs are invalid
399
+ *
400
+ * @example
401
+ * ```typescript
402
+ * builder.withAutoJoinRooms(['general', 'announcements', 'support'])
403
+ * ```
404
+ */
405
+ withAutoJoinRooms(rooms: string[]): this {
406
+ this.config.autoJoinRooms = z.array(z.string()).parse(rooms);
407
+ return this;
408
+ }
409
+
410
+ /**
411
+ * Configures automatic reconnection behavior for WebSocket connections.
412
+ * When enabled, SDK will automatically attempt to reconnect on disconnection.
413
+ * Uses exponential backoff strategy for reconnection attempts.
414
+ *
415
+ * @param optionsOrEnabled - Reconnection configuration options object, or boolean for backwards compatibility
416
+ * @param delay - (Deprecated positional arg) Delay between reconnection attempts in ms
417
+ * @param maxAttempts - (Deprecated positional arg) Maximum reconnection attempts
418
+ * @returns this builder for method chaining
419
+ * @throws {z.ZodError} If options are out of valid range
420
+ *
421
+ * @example
422
+ * ```typescript
423
+ * // New API with object (recommended)
424
+ * builder.withReconnection({ enabled: true })
425
+ * builder.withReconnection({
426
+ * enabled: true,
427
+ * delay: 3000,
428
+ * maxAttempts: 5
429
+ * })
430
+ *
431
+ * // Old API with positional args (backwards compatible)
432
+ * builder.withReconnection(true, 3000, 5)
433
+ * ```
434
+ */
435
+ withReconnection(
436
+ optionsOrEnabled: { enabled?: boolean; delay?: number; maxAttempts?: number } | boolean,
437
+ delay?: number,
438
+ maxAttempts?: number
439
+ ): this {
440
+ // Handle backwards compatible positional arguments
441
+ if (typeof optionsOrEnabled === "boolean") {
442
+ this.config.reconnect = z.boolean().parse(optionsOrEnabled);
443
+ if (delay !== undefined) {
444
+ this.config.reconnectDelay = z.number().min(100).max(60000).parse(delay);
445
+ }
446
+ if (maxAttempts !== undefined) {
447
+ this.config.maxReconnectAttempts = z.number().min(0).max(100).parse(maxAttempts);
448
+ }
449
+ } else {
450
+ // New object API
451
+ const options = optionsOrEnabled;
452
+ if (options.enabled !== undefined) {
453
+ this.config.reconnect = z.boolean().parse(options.enabled);
454
+ }
455
+ if (options.delay !== undefined) {
456
+ this.config.reconnectDelay = z.number().min(100).max(60000).parse(options.delay);
457
+ }
458
+ if (options.maxAttempts !== undefined) {
459
+ this.config.maxReconnectAttempts = z.number().min(0).max(100).parse(options.maxAttempts);
460
+ }
461
+ }
462
+ return this;
463
+ }
464
+
465
+ /**
466
+ * Configures how agent responses are formatted when received.
467
+ * Choose between raw JSON, human-readable text, or both formats.
468
+ * Metadata includes timestamps, agent info, and other contextual data.
469
+ *
470
+ * @param optionsOrFormat - Response format configuration object, or format string for backwards compatibility
471
+ * @param includeMetadata - (Deprecated positional arg) Include metadata in responses
472
+ * @returns this builder for method chaining
473
+ * @throws {z.ZodError} If format is invalid
474
+ *
475
+ * @example
476
+ * ```typescript
477
+ * // New API with object (recommended)
478
+ * builder.withResponseFormat({ format: 'humanized' })
479
+ * builder.withResponseFormat({
480
+ * format: 'both',
481
+ * includeMetadata: true
482
+ * })
483
+ *
484
+ * // Old API with positional args (backwards compatible)
485
+ * builder.withResponseFormat('humanized', true)
486
+ * ```
487
+ */
488
+ withResponseFormat(
489
+ optionsOrFormat: { format?: ResponseFormat; includeMetadata?: boolean } | ResponseFormat,
490
+ includeMetadata?: boolean
491
+ ): this {
492
+ // Handle backwards compatible positional arguments
493
+ if (typeof optionsOrFormat === "string") {
494
+ this.config.responseFormat = ResponseFormatSchema.parse(optionsOrFormat);
495
+ if (includeMetadata !== undefined) {
496
+ this.config.includeMetadata = z.boolean().parse(includeMetadata);
497
+ }
498
+ } else {
499
+ // New object API
500
+ const options = optionsOrFormat;
501
+ if (options.format !== undefined) {
502
+ this.config.responseFormat = ResponseFormatSchema.parse(options.format);
503
+ }
504
+ if (options.includeMetadata !== undefined) {
505
+ this.config.includeMetadata = z.boolean().parse(options.includeMetadata);
506
+ }
507
+ }
508
+ return this;
509
+ }
510
+
511
+ /**
512
+ * Configures logging level and optionally provides a custom logger implementation.
513
+ * Default logger uses pino with pretty printing in development and JSON in production.
514
+ * Custom logger must implement the Logger interface (debug, info, warn, error methods).
515
+ *
516
+ * @param level - Log level: 'debug', 'info', 'warn', 'error', or 'silent' (default: 'info')
517
+ * @param logger - Optional custom logger implementation
518
+ * @returns this builder for method chaining
519
+ * @throws {z.ZodError} If level is invalid or logger doesn't implement required interface
520
+ *
521
+ * @example
522
+ * ```typescript
523
+ * // Set log level only (uses default pino logger)
524
+ * builder.withLogging('debug')
525
+ *
526
+ * // With custom logger
527
+ * const customLogger = {
528
+ * debug: (msg, data) => console.debug(msg, data),
529
+ * info: (msg, data) => console.info(msg, data),
530
+ * warn: (msg, data) => console.warn(msg, data),
531
+ * error: (msg, data) => console.error(msg, data)
532
+ * };
533
+ * builder.withLogging('info', customLogger)
534
+ *
535
+ * // Silent mode (no logs)
536
+ * builder.withLogging('silent')
537
+ * ```
538
+ */
539
+ withLogging(level: LogLevel, logger?: Logger): this {
540
+ this.config.logLevel = LogLevelSchema.parse(level);
541
+ if (logger) {
542
+ this.config.logger = LoggerSchema.parse(logger);
543
+ }
544
+ return this;
545
+ }
546
+
547
+ /**
548
+ * Configures agent caching for improved performance.
549
+ * Cache stores agent information to reduce lookup overhead.
550
+ * Automatically invalidates stale entries based on timeout.
551
+ *
552
+ * @param enabled - Enable/disable agent caching (default: true)
553
+ * @param timeout - Cache entry timeout in ms (default: 300000 / 5 minutes, range: 1000-3600000)
554
+ * @param maxSize - Maximum cache size (default: 100, range: 1-10000)
555
+ * @returns this builder for method chaining
556
+ * @throws {z.ZodError} If timeout or maxSize are out of valid range
557
+ *
558
+ * @example
559
+ * ```typescript
560
+ * // Enable with defaults
561
+ * builder.withCache(true)
562
+ *
563
+ * // Custom cache settings
564
+ * builder.withCache(true, 600000, 500) // 10 minutes, 500 entries
565
+ *
566
+ * // Disable caching
567
+ * builder.withCache(false)
568
+ * ```
569
+ */
570
+ withCache(enabled: boolean, timeout?: number, maxSize?: number): this {
571
+ this.config.enableCache = z.boolean().parse(enabled);
572
+ if (timeout !== undefined) {
573
+ this.config.cacheTimeout = z.number().min(1000).max(3600000).parse(timeout);
574
+ }
575
+ if (maxSize !== undefined) {
576
+ this.config.maxCacheSize = z.number().min(1).max(10000).parse(maxSize);
577
+ }
578
+ return this;
579
+ }
580
+
581
+ /**
582
+ * Configures message signature verification for security (SEC-2).
583
+ * Verifies Ethereum ECDSA signatures on incoming messages to prevent spoofing attacks.
584
+ * Disabled by default for backwards compatibility.
585
+ *
586
+ * @param options - Signature verification configuration
587
+ * @param options.enabled - Enable/disable signature verification (default: false)
588
+ * @param options.trustedAddresses - Whitelist of trusted agent addresses (empty = allow all)
589
+ * @param options.requireFor - Message types that require signatures (default: ['task_response', 'agent_selected'])
590
+ * @param options.strictMode - Reject all unsigned messages vs just critical ones (default: false)
591
+ * @returns this builder for method chaining
592
+ * @throws {z.ZodError} If options are invalid
593
+ *
594
+ * @example
595
+ * ```typescript
596
+ * // Enable with defaults (verify but allow unsigned non-critical messages)
597
+ * builder.withSignatureVerification({ enabled: true })
598
+ *
599
+ * // Enable with trusted address whitelist
600
+ * builder.withSignatureVerification({
601
+ * enabled: true,
602
+ * trustedAddresses: ['0xAgent1...', '0xAgent2...']
603
+ * })
604
+ *
605
+ * // Strict mode (reject all unsigned messages)
606
+ * builder.withSignatureVerification({
607
+ * enabled: true,
608
+ * strictMode: true,
609
+ * requireFor: ['task_response', 'agent_selected', 'message']
610
+ * })
611
+ * ```
612
+ */
613
+ withSignatureVerification(options: {
614
+ enabled?: boolean;
615
+ trustedAddresses?: string[];
616
+ requireFor?: MessageType[];
617
+ strictMode?: boolean;
618
+ }): this {
619
+ const { enabled, trustedAddresses, requireFor, strictMode } = options;
620
+
621
+ if (enabled !== undefined) {
622
+ this.config.validateSignatures = z.boolean().parse(enabled);
623
+ }
624
+ if (trustedAddresses !== undefined) {
625
+ this.config.trustedAgentAddresses = z.array(z.string()).parse(trustedAddresses);
626
+ }
627
+ if (requireFor !== undefined) {
628
+ this.config.requireSignaturesFor = z.array(MessageTypeSchema).parse(requireFor);
629
+ }
630
+ if (strictMode !== undefined) {
631
+ this.config.strictSignatureValidation = z.boolean().parse(strictMode);
632
+ }
633
+
634
+ return this;
635
+ }
636
+
637
+ /**
638
+ * Configures WebSocket reconnection retry strategy (REL-3).
639
+ * Allows full control over retry behavior: exponential, linear, or constant backoff.
640
+ * If not specified, uses exponential backoff with default parameters for backward compatibility.
641
+ *
642
+ * @param strategy - Partial retry strategy configuration (unspecified fields use defaults)
643
+ * @returns this builder for method chaining
644
+ * @throws {z.ZodError} If strategy parameters are invalid
645
+ *
646
+ * @example
647
+ * ```typescript
648
+ * // Exponential backoff with aggressive multiplier
649
+ * builder.withReconnectionStrategy({
650
+ * type: 'exponential',
651
+ * baseDelay: 3000,
652
+ * maxDelay: 120000,
653
+ * maxAttempts: 20,
654
+ * jitter: true,
655
+ * backoffMultiplier: 3
656
+ * })
657
+ *
658
+ * // Linear backoff for predictable delays
659
+ * builder.withReconnectionStrategy({
660
+ * type: 'linear',
661
+ * baseDelay: 5000,
662
+ * maxDelay: 60000,
663
+ * maxAttempts: 10,
664
+ * jitter: false
665
+ * })
666
+ *
667
+ * // Constant delay (useful for testing)
668
+ * builder.withReconnectionStrategy({
669
+ * type: 'constant',
670
+ * baseDelay: 10000,
671
+ * maxDelay: 10000,
672
+ * maxAttempts: 5,
673
+ * jitter: false
674
+ * })
675
+ * ```
676
+ */
677
+ withReconnectionStrategy(strategy: Partial<RetryStrategy>): this {
678
+ // Merge with defaults to allow partial strategy specification
679
+ const fullStrategy: RetryStrategy = {
680
+ type: strategy.type || "exponential",
681
+ baseDelay: strategy.baseDelay !== undefined ? strategy.baseDelay : 5000,
682
+ maxDelay: strategy.maxDelay !== undefined ? strategy.maxDelay : 60000,
683
+ maxAttempts: strategy.maxAttempts !== undefined ? strategy.maxAttempts : 10,
684
+ jitter: strategy.jitter !== undefined ? strategy.jitter : true,
685
+ backoffMultiplier: strategy.backoffMultiplier
686
+ };
687
+
688
+ // Validate with schema
689
+ this.config.reconnectStrategy = RetryStrategySchema.parse(fullStrategy);
690
+ return this;
691
+ }
692
+
693
+ /**
694
+ * Configures webhook delivery retry strategy (REL-3).
695
+ * Allows full control over retry behavior: exponential, linear, or constant backoff.
696
+ * If not specified, uses exponential backoff with default parameters for backward compatibility.
697
+ *
698
+ * @param strategy - Partial retry strategy configuration (unspecified fields use defaults)
699
+ * @returns this builder for method chaining
700
+ * @throws {z.ZodError} If strategy parameters are invalid
701
+ *
702
+ * @example
703
+ * ```typescript
704
+ * // Exponential backoff without jitter
705
+ * builder.withWebhookRetryStrategy({
706
+ * type: 'exponential',
707
+ * baseDelay: 1000,
708
+ * maxDelay: 30000,
709
+ * maxAttempts: 5,
710
+ * jitter: false,
711
+ * backoffMultiplier: 2
712
+ * })
713
+ *
714
+ * // Linear backoff with jitter to spread load
715
+ * builder.withWebhookRetryStrategy({
716
+ * type: 'linear',
717
+ * baseDelay: 2000,
718
+ * maxDelay: 20000,
719
+ * maxAttempts: 3,
720
+ * jitter: true
721
+ * })
722
+ * ```
723
+ */
724
+ withWebhookRetryStrategy(strategy: Partial<RetryStrategy>): this {
725
+ // Merge with defaults to allow partial strategy specification
726
+ const fullStrategy: RetryStrategy = {
727
+ type: strategy.type || "exponential",
728
+ baseDelay: strategy.baseDelay !== undefined ? strategy.baseDelay : 1000,
729
+ maxDelay: strategy.maxDelay !== undefined ? strategy.maxDelay : 30000,
730
+ maxAttempts: strategy.maxAttempts !== undefined ? strategy.maxAttempts : 3,
731
+ jitter: strategy.jitter !== undefined ? strategy.jitter : false,
732
+ backoffMultiplier: strategy.backoffMultiplier
733
+ };
734
+
735
+ // Validate with schema
736
+ this.config.webhookRetryStrategy = RetryStrategySchema.parse(fullStrategy);
737
+ return this;
738
+ }
739
+
740
+ /**
741
+ * Configures message deduplication to prevent duplicate processing (CB-4).
742
+ * Uses TTL-based cache to track recently processed message IDs.
743
+ * Automatically expires entries to prevent unbounded memory growth.
744
+ * Enabled by default with sensible limits for most use cases.
745
+ *
746
+ * @param enabled - Enable/disable message deduplication (default: true)
747
+ * @param ttl - How long to remember message IDs in milliseconds (default: 60000 / 1 minute, range: 1000-3600000)
748
+ * @param maxSize - Maximum cache size (default: 10000, range: 1-100000)
749
+ * @returns this builder for method chaining
750
+ * @throws {z.ZodError} If ttl or maxSize are out of valid range
751
+ *
752
+ * @example
753
+ * ```typescript
754
+ * // Enable with defaults (60s TTL, 10k cache)
755
+ * builder.withMessageDeduplication(true)
756
+ *
757
+ * // Custom settings for high-volume scenarios
758
+ * builder.withMessageDeduplication(true, 120000, 50000) // 2 minutes, 50k entries
759
+ *
760
+ * // Disable deduplication (not recommended for production)
761
+ * builder.withMessageDeduplication(false)
762
+ * ```
763
+ */
764
+ withMessageDeduplication(enabled: boolean, ttl?: number, maxSize?: number): this {
765
+ this.config.enableMessageDeduplication = z.boolean().parse(enabled);
766
+ if (ttl !== undefined) {
767
+ this.config.messageDedupeTtl = z.number().min(1000).max(3600000).parse(ttl);
768
+ }
769
+ if (maxSize !== undefined) {
770
+ this.config.messageDedupMaxSize = z.number().min(1).max(100000).parse(maxSize);
771
+ }
772
+ return this;
773
+ }
774
+
775
+ /**
776
+ * Builds and validates the final SDK configuration.
777
+ * Performs comprehensive validation including custom refinements (e.g., webhook security).
778
+ * Must be called after setting all desired configuration options.
779
+ *
780
+ * @returns Validated SDK configuration ready to pass to TeneoSDK constructor
781
+ * @throws {Error} If configuration is invalid or fails validation
782
+ * @throws {z.ZodError} If required fields are missing or values are out of range
783
+ *
784
+ * @example
785
+ * ```typescript
786
+ * const config = new SDKConfigBuilder()
787
+ * .withWebSocketUrl('wss://teneo.example.com')
788
+ * .withAuthentication('0x...')
789
+ * .withAutoJoinRooms(['general'])
790
+ * .build(); // Validates and returns final config
791
+ *
792
+ * const sdk = new TeneoSDK(config);
793
+ * ```
794
+ */
795
+ build(): SDKConfig {
796
+ // Validate and return complete config
797
+ return validateConfig(this.config);
798
+ }
799
+ }