@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,570 @@
1
+ /**
2
+ * Core message schemas for Teneo Protocol SDK using Zod
3
+ * Provides runtime validation and TypeScript type inference
4
+ */
5
+
6
+ import { z } from "zod";
7
+
8
+ /**
9
+ * Coerces string booleans to actual booleans with strict validation.
10
+ * The server sometimes sends "true"/"false" as strings instead of actual booleans.
11
+ *
12
+ * Accepts:
13
+ * - Booleans: true, false
14
+ * - Truthy strings (case-insensitive): "true", "1", "yes"
15
+ * - Falsy strings (case-insensitive): "false", "0", "no"
16
+ *
17
+ * Rejects: Any other string value (throws validation error)
18
+ *
19
+ * @throws {ZodError} If string value is not a recognized boolean representation
20
+ */
21
+ const stringToBoolean = z
22
+ .union([
23
+ z.boolean(),
24
+ z.string().transform((val, ctx) => {
25
+ const normalized = val.toLowerCase().trim();
26
+
27
+ // Accept truthy values
28
+ if (normalized === "true" || normalized === "1" || normalized === "yes") {
29
+ return true;
30
+ }
31
+
32
+ // Accept falsy values
33
+ if (normalized === "false" || normalized === "0" || normalized === "no") {
34
+ return false;
35
+ }
36
+
37
+ // Reject invalid values
38
+ ctx.addIssue({
39
+ code: z.ZodIssueCode.custom,
40
+ message: `Invalid boolean value: "${val}". Expected: true/false, yes/no, 1/0 (case-insensitive)`
41
+ });
42
+ return z.NEVER;
43
+ })
44
+ ])
45
+ .pipe(z.boolean());
46
+
47
+ // Enum schemas
48
+ export const MessageTypeSchema = z.enum([
49
+ "request_challenge",
50
+ "challenge",
51
+ "check_cached_auth",
52
+ "auth",
53
+ "auth_required",
54
+ "auth_success",
55
+ "auth_error",
56
+ "register",
57
+ "registration_success",
58
+ "message",
59
+ "task",
60
+ "task_response",
61
+ "agent_selected",
62
+ "agents",
63
+ "error",
64
+ "ping",
65
+ "pong",
66
+ "capabilities",
67
+ "subscribe",
68
+ "unsubscribe",
69
+ "list_rooms"
70
+ ]);
71
+
72
+ export const ContentTypeSchema = z.enum([
73
+ "text/plain",
74
+ "text/markdown",
75
+ "text/html",
76
+ "application/json",
77
+ "image/*",
78
+ "STRING",
79
+ "JSON",
80
+ "MD",
81
+ "ARRAY"
82
+ ]);
83
+
84
+ export const ClientTypeSchema = z.enum(["user", "agent", "coordinator"]);
85
+
86
+ export const AgentTypeSchema = z.enum(["command", "nlp", "mcp"]);
87
+
88
+ export const AgentStatusSchema = z.enum(["online", "offline"]);
89
+
90
+ // Supporting schemas
91
+ export const CapabilitySchema = z.object({
92
+ name: z.string(),
93
+ description: z.string()
94
+ });
95
+
96
+ export const CommandSchema = z.object({
97
+ trigger: z.string(),
98
+ argument: z.string().optional(),
99
+ description: z.string()
100
+ });
101
+
102
+ export const RoomSchema = z.object({
103
+ id: z.string(),
104
+ name: z.string(),
105
+ description: z.string().optional(),
106
+ is_public: stringToBoolean,
107
+ is_active: stringToBoolean,
108
+ created_by: z.string(),
109
+ created_at: z.string(),
110
+ updated_at: z.string()
111
+ });
112
+
113
+ export const AgentSchema = z.object({
114
+ id: z.string(),
115
+ name: z.string(),
116
+ description: z.string().optional(),
117
+ room: z.string().optional(),
118
+ capabilities: z.array(CapabilitySchema).optional(),
119
+ commands: z.array(CommandSchema).optional(),
120
+ status: AgentStatusSchema,
121
+ image: z.string().optional(),
122
+ agentType: AgentTypeSchema.optional(),
123
+ nlpFallback: stringToBoolean.optional(),
124
+ webhookUrl: z.string().url().optional()
125
+ });
126
+
127
+ // Base message schema
128
+ export const BaseMessageSchema = z.object({
129
+ type: MessageTypeSchema,
130
+ content: z.any().optional(),
131
+ content_type: ContentTypeSchema.optional(),
132
+ from: z.string().optional(),
133
+ to: z.string().optional(),
134
+ room: z.string().optional(),
135
+ timestamp: z.string().optional(),
136
+ data: z.record(z.any()).optional(),
137
+ signature: z.string().optional(),
138
+ publicKey: z.string().optional(),
139
+ reasoning: z.string().optional(),
140
+ task_id: z.string().optional(),
141
+ id: z.string().optional() // Added for message tracking
142
+ });
143
+
144
+ // Authentication message schemas
145
+ export const RequestChallengeMessageSchema = BaseMessageSchema.extend({
146
+ type: z.literal("request_challenge"),
147
+ data: z.object({
148
+ userType: ClientTypeSchema,
149
+ address: z.string().optional()
150
+ })
151
+ });
152
+
153
+ export const ChallengeMessageSchema = BaseMessageSchema.extend({
154
+ type: z.literal("challenge"),
155
+ data: z.object({
156
+ challenge: z.string(),
157
+ timestamp: z.number()
158
+ })
159
+ });
160
+
161
+ export const CheckCachedAuthMessageSchema = BaseMessageSchema.extend({
162
+ type: z.literal("check_cached_auth"),
163
+ data: z.object({
164
+ address: z.string()
165
+ })
166
+ });
167
+
168
+ export const AuthRequiredMessageSchema = BaseMessageSchema.extend({
169
+ type: z.literal("auth_required"),
170
+ content: z.string().optional(),
171
+ from: z.literal("system").optional(),
172
+ data: z
173
+ .object({
174
+ cache_ttl_hours: z.number().optional(),
175
+ supported_auth_methods: z.array(z.string()).optional(),
176
+ supports_cache_check: z.boolean().optional()
177
+ })
178
+ .optional()
179
+ });
180
+
181
+ export const AuthMessageSchema = BaseMessageSchema.extend({
182
+ type: z.literal("auth"),
183
+ data: z
184
+ .object({
185
+ address: z.string().optional(),
186
+ signature: z.string().optional(),
187
+ message: z.string().optional(),
188
+ userType: ClientTypeSchema.optional(),
189
+ agentName: z.string().optional(),
190
+ id: z.string().optional(),
191
+ type: ClientTypeSchema.optional(),
192
+ nft_verified: stringToBoolean.optional(),
193
+ is_whitelisted: stringToBoolean.optional(),
194
+ is_admin_whitelisted: stringToBoolean.optional(),
195
+ rooms: z.array(RoomSchema).optional(),
196
+ private_room_id: z.string().optional(),
197
+ cached_auth: stringToBoolean.optional()
198
+ })
199
+ .optional()
200
+ });
201
+
202
+ export const AuthSuccessMessageSchema = BaseMessageSchema.extend({
203
+ type: z.literal("auth_success"),
204
+ data: z.object({
205
+ id: z.string(),
206
+ type: ClientTypeSchema,
207
+ address: z.string(),
208
+ nft_verified: stringToBoolean.optional(),
209
+ is_whitelisted: stringToBoolean.optional(),
210
+ is_admin_whitelisted: stringToBoolean.optional(),
211
+ rooms: z.array(RoomSchema).optional(),
212
+ private_room_id: z.string().optional(),
213
+ cached_auth: stringToBoolean.optional()
214
+ })
215
+ });
216
+
217
+ export const AuthErrorMessageSchema = BaseMessageSchema.extend({
218
+ type: z.literal("auth_error"),
219
+ data: z.object({
220
+ error: z.string(),
221
+ code: z.number().optional()
222
+ })
223
+ });
224
+
225
+ // Registration message schemas
226
+ export const RegisterMessageSchema = BaseMessageSchema.extend({
227
+ type: z.literal("register"),
228
+ data: z.object({
229
+ name: z.string().optional(),
230
+ userType: ClientTypeSchema,
231
+ room: z.string(),
232
+ capabilities: z.array(CapabilitySchema).optional(),
233
+ commands: z.array(CommandSchema).optional(),
234
+ nft_token_id: z.string().optional(),
235
+ wallet_address: z.string().optional(),
236
+ challenge: z.string().optional(),
237
+ challenge_response: z.string().optional()
238
+ })
239
+ });
240
+
241
+ export const RegistrationSuccessMessageSchema = BaseMessageSchema.extend({
242
+ type: z.literal("registration_success"),
243
+ data: z.object({
244
+ agent_id: z.string(),
245
+ name: z.string(),
246
+ room: z.string()
247
+ })
248
+ });
249
+
250
+ // Communication message schemas
251
+ export const UserMessageSchema = BaseMessageSchema.extend({
252
+ type: z.literal("message"),
253
+ content: z.string(),
254
+ room: z.string().optional()
255
+ });
256
+
257
+ export const TaskMessageSchema = BaseMessageSchema.extend({
258
+ type: z.literal("task"),
259
+ content: z.string(),
260
+ from: z.literal("coordinator"),
261
+ to: z.string(),
262
+ room: z.string(),
263
+ data: z.object({
264
+ task_id: z.string(),
265
+ user_prompt: z.string(),
266
+ requesting_user_id: z.string(),
267
+ room_id: z.string()
268
+ })
269
+ });
270
+
271
+ export const TaskResponseMessageSchema = BaseMessageSchema.extend({
272
+ type: z.literal("task_response"),
273
+ content: z.string(),
274
+ content_type: ContentTypeSchema,
275
+ from: z.string(),
276
+ data: z.object({
277
+ task_id: z.string(),
278
+ agent_name: z.string().optional(),
279
+ success: stringToBoolean.optional(),
280
+ error: z.string().optional()
281
+ })
282
+ });
283
+
284
+ export const AgentSelectedMessageSchema = BaseMessageSchema.extend({
285
+ type: z.literal("agent_selected"),
286
+ content: z.string(),
287
+ from: z.literal("coordinator"),
288
+ reasoning: z.string(),
289
+ data: z.object({
290
+ agent_id: z.string(),
291
+ agent_name: z.string(),
292
+ capabilities: z.array(CapabilitySchema).optional(),
293
+ user_request: z.string(),
294
+ command: z.string().optional(),
295
+ command_reasoning: z.string().optional()
296
+ })
297
+ });
298
+
299
+ // System message schemas
300
+ export const AgentsListMessageSchema = BaseMessageSchema.extend({
301
+ type: z.literal("agents"),
302
+ from: z.literal("system"),
303
+ data: z.array(AgentSchema)
304
+ });
305
+
306
+ export const ErrorMessageSchema = BaseMessageSchema.extend({
307
+ type: z.literal("error"),
308
+ content: z.string(),
309
+ from: z.literal("system"),
310
+ data: z.object({
311
+ code: z.number(),
312
+ message: z.string(),
313
+ details: z.any().optional()
314
+ })
315
+ });
316
+
317
+ export const PingMessageSchema = BaseMessageSchema.extend({
318
+ type: z.literal("ping")
319
+ });
320
+
321
+ export const PongMessageSchema = BaseMessageSchema.extend({
322
+ type: z.literal("pong")
323
+ });
324
+
325
+ // Room subscription schemas
326
+ export const SubscribeMessageSchema = BaseMessageSchema.extend({
327
+ type: z.literal("subscribe"),
328
+ data: z.object({
329
+ room_id: z.string()
330
+ })
331
+ });
332
+
333
+ export const UnsubscribeMessageSchema = BaseMessageSchema.extend({
334
+ type: z.literal("unsubscribe"),
335
+ data: z.object({
336
+ room_id: z.string()
337
+ })
338
+ });
339
+
340
+ export const ListRoomsMessageSchema = BaseMessageSchema.extend({
341
+ type: z.literal("list_rooms")
342
+ });
343
+
344
+ export const SubscribeResponseSchema = BaseMessageSchema.extend({
345
+ type: z.literal("subscribe"),
346
+ data: z.object({
347
+ room_id: z.string(),
348
+ success: z.boolean(),
349
+ message: z.string(),
350
+ subscriptions: z.array(z.string()).optional()
351
+ })
352
+ });
353
+
354
+ export const UnsubscribeResponseSchema = BaseMessageSchema.extend({
355
+ type: z.literal("unsubscribe"),
356
+ data: z.object({
357
+ room_id: z.string(),
358
+ success: z.boolean(),
359
+ message: z.string(),
360
+ subscriptions: z.array(z.string()).optional()
361
+ })
362
+ });
363
+
364
+ export const RoomInfoSchema = z.object({
365
+ id: z.string(),
366
+ name: z.string(),
367
+ description: z.string().optional().nullable(),
368
+ is_public: z.boolean(),
369
+ created_at: z.string(),
370
+ updated_at: z.string(),
371
+ is_owner: z.boolean()
372
+ });
373
+
374
+ export const ListRoomsResponseSchema = BaseMessageSchema.extend({
375
+ type: z.literal("list_rooms"),
376
+ data: z.object({
377
+ rooms: z.array(RoomInfoSchema)
378
+ })
379
+ });
380
+
381
+ // Union of all INCOMING message schemas for validation
382
+ // Note: Outgoing message schemas (Subscribe, Unsubscribe, ListRooms) are excluded
383
+ // as they share the same type values with their response counterparts
384
+ export const AnyMessageSchema = z.discriminatedUnion("type", [
385
+ RequestChallengeMessageSchema,
386
+ ChallengeMessageSchema,
387
+ CheckCachedAuthMessageSchema,
388
+ AuthRequiredMessageSchema,
389
+ AuthMessageSchema,
390
+ AuthSuccessMessageSchema,
391
+ AuthErrorMessageSchema,
392
+ RegisterMessageSchema,
393
+ RegistrationSuccessMessageSchema,
394
+ UserMessageSchema,
395
+ TaskMessageSchema,
396
+ TaskResponseMessageSchema,
397
+ AgentSelectedMessageSchema,
398
+ AgentsListMessageSchema,
399
+ ErrorMessageSchema,
400
+ PingMessageSchema,
401
+ PongMessageSchema,
402
+ // Only response schemas for room operations (not outgoing request schemas)
403
+ SubscribeResponseSchema,
404
+ UnsubscribeResponseSchema,
405
+ ListRoomsResponseSchema
406
+ ]);
407
+
408
+ // Type inference from schemas
409
+ export type MessageType = z.infer<typeof MessageTypeSchema>;
410
+ export type ContentType = z.infer<typeof ContentTypeSchema>;
411
+ export type ClientType = z.infer<typeof ClientTypeSchema>;
412
+ export type AgentType = z.infer<typeof AgentTypeSchema>;
413
+ export type AgentStatus = z.infer<typeof AgentStatusSchema>;
414
+
415
+ export type Capability = z.infer<typeof CapabilitySchema>;
416
+ export type Command = z.infer<typeof CommandSchema>;
417
+ export type Room = z.infer<typeof RoomSchema>;
418
+ export type Agent = z.infer<typeof AgentSchema>;
419
+
420
+ export type BaseMessage = z.infer<typeof BaseMessageSchema>;
421
+ export type RequestChallengeMessage = z.infer<typeof RequestChallengeMessageSchema>;
422
+ export type ChallengeMessage = z.infer<typeof ChallengeMessageSchema>;
423
+ export type CheckCachedAuthMessage = z.infer<typeof CheckCachedAuthMessageSchema>;
424
+ export type AuthRequiredMessage = z.infer<typeof AuthRequiredMessageSchema>;
425
+ export type AuthMessage = z.infer<typeof AuthMessageSchema>;
426
+ export type AuthSuccessMessage = z.infer<typeof AuthSuccessMessageSchema>;
427
+ export type AuthErrorMessage = z.infer<typeof AuthErrorMessageSchema>;
428
+ export type RegisterMessage = z.infer<typeof RegisterMessageSchema>;
429
+ export type RegistrationSuccessMessage = z.infer<typeof RegistrationSuccessMessageSchema>;
430
+ export type UserMessage = z.infer<typeof UserMessageSchema>;
431
+ export type TaskMessage = z.infer<typeof TaskMessageSchema>;
432
+ export type TaskResponseMessage = z.infer<typeof TaskResponseMessageSchema>;
433
+ export type AgentSelectedMessage = z.infer<typeof AgentSelectedMessageSchema>;
434
+ export type AgentsListMessage = z.infer<typeof AgentsListMessageSchema>;
435
+ export type ErrorMessage = z.infer<typeof ErrorMessageSchema>;
436
+ export type PingMessage = z.infer<typeof PingMessageSchema>;
437
+ export type PongMessage = z.infer<typeof PongMessageSchema>;
438
+ export type SubscribeMessage = z.infer<typeof SubscribeMessageSchema>;
439
+ export type UnsubscribeMessage = z.infer<typeof UnsubscribeMessageSchema>;
440
+ export type ListRoomsMessage = z.infer<typeof ListRoomsMessageSchema>;
441
+ export type SubscribeResponse = z.infer<typeof SubscribeResponseSchema>;
442
+ export type UnsubscribeResponse = z.infer<typeof UnsubscribeResponseSchema>;
443
+ export type RoomInfo = z.infer<typeof RoomInfoSchema>;
444
+ export type ListRoomsResponse = z.infer<typeof ListRoomsResponseSchema>;
445
+
446
+ export type AnyMessage = z.infer<typeof AnyMessageSchema>;
447
+
448
+ // Type guards using Zod parse
449
+ export function isAuthSuccess(msg: unknown): msg is AuthSuccessMessage {
450
+ return AuthSuccessMessageSchema.safeParse(msg).success;
451
+ }
452
+
453
+ export function isAuthError(msg: unknown): msg is AuthErrorMessage {
454
+ return AuthErrorMessageSchema.safeParse(msg).success;
455
+ }
456
+
457
+ export function isAuth(msg: unknown): msg is AuthMessage {
458
+ return AuthMessageSchema.safeParse(msg).success;
459
+ }
460
+
461
+ export function isChallenge(msg: unknown): msg is ChallengeMessage {
462
+ return ChallengeMessageSchema.safeParse(msg).success;
463
+ }
464
+
465
+ export function isAgentSelected(msg: unknown): msg is AgentSelectedMessage {
466
+ return AgentSelectedMessageSchema.safeParse(msg).success;
467
+ }
468
+
469
+ export function isTaskResponse(msg: unknown): msg is TaskResponseMessage {
470
+ return TaskResponseMessageSchema.safeParse(msg).success;
471
+ }
472
+
473
+ export function isError(msg: unknown): msg is ErrorMessage {
474
+ return ErrorMessageSchema.safeParse(msg).success;
475
+ }
476
+
477
+ export function isAgentsList(msg: unknown): msg is AgentsListMessage {
478
+ return AgentsListMessageSchema.safeParse(msg).success;
479
+ }
480
+
481
+ // Message factory functions with validation
482
+ export function createRequestChallenge(
483
+ userType: ClientType = "user",
484
+ address?: string
485
+ ): RequestChallengeMessage {
486
+ return RequestChallengeMessageSchema.parse({
487
+ type: "request_challenge",
488
+ data: {
489
+ userType,
490
+ ...(address && { address })
491
+ }
492
+ });
493
+ }
494
+
495
+ export function createCheckCachedAuth(address: string): CheckCachedAuthMessage {
496
+ return CheckCachedAuthMessageSchema.parse({
497
+ type: "check_cached_auth",
498
+ data: { address }
499
+ });
500
+ }
501
+
502
+ export function createAuth(
503
+ address: string,
504
+ signature: string,
505
+ message: string,
506
+ userType: ClientType = "user"
507
+ ): AuthMessage {
508
+ return AuthMessageSchema.parse({
509
+ type: "auth",
510
+ data: {
511
+ address,
512
+ signature,
513
+ message,
514
+ userType
515
+ }
516
+ });
517
+ }
518
+
519
+ export function createUserMessage(content: string, room: string, from?: string): UserMessage {
520
+ return UserMessageSchema.parse({
521
+ type: "message",
522
+ content,
523
+ room,
524
+ ...(from && { from })
525
+ });
526
+ }
527
+
528
+ export function createPing(): PingMessage {
529
+ return PingMessageSchema.parse({
530
+ type: "ping"
531
+ });
532
+ }
533
+
534
+ export function createSubscribe(roomId: string): SubscribeMessage {
535
+ return SubscribeMessageSchema.parse({
536
+ type: "subscribe",
537
+ data: { room_id: roomId }
538
+ });
539
+ }
540
+
541
+ export function createUnsubscribe(roomId: string): UnsubscribeMessage {
542
+ return UnsubscribeMessageSchema.parse({
543
+ type: "unsubscribe",
544
+ data: { room_id: roomId }
545
+ });
546
+ }
547
+
548
+ export function createListRooms(): ListRoomsMessage {
549
+ return ListRoomsMessageSchema.parse({
550
+ type: "list_rooms"
551
+ });
552
+ }
553
+
554
+ // Validation helper
555
+ export function validateMessage(message: unknown): AnyMessage {
556
+ return AnyMessageSchema.parse(message);
557
+ }
558
+
559
+ // Safe parse helper
560
+ export function safeParseMessage(message: unknown): {
561
+ success: boolean;
562
+ data?: AnyMessage;
563
+ error?: z.ZodError;
564
+ } {
565
+ const result = AnyMessageSchema.safeParse(message);
566
+ if (result.success) {
567
+ return { success: true, data: result.data };
568
+ }
569
+ return { success: false, error: result.error };
570
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Validation schemas for user inputs
3
+ * Provides strict validation with length limits, character restrictions, and format requirements
4
+ */
5
+
6
+ import { z } from "zod";
7
+
8
+ /**
9
+ * Validates room IDs with strict constraints.
10
+ * - Must not be empty
11
+ * - Maximum 100 characters
12
+ * - Only alphanumeric characters, spaces, dashes, and underscores allowed
13
+ * - Automatically trims whitespace
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const roomId = RoomIdSchema.parse('general-chat'); // OK
18
+ * const roomId = RoomIdSchema.parse('room_123'); // OK
19
+ * const roomId = RoomIdSchema.parse('Crawler Room'); // OK
20
+ * const roomId = RoomIdSchema.parse('invalid room!'); // Error: invalid characters
21
+ * ```
22
+ */
23
+ export const RoomIdSchema = z
24
+ .string()
25
+ .min(1, "Room ID cannot be empty")
26
+ .max(100, "Room ID must be 100 characters or less")
27
+ .regex(/^[a-zA-Z0-9_\- ]+$/, "Room ID can only contain letters, numbers, spaces, dashes, and underscores")
28
+ .trim();
29
+
30
+ /**
31
+ * Validates agent IDs with strict constraints.
32
+ * - Must not be empty
33
+ * - Maximum 100 characters
34
+ * - Only alphanumeric characters, dashes, and underscores allowed
35
+ * - Automatically trims whitespace
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const agentId = AgentIdSchema.parse('agent-123'); // OK
40
+ * const agentId = AgentIdSchema.parse('my_agent'); // OK
41
+ * const agentId = AgentIdSchema.parse('invalid agent!'); // Error: invalid characters
42
+ * ```
43
+ */
44
+ export const AgentIdSchema = z
45
+ .string()
46
+ .min(1, "Agent ID cannot be empty")
47
+ .max(100, "Agent ID must be 100 characters or less")
48
+ .regex(/^[a-zA-Z0-9_-]+$/, "Agent ID can only contain letters, numbers, dashes, and underscores")
49
+ .trim();
50
+
51
+ /**
52
+ * Validates message content with strict constraints.
53
+ * - Must not be empty
54
+ * - Maximum 10,000 characters to prevent abuse
55
+ * - No control characters (except newline, tab, carriage return)
56
+ * - Automatically trims whitespace
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * const content = MessageContentSchema.parse('Hello, world!'); // OK
61
+ * const content = MessageContentSchema.parse('Multi\nline\nmessage'); // OK
62
+ * const content = MessageContentSchema.parse('\x00Bad content'); // Error: control characters
63
+ * ```
64
+ */
65
+ export const MessageContentSchema = z
66
+ .string()
67
+ .trim()
68
+ .min(1, "Message content cannot be empty")
69
+ .max(10000, "Message content must be 10,000 characters or less")
70
+ .refine(
71
+ // eslint-disable-next-line no-control-regex -- Intentionally checking for control characters to prevent injection
72
+ (str) => !/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/.test(str),
73
+ "Message content cannot contain control characters"
74
+ );
75
+
76
+ /**
77
+ * Validates agent command content (string) with strict constraints.
78
+ * - Must not be empty
79
+ * - Maximum 5,000 characters
80
+ * - No control characters (except newline, tab, carriage return)
81
+ * - Automatically trims whitespace
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * const command = AgentCommandContentSchema.parse('execute task'); // OK
86
+ * const command = AgentCommandContentSchema.parse('multi\nline\ncommand'); // OK
87
+ * const command = AgentCommandContentSchema.parse('\x00Bad'); // Error: control characters
88
+ * ```
89
+ */
90
+ export const AgentCommandContentSchema = z
91
+ .string()
92
+ .trim()
93
+ .min(1, "Agent command cannot be empty")
94
+ .max(5000, "Agent command must be 5,000 characters or less")
95
+ .refine(
96
+ // eslint-disable-next-line no-control-regex -- Intentionally checking for control characters to prevent injection
97
+ (str) => !/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/.test(str),
98
+ "Agent command cannot contain control characters"
99
+ );
100
+
101
+ /**
102
+ * Validates search queries with strict constraints.
103
+ * - Must not be empty
104
+ * - Maximum 200 characters (reasonable for search)
105
+ * - No control characters
106
+ * - Automatically trims whitespace
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const query = SearchQuerySchema.parse('find agents'); // OK
111
+ * const query = SearchQuerySchema.parse('\x00Bad query'); // Error: control characters
112
+ * ```
113
+ */
114
+ export const SearchQuerySchema = z
115
+ .string()
116
+ .trim()
117
+ .min(1, "Search query cannot be empty")
118
+ .max(200, "Search query must be 200 characters or less")
119
+ .refine(
120
+ // eslint-disable-next-line no-control-regex -- Intentionally checking for control characters to prevent injection
121
+ (str) => !/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/.test(str),
122
+ "Search query cannot contain control characters"
123
+ );