@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,677 @@
1
+ /**
2
+ * Response formatter for Teneo Protocol SDK using Zod schemas
3
+ * Provides humanized and raw formatting for agent responses with validation
4
+ */
5
+
6
+ import { z } from "zod";
7
+ import {
8
+ BaseMessage,
9
+ TaskResponseMessage,
10
+ AgentSelectedMessage,
11
+ ErrorMessage,
12
+ Agent,
13
+ ContentTypeSchema,
14
+ TaskResponseMessageSchema,
15
+ AgentSelectedMessageSchema,
16
+ ErrorMessageSchema,
17
+ AgentSchema
18
+ } from "../types";
19
+
20
+ // Format option schema
21
+ export const FormatOptionSchema = z.enum(["raw", "humanized", "both"]);
22
+ export type FormatOption = z.infer<typeof FormatOptionSchema>;
23
+
24
+ // Response format options schema
25
+ export const ResponseFormatOptionsSchema = z.object({
26
+ format: FormatOptionSchema.optional(),
27
+ includeMetadata: z.boolean().optional(),
28
+ includeTimestamps: z.boolean().optional(),
29
+ prettyPrint: z.boolean().optional()
30
+ });
31
+
32
+ export type ResponseFormatOptions = z.infer<typeof ResponseFormatOptionsSchema>;
33
+
34
+ // Response metadata schema
35
+ export const ResponseMetadataSchema = z.object({
36
+ timestamp: z.date(),
37
+ messageType: z.string(),
38
+ agentId: z.string().optional(),
39
+ agentName: z.string().optional(),
40
+ taskId: z.string().optional(),
41
+ success: z.boolean().optional(),
42
+ contentType: ContentTypeSchema.optional(),
43
+ reasoning: z.string().optional()
44
+ });
45
+
46
+ // Formatted response schema
47
+ export const FormattedResponseSchema = z.object({
48
+ raw: z.any().optional(),
49
+ humanized: z.string().optional(),
50
+ metadata: ResponseMetadataSchema.optional()
51
+ });
52
+
53
+ // Type inference
54
+ export type ResponseMetadata = z.infer<typeof ResponseMetadataSchema>;
55
+ export type FormattedResponse = z.infer<typeof FormattedResponseSchema>;
56
+
57
+ export class ResponseFormatter {
58
+ private formatOption: FormatOption;
59
+ private includeMetadata: boolean;
60
+ private includeTimestamps: boolean;
61
+ private prettyPrint: boolean;
62
+
63
+ constructor(options: ResponseFormatOptions = {}) {
64
+ const validatedOptions = ResponseFormatOptionsSchema.parse(options);
65
+
66
+ this.formatOption = validatedOptions.format ?? "humanized";
67
+ this.includeMetadata = validatedOptions.includeMetadata ?? false;
68
+ this.includeTimestamps = validatedOptions.includeTimestamps ?? true;
69
+ this.prettyPrint = validatedOptions.prettyPrint ?? true;
70
+ }
71
+
72
+ /**
73
+ * Formats a message based on current configuration settings.
74
+ * Automatically determines the message type and applies appropriate formatting.
75
+ * Supports raw JSON, humanized text, or both formats simultaneously.
76
+ *
77
+ * @param message - The message to format
78
+ * @returns Formatted response with raw/humanized content and optional metadata
79
+ * @throws {ZodError} If message validation fails
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * const formatter = new ResponseFormatter({ format: 'both', includeMetadata: true });
84
+ * const formatted = formatter.format(message);
85
+ * console.log(formatted.humanized); // Human-readable text
86
+ * console.log(formatted.raw); // Original JSON
87
+ * console.log(formatted.metadata); // Timestamp, agent info, etc.
88
+ * ```
89
+ */
90
+ public format(message: BaseMessage): FormattedResponse {
91
+ const response: FormattedResponse = {};
92
+
93
+ // Add metadata if requested
94
+ if (this.includeMetadata) {
95
+ response.metadata = this.extractMetadata(message);
96
+ }
97
+
98
+ // Format based on option
99
+ if (this.formatOption === "raw" || this.formatOption === "both") {
100
+ response.raw = this.formatRaw(message);
101
+ }
102
+
103
+ if (this.formatOption === "humanized" || this.formatOption === "both") {
104
+ response.humanized = this.formatHumanized(message);
105
+ }
106
+
107
+ // Validate the response
108
+ return FormattedResponseSchema.parse(response);
109
+ }
110
+
111
+ /**
112
+ * Formats a task response message from an agent.
113
+ * Handles different content types (JSON, Markdown, plain text, arrays)
114
+ * and formats them appropriately for human consumption.
115
+ * Includes agent name, success status, reasoning, and formatted content.
116
+ *
117
+ * @param message - The task response message to format
118
+ * @returns Formatted response with task-specific formatting
119
+ * @throws {ZodError} If message validation fails
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * const formatted = formatter.formatTaskResponse(taskMessage);
124
+ * // Example output:
125
+ * // [RESPONSE] From Weather Agent:
126
+ * // Temperature: 72°F
127
+ * // Conditions: Sunny
128
+ * // [REASONING] Used latest weather API data
129
+ * ```
130
+ */
131
+ public formatTaskResponse(message: TaskResponseMessage): FormattedResponse {
132
+ // Validate input
133
+ const validatedMessage = TaskResponseMessageSchema.parse(message);
134
+ const response: FormattedResponse = {};
135
+
136
+ if (this.includeMetadata) {
137
+ response.metadata = ResponseMetadataSchema.parse({
138
+ timestamp: new Date(validatedMessage.timestamp ?? Date.now()),
139
+ messageType: "task_response",
140
+ agentId: validatedMessage.from,
141
+ agentName: validatedMessage.data?.agent_name,
142
+ taskId: validatedMessage.data?.task_id,
143
+ success: validatedMessage.data?.success !== false,
144
+ contentType: validatedMessage.content_type,
145
+ reasoning: validatedMessage.reasoning
146
+ });
147
+ }
148
+
149
+ if (this.formatOption === "raw" || this.formatOption === "both") {
150
+ response.raw = validatedMessage;
151
+ }
152
+
153
+ if (this.formatOption === "humanized" || this.formatOption === "both") {
154
+ response.humanized = this.formatTaskResponseHumanized(validatedMessage);
155
+ }
156
+
157
+ return FormattedResponseSchema.parse(response);
158
+ }
159
+
160
+ /**
161
+ * Formats an agent selection message from the coordinator.
162
+ * Shows which agent was selected, the command being executed,
163
+ * the reasoning behind the selection, and agent capabilities.
164
+ *
165
+ * @param message - The agent selection message to format
166
+ * @returns Formatted response with agent selection details
167
+ * @throws {ZodError} If message validation fails
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * const formatted = formatter.formatAgentSelected(selectionMessage);
172
+ * // Example output:
173
+ * // [AGENT] Coordinator selected Weather Agent
174
+ * // [COMMAND] Get forecast for New York
175
+ * // [REASONING] Agent has weather-forecast capability
176
+ * // [CAPABILITIES]
177
+ * // - weather-forecast: Provides weather forecasts
178
+ * ```
179
+ */
180
+ public formatAgentSelected(message: AgentSelectedMessage): FormattedResponse {
181
+ // Validate input
182
+ const validatedMessage = AgentSelectedMessageSchema.parse(message);
183
+ const response: FormattedResponse = {};
184
+
185
+ if (this.includeMetadata) {
186
+ response.metadata = ResponseMetadataSchema.parse({
187
+ timestamp: new Date(validatedMessage.timestamp ?? Date.now()),
188
+ messageType: "agent_selected",
189
+ agentId: validatedMessage.data?.agent_id,
190
+ agentName: validatedMessage.data?.agent_name,
191
+ reasoning: validatedMessage.reasoning
192
+ });
193
+ }
194
+
195
+ if (this.formatOption === "raw" || this.formatOption === "both") {
196
+ response.raw = validatedMessage;
197
+ }
198
+
199
+ if (this.formatOption === "humanized" || this.formatOption === "both") {
200
+ response.humanized = this.formatAgentSelectedHumanized(validatedMessage);
201
+ }
202
+
203
+ return FormattedResponseSchema.parse(response);
204
+ }
205
+
206
+ /**
207
+ * Formats an error message with error code and details.
208
+ * Displays error message, optional error code, and detailed information
209
+ * in a human-readable format.
210
+ *
211
+ * @param message - The error message to format
212
+ * @returns Formatted response with error information
213
+ * @throws {ZodError} If message validation fails
214
+ *
215
+ * @example
216
+ * ```typescript
217
+ * const formatted = formatter.formatError(errorMessage);
218
+ * // Example output:
219
+ * // [ERROR] Agent not found (Code: AGENT_NOT_FOUND)
220
+ * // Details: {
221
+ * // "requestedAgent": "unknown-agent",
222
+ * // "availableAgents": 5
223
+ * // }
224
+ * ```
225
+ */
226
+ public formatError(message: ErrorMessage): FormattedResponse {
227
+ // Validate input
228
+ const validatedMessage = ErrorMessageSchema.parse(message);
229
+ const response: FormattedResponse = {};
230
+
231
+ if (this.includeMetadata) {
232
+ response.metadata = ResponseMetadataSchema.parse({
233
+ timestamp: new Date(validatedMessage.timestamp ?? Date.now()),
234
+ messageType: "error",
235
+ success: false
236
+ });
237
+ }
238
+
239
+ if (this.formatOption === "raw" || this.formatOption === "both") {
240
+ response.raw = validatedMessage;
241
+ }
242
+
243
+ if (this.formatOption === "humanized" || this.formatOption === "both") {
244
+ response.humanized = this.formatErrorHumanized(validatedMessage);
245
+ }
246
+
247
+ return FormattedResponseSchema.parse(response);
248
+ }
249
+
250
+ /**
251
+ * Formats a list of agents with their details, capabilities, and commands.
252
+ * Displays each agent's name, status, description, room, capabilities, and available commands.
253
+ *
254
+ * @param agents - Array of agents to format
255
+ * @returns Formatted response with agent list
256
+ * @throws {ZodError} If agent validation fails
257
+ *
258
+ * @example
259
+ * ```typescript
260
+ * const formatted = formatter.formatAgentList(agents);
261
+ * // Example output:
262
+ * // [AGENTS] Available Agents (3):
263
+ * //
264
+ * // [AGENT] Weather Agent [ONLINE]
265
+ * // Provides weather forecasts and conditions
266
+ * // Room: general
267
+ * // Capabilities:
268
+ * // - weather-forecast
269
+ * // Commands:
270
+ * // - /weather <location>
271
+ * ```
272
+ */
273
+ public formatAgentList(agents: Agent[]): FormattedResponse {
274
+ // Validate input
275
+ const validatedAgents = z.array(AgentSchema).parse(agents);
276
+ const response: FormattedResponse = {};
277
+
278
+ if (this.includeMetadata) {
279
+ response.metadata = ResponseMetadataSchema.parse({
280
+ timestamp: new Date(),
281
+ messageType: "agents_list"
282
+ });
283
+ }
284
+
285
+ if (this.formatOption === "raw" || this.formatOption === "both") {
286
+ response.raw = validatedAgents;
287
+ }
288
+
289
+ if (this.formatOption === "humanized" || this.formatOption === "both") {
290
+ response.humanized = this.formatAgentListHumanized(validatedAgents);
291
+ }
292
+
293
+ return FormattedResponseSchema.parse(response);
294
+ }
295
+
296
+ /**
297
+ * Format raw message (no transformation)
298
+ */
299
+ private formatRaw(message: BaseMessage): any {
300
+ return message;
301
+ }
302
+
303
+ /**
304
+ * Format message as humanized text
305
+ */
306
+ private formatHumanized(message: BaseMessage): string {
307
+ switch (message.type) {
308
+ case "task_response":
309
+ return this.formatTaskResponseHumanized(message as TaskResponseMessage);
310
+
311
+ case "agent_selected":
312
+ return this.formatAgentSelectedHumanized(message as AgentSelectedMessage);
313
+
314
+ case "error":
315
+ return this.formatErrorHumanized(message as ErrorMessage);
316
+
317
+ case "message":
318
+ return this.formatMessageHumanized(message);
319
+
320
+ default:
321
+ return message.content || JSON.stringify(message.data || {});
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Format task response as humanized text
327
+ */
328
+ private formatTaskResponseHumanized(message: TaskResponseMessage): string {
329
+ const agentName = message.data?.agent_name || message.from || "Agent";
330
+ const success = message.data?.success !== false;
331
+
332
+ let result = `[RESPONSE] From ${agentName}:\n`;
333
+
334
+ if (!success && message.data?.error) {
335
+ result += `[ERROR] ${message.data.error}\n`;
336
+ }
337
+
338
+ // Format content based on content type
339
+ if (message.content_type === "application/json" || message.content_type === "JSON") {
340
+ try {
341
+ const json =
342
+ typeof message.content === "string" ? JSON.parse(message.content) : message.content;
343
+ result += this.formatJSON(json);
344
+ } catch {
345
+ result += message.content;
346
+ }
347
+ } else if (message.content_type === "text/markdown" || message.content_type === "MD") {
348
+ result += message.content;
349
+ } else if (message.content_type === "ARRAY") {
350
+ try {
351
+ const array =
352
+ typeof message.content === "string" ? JSON.parse(message.content) : message.content;
353
+ result += this.formatArray(array);
354
+ } catch {
355
+ result += message.content;
356
+ }
357
+ } else {
358
+ result += message.content;
359
+ }
360
+
361
+ if (message.reasoning) {
362
+ result += `\n[REASONING] ${message.reasoning}`;
363
+ }
364
+
365
+ return result;
366
+ }
367
+
368
+ /**
369
+ * Format agent selection as humanized text
370
+ */
371
+ private formatAgentSelectedHumanized(message: AgentSelectedMessage): string {
372
+ let result = `[AGENT] Coordinator selected ${message.data?.agent_name}`;
373
+
374
+ if (message.data?.command) {
375
+ result += `\n[COMMAND] ${message.data.command}`;
376
+ }
377
+
378
+ if (message.reasoning) {
379
+ result += `\n[REASONING] ${message.reasoning}`;
380
+ }
381
+
382
+ if (message.data?.capabilities && message.data.capabilities.length > 0) {
383
+ result += "\n[CAPABILITIES]";
384
+ for (const cap of message.data.capabilities) {
385
+ result += `\n - ${cap.name}: ${cap.description}`;
386
+ }
387
+ }
388
+
389
+ return result;
390
+ }
391
+
392
+ /**
393
+ * Format error as humanized text
394
+ */
395
+ private formatErrorHumanized(message: ErrorMessage): string {
396
+ let result = `[ERROR] ${message.content || message.data?.message || "Unknown error"}`;
397
+
398
+ if (message.data?.code) {
399
+ result += ` (Code: ${message.data.code})`;
400
+ }
401
+
402
+ if (message.data?.details) {
403
+ result += `\nDetails: ${JSON.stringify(message.data.details, null, 2)}`;
404
+ }
405
+
406
+ return result;
407
+ }
408
+
409
+ /**
410
+ * Format regular message as humanized text
411
+ */
412
+ private formatMessageHumanized(message: BaseMessage): string {
413
+ const from = message.from || "Unknown";
414
+ let result = `[MESSAGE] ${from}: ${message.content}`;
415
+
416
+ if (message.room) {
417
+ result = `[${message.room}] ${result}`;
418
+ }
419
+
420
+ return result;
421
+ }
422
+
423
+ /**
424
+ * Format agent list as humanized text
425
+ */
426
+ private formatAgentListHumanized(agents: Agent[]): string {
427
+ if (agents.length === 0) {
428
+ return "[AGENTS] No agents available";
429
+ }
430
+
431
+ let result = `[AGENTS] Available Agents (${agents.length}):\n`;
432
+
433
+ for (const agent of agents) {
434
+ result += `\n[AGENT] ${agent.name}`;
435
+
436
+ if (agent.status) {
437
+ const statusText = agent.status === "online" ? "ONLINE" : "OFFLINE";
438
+ result += ` [${statusText}]`;
439
+ }
440
+
441
+ if (agent.description) {
442
+ result += `\n ${agent.description}`;
443
+ }
444
+
445
+ if (agent.room) {
446
+ result += `\n Room: ${agent.room}`;
447
+ }
448
+
449
+ if (agent.capabilities && agent.capabilities.length > 0) {
450
+ result += "\n Capabilities:";
451
+ for (const cap of agent.capabilities) {
452
+ result += `\n - ${cap.name}`;
453
+ }
454
+ }
455
+
456
+ if (agent.commands && agent.commands.length > 0) {
457
+ result += "\n Commands:";
458
+ for (const cmd of agent.commands) {
459
+ result += `\n - ${cmd.trigger}${cmd.argument ? ` ${cmd.argument}` : ""}`;
460
+ }
461
+ }
462
+ }
463
+
464
+ return result;
465
+ }
466
+
467
+ /**
468
+ * Format JSON object for display
469
+ */
470
+ private formatJSON(obj: any, indent = 0): string {
471
+ if (obj === null || obj === undefined) {
472
+ return "null";
473
+ }
474
+
475
+ if (typeof obj !== "object") {
476
+ return String(obj);
477
+ }
478
+
479
+ if (Array.isArray(obj)) {
480
+ return this.formatArray(obj, indent);
481
+ }
482
+
483
+ const spaces = " ".repeat(indent);
484
+ let result = "";
485
+
486
+ for (const [key, value] of Object.entries(obj)) {
487
+ if (typeof value === "object" && value !== null) {
488
+ result += `${spaces}${key}:\n${this.formatJSON(value, indent + 1)}`;
489
+ } else {
490
+ result += `${spaces}${key}: ${value}\n`;
491
+ }
492
+ }
493
+
494
+ return result;
495
+ }
496
+
497
+ /**
498
+ * Format array for display
499
+ */
500
+ private formatArray(arr: any[], indent = 0): string {
501
+ if (arr.length === 0) {
502
+ return "[]";
503
+ }
504
+
505
+ const spaces = " ".repeat(indent);
506
+ let result = "";
507
+
508
+ for (let i = 0; i < arr.length; i++) {
509
+ const item = arr[i];
510
+ if (typeof item === "object" && item !== null) {
511
+ result += `${spaces}[${i}]:\n${this.formatJSON(item, indent + 1)}`;
512
+ } else {
513
+ result += `${spaces}[${i}]: ${item}\n`;
514
+ }
515
+ }
516
+
517
+ return result;
518
+ }
519
+
520
+ /**
521
+ * Extract metadata from message with validation
522
+ */
523
+ private extractMetadata(message: BaseMessage): ResponseMetadata {
524
+ return ResponseMetadataSchema.parse({
525
+ timestamp: new Date(message.timestamp ?? Date.now()),
526
+ messageType: message.type,
527
+ agentId: message.from,
528
+ agentName: message.data?.agent_name,
529
+ taskId: message.task_id ?? message.data?.task_id,
530
+ success: message.data?.success !== false,
531
+ contentType: message.content_type,
532
+ reasoning: message.reasoning
533
+ });
534
+ }
535
+
536
+ /**
537
+ * Updates the format option for future formatting operations.
538
+ * Validates the option with Zod schema before applying.
539
+ *
540
+ * @param option - Format option: 'raw', 'humanized', or 'both'
541
+ * @throws {ZodError} If option is invalid
542
+ *
543
+ * @example
544
+ * ```typescript
545
+ * formatter.setFormatOption('both');
546
+ * // Now all formatted responses include both raw and humanized formats
547
+ * ```
548
+ */
549
+ public setFormatOption(option: FormatOption): void {
550
+ this.formatOption = FormatOptionSchema.parse(option);
551
+ }
552
+
553
+ /**
554
+ * Updates whether metadata should be included in formatted responses.
555
+ * Metadata includes timestamp, message type, agent info, task ID, success status, etc.
556
+ *
557
+ * @param include - Whether to include metadata in responses
558
+ * @throws {ZodError} If value is not a boolean
559
+ *
560
+ * @example
561
+ * ```typescript
562
+ * formatter.setIncludeMetadata(true);
563
+ * const formatted = formatter.format(message);
564
+ * console.log(formatted.metadata); // { timestamp, messageType, agentId, ... }
565
+ * ```
566
+ */
567
+ public setIncludeMetadata(include: boolean): void {
568
+ this.includeMetadata = z.boolean().parse(include);
569
+ }
570
+
571
+ /**
572
+ * Updates multiple format options at once with validation.
573
+ * Allows configuring format type, metadata inclusion, timestamps, and pretty-printing.
574
+ *
575
+ * @param options - Response format configuration options
576
+ * @param options.format - Format type: 'raw', 'humanized', or 'both'
577
+ * @param options.includeMetadata - Whether to include metadata
578
+ * @param options.includeTimestamps - Whether to include timestamps
579
+ * @param options.prettyPrint - Whether to pretty-print JSON
580
+ * @throws {ZodError} If options fail validation
581
+ *
582
+ * @example
583
+ * ```typescript
584
+ * formatter.setFormatOptions({
585
+ * format: 'both',
586
+ * includeMetadata: true,
587
+ * prettyPrint: true
588
+ * });
589
+ * ```
590
+ */
591
+ public setFormatOptions(options: ResponseFormatOptions): void {
592
+ const validatedOptions = ResponseFormatOptionsSchema.parse(options);
593
+
594
+ if (validatedOptions.format !== undefined) {
595
+ this.formatOption = validatedOptions.format;
596
+ }
597
+ if (validatedOptions.includeMetadata !== undefined) {
598
+ this.includeMetadata = validatedOptions.includeMetadata;
599
+ }
600
+ if (validatedOptions.includeTimestamps !== undefined) {
601
+ this.includeTimestamps = validatedOptions.includeTimestamps;
602
+ }
603
+ if (validatedOptions.prettyPrint !== undefined) {
604
+ this.prettyPrint = validatedOptions.prettyPrint;
605
+ }
606
+ }
607
+
608
+ /**
609
+ * Gets the current format configuration settings.
610
+ * Returns all current options including format type, metadata, timestamps, and pretty-print.
611
+ *
612
+ * @returns Current format options
613
+ *
614
+ * @example
615
+ * ```typescript
616
+ * const options = formatter.getFormatOptions();
617
+ * console.log(options.format); // 'both'
618
+ * console.log(options.includeMetadata); // true
619
+ * ```
620
+ */
621
+ public getFormatOptions(): ResponseFormatOptions {
622
+ return {
623
+ format: this.formatOption,
624
+ includeMetadata: this.includeMetadata,
625
+ includeTimestamps: this.includeTimestamps,
626
+ prettyPrint: this.prettyPrint
627
+ };
628
+ }
629
+
630
+ /**
631
+ * Static utility method that validates and formats any message in one step.
632
+ * Automatically detects message type and applies appropriate formatting.
633
+ * Useful for formatting messages without creating a formatter instance.
634
+ *
635
+ * @param message - Unknown message object to validate and format
636
+ * @param options - Optional format configuration
637
+ * @returns Formatted response with validated and formatted content
638
+ *
639
+ * @example
640
+ * ```typescript
641
+ * // Format unknown message from API
642
+ * const formatted = ResponseFormatter.validateAndFormat(unknownMessage, {
643
+ * format: 'humanized',
644
+ * includeMetadata: true
645
+ * });
646
+ *
647
+ * if (formatted.humanized) {
648
+ * console.log(formatted.humanized);
649
+ * }
650
+ * ```
651
+ */
652
+ public static validateAndFormat(
653
+ message: unknown,
654
+ options: ResponseFormatOptions = {}
655
+ ): FormattedResponse {
656
+ const formatter = new ResponseFormatter(options);
657
+
658
+ // Try to parse as different message types
659
+ const taskResponseResult = TaskResponseMessageSchema.safeParse(message);
660
+ if (taskResponseResult.success) {
661
+ return formatter.formatTaskResponse(taskResponseResult.data);
662
+ }
663
+
664
+ const agentSelectedResult = AgentSelectedMessageSchema.safeParse(message);
665
+ if (agentSelectedResult.success) {
666
+ return formatter.formatAgentSelected(agentSelectedResult.data);
667
+ }
668
+
669
+ const errorResult = ErrorMessageSchema.safeParse(message);
670
+ if (errorResult.success) {
671
+ return formatter.formatError(errorResult.data);
672
+ }
673
+
674
+ // Default to base message format
675
+ return formatter.format(message as BaseMessage);
676
+ }
677
+ }