@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.
- package/.dockerignore +14 -0
- package/.env.test.example +14 -0
- package/.eslintrc.json +26 -0
- package/.github/workflows/claude-code-review.yml +78 -0
- package/.github/workflows/claude-reviewer.yml +64 -0
- package/.github/workflows/publish-npm.yml +38 -0
- package/.github/workflows/push-to-main.yml +23 -0
- package/.node-version +1 -0
- package/.prettierrc +11 -0
- package/Dockerfile +25 -0
- package/LICENCE +661 -0
- package/README.md +709 -0
- package/dist/constants.d.ts +42 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +45 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/websocket-client.d.ts +261 -0
- package/dist/core/websocket-client.d.ts.map +1 -0
- package/dist/core/websocket-client.js +875 -0
- package/dist/core/websocket-client.js.map +1 -0
- package/dist/formatters/response-formatter.d.ts +354 -0
- package/dist/formatters/response-formatter.d.ts.map +1 -0
- package/dist/formatters/response-formatter.js +575 -0
- package/dist/formatters/response-formatter.js.map +1 -0
- package/dist/handlers/message-handler-registry.d.ts +155 -0
- package/dist/handlers/message-handler-registry.d.ts.map +1 -0
- package/dist/handlers/message-handler-registry.js +216 -0
- package/dist/handlers/message-handler-registry.js.map +1 -0
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts +112 -0
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agent-selected-handler.js +40 -0
- package/dist/handlers/message-handlers/agent-selected-handler.js.map +1 -0
- package/dist/handlers/message-handlers/agents-list-handler.d.ts +14 -0
- package/dist/handlers/message-handlers/agents-list-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agents-list-handler.js +25 -0
- package/dist/handlers/message-handlers/agents-list-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-error-handler.d.ts +71 -0
- package/dist/handlers/message-handlers/auth-error-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-error-handler.js +30 -0
- package/dist/handlers/message-handlers/auth-error-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-message-handler.d.ts +18 -0
- package/dist/handlers/message-handlers/auth-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-message-handler.js +60 -0
- package/dist/handlers/message-handlers/auth-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-required-handler.d.ts +76 -0
- package/dist/handlers/message-handlers/auth-required-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-required-handler.js +23 -0
- package/dist/handlers/message-handlers/auth-required-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-success-handler.d.ts +18 -0
- package/dist/handlers/message-handlers/auth-success-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-success-handler.js +51 -0
- package/dist/handlers/message-handlers/auth-success-handler.js.map +1 -0
- package/dist/handlers/message-handlers/base-handler.d.ts +55 -0
- package/dist/handlers/message-handlers/base-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/base-handler.js +83 -0
- package/dist/handlers/message-handlers/base-handler.js.map +1 -0
- package/dist/handlers/message-handlers/challenge-handler.d.ts +73 -0
- package/dist/handlers/message-handlers/challenge-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/challenge-handler.js +47 -0
- package/dist/handlers/message-handlers/challenge-handler.js.map +1 -0
- package/dist/handlers/message-handlers/error-message-handler.d.ts +76 -0
- package/dist/handlers/message-handlers/error-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/error-message-handler.js +29 -0
- package/dist/handlers/message-handlers/error-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/index.d.ts +28 -0
- package/dist/handlers/message-handlers/index.d.ts.map +1 -0
- package/dist/handlers/message-handlers/index.js +100 -0
- package/dist/handlers/message-handlers/index.js.map +1 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts +122 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.js +30 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts +104 -0
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/ping-pong-handler.js +36 -0
- package/dist/handlers/message-handlers/ping-pong-handler.js.map +1 -0
- package/dist/handlers/message-handlers/regular-message-handler.d.ts +56 -0
- package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/regular-message-handler.js +59 -0
- package/dist/handlers/message-handlers/regular-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts +81 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.js +48 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/task-response-handler.d.ts +14 -0
- package/dist/handlers/message-handlers/task-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/task-response-handler.js +44 -0
- package/dist/handlers/message-handlers/task-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/types.d.ts +51 -0
- package/dist/handlers/message-handlers/types.d.ts.map +1 -0
- package/dist/handlers/message-handlers/types.js +7 -0
- package/dist/handlers/message-handlers/types.js.map +1 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts +81 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.js +48 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.js.map +1 -0
- package/dist/handlers/webhook-handler.d.ts +202 -0
- package/dist/handlers/webhook-handler.d.ts.map +1 -0
- package/dist/handlers/webhook-handler.js +511 -0
- package/dist/handlers/webhook-handler.js.map +1 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +217 -0
- package/dist/index.js.map +1 -0
- package/dist/managers/agent-registry.d.ts +173 -0
- package/dist/managers/agent-registry.d.ts.map +1 -0
- package/dist/managers/agent-registry.js +310 -0
- package/dist/managers/agent-registry.js.map +1 -0
- package/dist/managers/connection-manager.d.ts +134 -0
- package/dist/managers/connection-manager.d.ts.map +1 -0
- package/dist/managers/connection-manager.js +176 -0
- package/dist/managers/connection-manager.js.map +1 -0
- package/dist/managers/index.d.ts +9 -0
- package/dist/managers/index.d.ts.map +1 -0
- package/dist/managers/index.js +16 -0
- package/dist/managers/index.js.map +1 -0
- package/dist/managers/message-router.d.ts +112 -0
- package/dist/managers/message-router.d.ts.map +1 -0
- package/dist/managers/message-router.js +260 -0
- package/dist/managers/message-router.js.map +1 -0
- package/dist/managers/room-manager.d.ts +165 -0
- package/dist/managers/room-manager.d.ts.map +1 -0
- package/dist/managers/room-manager.js +227 -0
- package/dist/managers/room-manager.js.map +1 -0
- package/dist/teneo-sdk.d.ts +703 -0
- package/dist/teneo-sdk.d.ts.map +1 -0
- package/dist/teneo-sdk.js +907 -0
- package/dist/teneo-sdk.js.map +1 -0
- package/dist/types/config.d.ts +1047 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +720 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/error-codes.d.ts +29 -0
- package/dist/types/error-codes.d.ts.map +1 -0
- package/dist/types/error-codes.js +41 -0
- package/dist/types/error-codes.js.map +1 -0
- package/dist/types/events.d.ts +616 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +261 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/health.d.ts +40 -0
- package/dist/types/health.d.ts.map +1 -0
- package/dist/types/health.js +6 -0
- package/dist/types/health.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +123 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/messages.d.ts +3734 -0
- package/dist/types/messages.d.ts.map +1 -0
- package/dist/types/messages.js +482 -0
- package/dist/types/messages.js.map +1 -0
- package/dist/types/validation.d.ts +81 -0
- package/dist/types/validation.d.ts.map +1 -0
- package/dist/types/validation.js +115 -0
- package/dist/types/validation.js.map +1 -0
- package/dist/utils/bounded-queue.d.ts +127 -0
- package/dist/utils/bounded-queue.d.ts.map +1 -0
- package/dist/utils/bounded-queue.js +181 -0
- package/dist/utils/bounded-queue.js.map +1 -0
- package/dist/utils/circuit-breaker.d.ts +141 -0
- package/dist/utils/circuit-breaker.d.ts.map +1 -0
- package/dist/utils/circuit-breaker.js +215 -0
- package/dist/utils/circuit-breaker.js.map +1 -0
- package/dist/utils/deduplication-cache.d.ts +110 -0
- package/dist/utils/deduplication-cache.d.ts.map +1 -0
- package/dist/utils/deduplication-cache.js +177 -0
- package/dist/utils/deduplication-cache.js.map +1 -0
- package/dist/utils/event-waiter.d.ts +101 -0
- package/dist/utils/event-waiter.d.ts.map +1 -0
- package/dist/utils/event-waiter.js +118 -0
- package/dist/utils/event-waiter.js.map +1 -0
- package/dist/utils/index.d.ts +51 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +72 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +22 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +91 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +122 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +190 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/retry-policy.d.ts +191 -0
- package/dist/utils/retry-policy.d.ts.map +1 -0
- package/dist/utils/retry-policy.js +225 -0
- package/dist/utils/retry-policy.js.map +1 -0
- package/dist/utils/secure-private-key.d.ts +113 -0
- package/dist/utils/secure-private-key.d.ts.map +1 -0
- package/dist/utils/secure-private-key.js +188 -0
- package/dist/utils/secure-private-key.js.map +1 -0
- package/dist/utils/signature-verifier.d.ts +143 -0
- package/dist/utils/signature-verifier.d.ts.map +1 -0
- package/dist/utils/signature-verifier.js +238 -0
- package/dist/utils/signature-verifier.js.map +1 -0
- package/dist/utils/ssrf-validator.d.ts +36 -0
- package/dist/utils/ssrf-validator.d.ts.map +1 -0
- package/dist/utils/ssrf-validator.js +195 -0
- package/dist/utils/ssrf-validator.js.map +1 -0
- package/examples/.env.example +17 -0
- package/examples/basic-usage.ts +211 -0
- package/examples/production-dashboard/.env.example +153 -0
- package/examples/production-dashboard/package.json +39 -0
- package/examples/production-dashboard/public/dashboard.html +642 -0
- package/examples/production-dashboard/server.ts +753 -0
- package/examples/webhook-integration.ts +239 -0
- package/examples/x-influencer-battle-redesign.html +1065 -0
- package/examples/x-influencer-battle-server.ts +217 -0
- package/examples/x-influencer-battle.html +787 -0
- package/package.json +65 -0
- package/src/constants.ts +43 -0
- package/src/core/websocket-client.test.ts +512 -0
- package/src/core/websocket-client.ts +1056 -0
- package/src/formatters/response-formatter.test.ts +571 -0
- package/src/formatters/response-formatter.ts +677 -0
- package/src/handlers/message-handler-registry.ts +239 -0
- package/src/handlers/message-handlers/agent-selected-handler.ts +40 -0
- package/src/handlers/message-handlers/agents-list-handler.ts +26 -0
- package/src/handlers/message-handlers/auth-error-handler.ts +31 -0
- package/src/handlers/message-handlers/auth-message-handler.ts +66 -0
- package/src/handlers/message-handlers/auth-required-handler.ts +23 -0
- package/src/handlers/message-handlers/auth-success-handler.ts +57 -0
- package/src/handlers/message-handlers/base-handler.ts +101 -0
- package/src/handlers/message-handlers/challenge-handler.ts +57 -0
- package/src/handlers/message-handlers/error-message-handler.ts +27 -0
- package/src/handlers/message-handlers/index.ts +77 -0
- package/src/handlers/message-handlers/list-rooms-response-handler.ts +28 -0
- package/src/handlers/message-handlers/ping-pong-handler.ts +30 -0
- package/src/handlers/message-handlers/regular-message-handler.ts +65 -0
- package/src/handlers/message-handlers/subscribe-response-handler.ts +47 -0
- package/src/handlers/message-handlers/task-response-handler.ts +45 -0
- package/src/handlers/message-handlers/types.ts +77 -0
- package/src/handlers/message-handlers/unsubscribe-response-handler.ts +47 -0
- package/src/handlers/webhook-handler.test.ts +789 -0
- package/src/handlers/webhook-handler.ts +576 -0
- package/src/index.ts +269 -0
- package/src/managers/agent-registry.test.ts +466 -0
- package/src/managers/agent-registry.ts +347 -0
- package/src/managers/connection-manager.ts +195 -0
- package/src/managers/index.ts +9 -0
- package/src/managers/message-router.ts +349 -0
- package/src/managers/room-manager.ts +248 -0
- package/src/teneo-sdk.ts +1022 -0
- package/src/types/config.test.ts +325 -0
- package/src/types/config.ts +799 -0
- package/src/types/error-codes.ts +44 -0
- package/src/types/events.test.ts +302 -0
- package/src/types/events.ts +382 -0
- package/src/types/health.ts +46 -0
- package/src/types/index.ts +199 -0
- package/src/types/messages.test.ts +660 -0
- package/src/types/messages.ts +570 -0
- package/src/types/validation.ts +123 -0
- package/src/utils/bounded-queue.test.ts +356 -0
- package/src/utils/bounded-queue.ts +205 -0
- package/src/utils/circuit-breaker.test.ts +394 -0
- package/src/utils/circuit-breaker.ts +262 -0
- package/src/utils/deduplication-cache.test.ts +380 -0
- package/src/utils/deduplication-cache.ts +198 -0
- package/src/utils/event-waiter.test.ts +381 -0
- package/src/utils/event-waiter.ts +172 -0
- package/src/utils/index.ts +74 -0
- package/src/utils/logger.ts +87 -0
- package/src/utils/rate-limiter.test.ts +341 -0
- package/src/utils/rate-limiter.ts +211 -0
- package/src/utils/retry-policy.test.ts +558 -0
- package/src/utils/retry-policy.ts +272 -0
- package/src/utils/secure-private-key.test.ts +356 -0
- package/src/utils/secure-private-key.ts +205 -0
- package/src/utils/signature-verifier.test.ts +464 -0
- package/src/utils/signature-verifier.ts +298 -0
- package/src/utils/ssrf-validator.test.ts +372 -0
- package/src/utils/ssrf-validator.ts +224 -0
- package/tests/integration/real-server.test.ts +740 -0
- package/tests/integration/websocket.test.ts +381 -0
- package/tests/integration-setup.ts +16 -0
- package/tests/setup.ts +34 -0
- package/tsconfig.json +32 -0
- package/vitest.config.ts +42 -0
- package/vitest.integration.config.ts +23 -0
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { ResponseFormatter } from "./response-formatter";
|
|
3
|
+
import type {
|
|
4
|
+
TaskResponseMessage,
|
|
5
|
+
AgentSelectedMessage,
|
|
6
|
+
ErrorMessage,
|
|
7
|
+
Agent,
|
|
8
|
+
BaseMessage
|
|
9
|
+
} from "../types";
|
|
10
|
+
|
|
11
|
+
describe("ResponseFormatter", () => {
|
|
12
|
+
let formatter: ResponseFormatter;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
formatter = new ResponseFormatter({ format: "humanized", includeMetadata: false });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("constructor", () => {
|
|
19
|
+
it("should initialize with default options", () => {
|
|
20
|
+
expect(formatter).toBeDefined();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("should initialize with different format options", () => {
|
|
24
|
+
const rawFormatter = new ResponseFormatter({ format: "raw", includeMetadata: false });
|
|
25
|
+
const bothFormatter = new ResponseFormatter({ format: "both", includeMetadata: true });
|
|
26
|
+
|
|
27
|
+
expect(rawFormatter).toBeDefined();
|
|
28
|
+
expect(bothFormatter).toBeDefined();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should validate format option", () => {
|
|
32
|
+
expect(() => new ResponseFormatter({ format: "invalid" as any })).toThrow();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("formatTaskResponse", () => {
|
|
37
|
+
const taskResponse: TaskResponseMessage = {
|
|
38
|
+
type: "task_response",
|
|
39
|
+
content: "Task completed successfully",
|
|
40
|
+
from: "agent-1",
|
|
41
|
+
data: {
|
|
42
|
+
agent_name: "Test Agent",
|
|
43
|
+
task_id: "task-123",
|
|
44
|
+
success: true
|
|
45
|
+
},
|
|
46
|
+
content_type: "text/plain",
|
|
47
|
+
reasoning: "Task was straightforward",
|
|
48
|
+
timestamp: new Date().toISOString()
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
describe("with humanized format", () => {
|
|
52
|
+
it("should format task response to humanized text", () => {
|
|
53
|
+
const result = formatter.formatTaskResponse(taskResponse);
|
|
54
|
+
|
|
55
|
+
expect(result.humanized).toBeDefined();
|
|
56
|
+
expect(result.humanized).toContain("[RESPONSE] From Test Agent");
|
|
57
|
+
expect(result.humanized).toContain("Task completed successfully");
|
|
58
|
+
expect(result.humanized).toContain("[REASONING] Task was straightforward");
|
|
59
|
+
expect(result.raw).toBeUndefined();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should handle error in task response", () => {
|
|
63
|
+
const errorResponse: TaskResponseMessage = {
|
|
64
|
+
...taskResponse,
|
|
65
|
+
data: {
|
|
66
|
+
...taskResponse.data,
|
|
67
|
+
success: false,
|
|
68
|
+
error: "Task failed due to error"
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const result = formatter.formatTaskResponse(errorResponse);
|
|
73
|
+
expect(result.humanized).toContain("[ERROR] Task failed due to error");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should handle JSON content type", () => {
|
|
77
|
+
const jsonResponse: TaskResponseMessage = {
|
|
78
|
+
...taskResponse,
|
|
79
|
+
content: JSON.stringify({ message: "Hello", data: [1, 2, 3] }),
|
|
80
|
+
content_type: "application/json"
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const result = formatter.formatTaskResponse(jsonResponse);
|
|
84
|
+
expect(result.humanized).toContain("message: Hello");
|
|
85
|
+
expect(result.humanized).toContain("data:");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should handle markdown content type", () => {
|
|
89
|
+
const mdResponse: TaskResponseMessage = {
|
|
90
|
+
...taskResponse,
|
|
91
|
+
content: "# Heading\n\n**Bold text**",
|
|
92
|
+
content_type: "text/markdown"
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const result = formatter.formatTaskResponse(mdResponse);
|
|
96
|
+
expect(result.humanized).toContain("# Heading");
|
|
97
|
+
expect(result.humanized).toContain("**Bold text**");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("should handle array content type", () => {
|
|
101
|
+
const arrayResponse: TaskResponseMessage = {
|
|
102
|
+
...taskResponse,
|
|
103
|
+
content: JSON.stringify(["item1", "item2", "item3"]),
|
|
104
|
+
content_type: "ARRAY"
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const result = formatter.formatTaskResponse(arrayResponse);
|
|
108
|
+
expect(result.humanized).toContain("[0]: item1");
|
|
109
|
+
expect(result.humanized).toContain("[1]: item2");
|
|
110
|
+
expect(result.humanized).toContain("[2]: item3");
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe("with raw format", () => {
|
|
115
|
+
beforeEach(() => {
|
|
116
|
+
formatter = new ResponseFormatter({ format: "raw", includeMetadata: false });
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("should return raw task response", () => {
|
|
120
|
+
const result = formatter.formatTaskResponse(taskResponse);
|
|
121
|
+
|
|
122
|
+
expect(result.raw).toEqual(taskResponse);
|
|
123
|
+
expect(result.humanized).toBeUndefined();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe("with both format", () => {
|
|
128
|
+
beforeEach(() => {
|
|
129
|
+
formatter = new ResponseFormatter({ format: "both", includeMetadata: false });
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should return both raw and humanized formats", () => {
|
|
133
|
+
const result = formatter.formatTaskResponse(taskResponse);
|
|
134
|
+
|
|
135
|
+
expect(result.raw).toEqual(taskResponse);
|
|
136
|
+
expect(result.humanized).toBeDefined();
|
|
137
|
+
expect(result.humanized).toContain("[RESPONSE] From Test Agent");
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe("with metadata", () => {
|
|
142
|
+
beforeEach(() => {
|
|
143
|
+
formatter = new ResponseFormatter({ format: "both", includeMetadata: true });
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("should include metadata in response", () => {
|
|
147
|
+
const result = formatter.formatTaskResponse(taskResponse);
|
|
148
|
+
|
|
149
|
+
expect(result.metadata).toBeDefined();
|
|
150
|
+
expect(result.metadata?.messageType).toBe("task_response");
|
|
151
|
+
expect(result.metadata?.agentId).toBe("agent-1");
|
|
152
|
+
expect(result.metadata?.agentName).toBe("Test Agent");
|
|
153
|
+
expect(result.metadata?.taskId).toBe("task-123");
|
|
154
|
+
expect(result.metadata?.success).toBe(true);
|
|
155
|
+
expect(result.metadata?.reasoning).toBe("Task was straightforward");
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe("formatAgentSelected", () => {
|
|
161
|
+
const agentSelected: AgentSelectedMessage = {
|
|
162
|
+
type: "agent_selected",
|
|
163
|
+
content: "Agent selected",
|
|
164
|
+
from: "coordinator",
|
|
165
|
+
data: {
|
|
166
|
+
agent_id: "agent-1",
|
|
167
|
+
agent_name: "Test Agent",
|
|
168
|
+
user_request: "Help me with a task",
|
|
169
|
+
capabilities: [
|
|
170
|
+
{ name: "chat", description: "Chat with users" },
|
|
171
|
+
{ name: "code", description: "Write code" }
|
|
172
|
+
],
|
|
173
|
+
command: "execute",
|
|
174
|
+
command_reasoning: "Best agent for the task"
|
|
175
|
+
},
|
|
176
|
+
reasoning: "Selected based on capabilities",
|
|
177
|
+
timestamp: new Date().toISOString()
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
it("should format agent selected message in humanized format", () => {
|
|
181
|
+
const result = formatter.formatAgentSelected(agentSelected);
|
|
182
|
+
|
|
183
|
+
expect(result.humanized).toContain("[AGENT] Coordinator selected Test Agent");
|
|
184
|
+
expect(result.humanized).toContain("[COMMAND] execute");
|
|
185
|
+
expect(result.humanized).toContain("[REASONING] Selected based on capabilities");
|
|
186
|
+
expect(result.humanized).toContain("[CAPABILITIES]");
|
|
187
|
+
expect(result.humanized).toContain("- chat: Chat with users");
|
|
188
|
+
expect(result.humanized).toContain("- code: Write code");
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("should handle missing optional fields in data", () => {
|
|
192
|
+
const minimal: AgentSelectedMessage = {
|
|
193
|
+
type: "agent_selected",
|
|
194
|
+
content: "Selected",
|
|
195
|
+
from: "coordinator",
|
|
196
|
+
reasoning: "Selected by coordinator",
|
|
197
|
+
data: {
|
|
198
|
+
agent_id: "agent-1",
|
|
199
|
+
agent_name: "Agent",
|
|
200
|
+
user_request: "Request"
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const result = formatter.formatAgentSelected(minimal);
|
|
205
|
+
expect(result.humanized).toContain("[AGENT] Coordinator selected Agent");
|
|
206
|
+
expect(result.humanized).not.toContain("[COMMAND]");
|
|
207
|
+
expect(result.humanized).not.toContain("[CAPABILITIES]");
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
describe("formatError", () => {
|
|
212
|
+
const errorMessage: ErrorMessage = {
|
|
213
|
+
type: "error",
|
|
214
|
+
content: "An error occurred",
|
|
215
|
+
from: "system",
|
|
216
|
+
data: {
|
|
217
|
+
message: "Connection failed",
|
|
218
|
+
code: 500,
|
|
219
|
+
details: { reason: "timeout", attempts: 3 }
|
|
220
|
+
},
|
|
221
|
+
timestamp: new Date().toISOString()
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
it("should format error message in humanized format", () => {
|
|
225
|
+
const result = formatter.formatError(errorMessage);
|
|
226
|
+
|
|
227
|
+
expect(result.humanized).toContain("[ERROR] An error occurred");
|
|
228
|
+
expect(result.humanized).toContain("(Code: 500)");
|
|
229
|
+
expect(result.humanized).toContain("Details:");
|
|
230
|
+
expect(result.humanized).toContain('"reason": "timeout"');
|
|
231
|
+
expect(result.humanized).toContain('"attempts": 3');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("should handle minimal error", () => {
|
|
235
|
+
const minimal: ErrorMessage = {
|
|
236
|
+
type: "error",
|
|
237
|
+
content: "Simple error",
|
|
238
|
+
from: "system",
|
|
239
|
+
data: {
|
|
240
|
+
message: "Simple error",
|
|
241
|
+
code: 1
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const result = formatter.formatError(minimal);
|
|
246
|
+
expect(result.humanized).toBe("[ERROR] Simple error (Code: 1)");
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
describe("formatAgentList", () => {
|
|
251
|
+
const agents: Agent[] = [
|
|
252
|
+
{
|
|
253
|
+
id: "agent-1",
|
|
254
|
+
name: "Agent One",
|
|
255
|
+
description: "First agent",
|
|
256
|
+
status: "online",
|
|
257
|
+
room: "room-1",
|
|
258
|
+
capabilities: [{ name: "chat", description: "Chat capability" }],
|
|
259
|
+
commands: [{ trigger: "/help", description: "Show help", argument: "[topic]" }]
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: "agent-2",
|
|
263
|
+
name: "Agent Two",
|
|
264
|
+
description: "Second agent",
|
|
265
|
+
room: "room-2",
|
|
266
|
+
status: "offline"
|
|
267
|
+
}
|
|
268
|
+
];
|
|
269
|
+
|
|
270
|
+
it("should format agent list in humanized format", () => {
|
|
271
|
+
const result = formatter.formatAgentList(agents);
|
|
272
|
+
|
|
273
|
+
expect(result.humanized).toContain("[AGENTS] Available Agents (2):");
|
|
274
|
+
expect(result.humanized).toContain("[AGENT] Agent One [ONLINE]");
|
|
275
|
+
expect(result.humanized).toContain("First agent");
|
|
276
|
+
expect(result.humanized).toContain("Room: room-1");
|
|
277
|
+
expect(result.humanized).toContain("Capabilities:");
|
|
278
|
+
expect(result.humanized).toContain("- chat");
|
|
279
|
+
expect(result.humanized).toContain("Commands:");
|
|
280
|
+
expect(result.humanized).toContain("- /help [topic]");
|
|
281
|
+
expect(result.humanized).toContain("[AGENT] Agent Two [OFFLINE]");
|
|
282
|
+
expect(result.humanized).toContain("Second agent");
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it("should handle empty agent list", () => {
|
|
286
|
+
const result = formatter.formatAgentList([]);
|
|
287
|
+
expect(result.humanized).toBe("[AGENTS] No agents available");
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
describe("format (generic)", () => {
|
|
292
|
+
it("should format message type", () => {
|
|
293
|
+
const message: BaseMessage = {
|
|
294
|
+
type: "message",
|
|
295
|
+
content: "Hello world",
|
|
296
|
+
from: "user-1",
|
|
297
|
+
room: "general"
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const result = formatter.format(message);
|
|
301
|
+
expect(result.humanized).toBe("[general] [MESSAGE] user-1: Hello world");
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it("should handle unknown message types", () => {
|
|
305
|
+
const unknown: BaseMessage = {
|
|
306
|
+
type: "unknown" as any,
|
|
307
|
+
content: "Unknown content",
|
|
308
|
+
data: { key: "value" }
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const result = formatter.format(unknown);
|
|
312
|
+
expect(result.humanized).toBe("Unknown content");
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("should fall back to data when no content", () => {
|
|
316
|
+
const noContent: BaseMessage = {
|
|
317
|
+
type: "custom" as any,
|
|
318
|
+
data: { message: "from data" }
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const result = formatter.format(noContent);
|
|
322
|
+
expect(result.humanized).toBe('{"message":"from data"}');
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
describe("setFormatOption", () => {
|
|
327
|
+
it("should update format option", () => {
|
|
328
|
+
formatter.setFormatOption("raw");
|
|
329
|
+
const result = formatter.formatError({
|
|
330
|
+
type: "error",
|
|
331
|
+
content: "Test",
|
|
332
|
+
from: "system",
|
|
333
|
+
data: { message: "Test", code: 1 }
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
expect(result.raw).toBeDefined();
|
|
337
|
+
expect(result.humanized).toBeUndefined();
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it("should validate format option", () => {
|
|
341
|
+
expect(() => formatter.setFormatOption("invalid" as any)).toThrow();
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
describe("setIncludeMetadata", () => {
|
|
346
|
+
it("should update metadata inclusion", () => {
|
|
347
|
+
formatter.setIncludeMetadata(true);
|
|
348
|
+
const result = formatter.formatError({
|
|
349
|
+
type: "error",
|
|
350
|
+
content: "Test",
|
|
351
|
+
from: "system",
|
|
352
|
+
data: { message: "Test", code: 1 }
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
expect(result.metadata).toBeDefined();
|
|
356
|
+
expect(result.metadata?.messageType).toBe("error");
|
|
357
|
+
expect(result.metadata?.success).toBe(false);
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
describe("validateAndFormat (static)", () => {
|
|
362
|
+
it("should detect and format task response", () => {
|
|
363
|
+
const taskResponse: TaskResponseMessage = {
|
|
364
|
+
type: "task_response",
|
|
365
|
+
content: "Task done",
|
|
366
|
+
from: "agent-1",
|
|
367
|
+
content_type: "text/plain",
|
|
368
|
+
data: {
|
|
369
|
+
task_id: "task-123",
|
|
370
|
+
agent_name: "agent-1",
|
|
371
|
+
success: true
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
const result = ResponseFormatter.validateAndFormat(taskResponse);
|
|
376
|
+
expect(result.humanized).toContain("[RESPONSE] From agent-1");
|
|
377
|
+
expect(result.humanized).toContain("Task done");
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it("should detect and format agent selected", () => {
|
|
381
|
+
const agentSelected: AgentSelectedMessage = {
|
|
382
|
+
type: "agent_selected",
|
|
383
|
+
content: "Selected",
|
|
384
|
+
from: "coordinator",
|
|
385
|
+
reasoning: "Selected for task",
|
|
386
|
+
data: {
|
|
387
|
+
agent_id: "agent-1",
|
|
388
|
+
agent_name: "Agent",
|
|
389
|
+
user_request: "Help"
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const result = ResponseFormatter.validateAndFormat(agentSelected);
|
|
394
|
+
expect(result.humanized).toContain("[AGENT] Coordinator selected Agent");
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it("should detect and format error", () => {
|
|
398
|
+
const error: ErrorMessage = {
|
|
399
|
+
type: "error",
|
|
400
|
+
content: "Error occurred",
|
|
401
|
+
from: "system",
|
|
402
|
+
data: {
|
|
403
|
+
message: "Error occurred",
|
|
404
|
+
code: 500
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const result = ResponseFormatter.validateAndFormat(error);
|
|
409
|
+
expect(result.humanized).toBe("[ERROR] Error occurred (Code: 500)");
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it("should fall back to generic format for unknown types", () => {
|
|
413
|
+
const unknown = {
|
|
414
|
+
type: "message",
|
|
415
|
+
content: "Generic message",
|
|
416
|
+
from: "user"
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
const result = ResponseFormatter.validateAndFormat(unknown);
|
|
420
|
+
expect(result.humanized).toContain("[MESSAGE] user: Generic message");
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it("should use provided format options", () => {
|
|
424
|
+
const message = {
|
|
425
|
+
type: "error",
|
|
426
|
+
content: "Test",
|
|
427
|
+
from: "system",
|
|
428
|
+
data: { message: "Test", code: 1 }
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
const rawResult = ResponseFormatter.validateAndFormat(message, { format: "raw", includeMetadata: false });
|
|
432
|
+
expect(rawResult.raw).toBeDefined();
|
|
433
|
+
expect(rawResult.humanized).toBeUndefined();
|
|
434
|
+
expect(rawResult.metadata).toBeUndefined();
|
|
435
|
+
|
|
436
|
+
const bothWithMeta = ResponseFormatter.validateAndFormat(message, { format: "both", includeMetadata: true });
|
|
437
|
+
expect(bothWithMeta.raw).toBeDefined();
|
|
438
|
+
expect(bothWithMeta.humanized).toBeDefined();
|
|
439
|
+
expect(bothWithMeta.metadata).toBeDefined();
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
describe("private formatting helpers", () => {
|
|
444
|
+
describe("formatJSON", () => {
|
|
445
|
+
it("should format nested objects", () => {
|
|
446
|
+
const json = { level1: { level2: { value: "deep" } } };
|
|
447
|
+
const result = (formatter as any).formatJSON(json);
|
|
448
|
+
|
|
449
|
+
expect(result).toContain("level1:");
|
|
450
|
+
expect(result).toContain(" level2:");
|
|
451
|
+
expect(result).toContain(" value: deep");
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it("should handle null and undefined", () => {
|
|
455
|
+
expect((formatter as any).formatJSON(null)).toBe("null");
|
|
456
|
+
expect((formatter as any).formatJSON(undefined)).toBe("null");
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it("should handle primitive values", () => {
|
|
460
|
+
expect((formatter as any).formatJSON("string")).toBe("string");
|
|
461
|
+
expect((formatter as any).formatJSON(123)).toBe("123");
|
|
462
|
+
expect((formatter as any).formatJSON(true)).toBe("true");
|
|
463
|
+
});
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
describe("formatArray", () => {
|
|
467
|
+
it("should format simple arrays", () => {
|
|
468
|
+
const result = (formatter as any).formatArray([1, 2, 3]);
|
|
469
|
+
expect(result).toContain("[0]: 1");
|
|
470
|
+
expect(result).toContain("[1]: 2");
|
|
471
|
+
expect(result).toContain("[2]: 3");
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
it("should format arrays with objects", () => {
|
|
475
|
+
const arr = [{ key: "value1" }, { key: "value2" }];
|
|
476
|
+
const result = (formatter as any).formatArray(arr);
|
|
477
|
+
|
|
478
|
+
expect(result).toContain("[0]:");
|
|
479
|
+
expect(result).toContain("key: value1");
|
|
480
|
+
expect(result).toContain("[1]:");
|
|
481
|
+
expect(result).toContain("key: value2");
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it("should handle empty arrays", () => {
|
|
485
|
+
expect((formatter as any).formatArray([])).toBe("[]");
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
describe("edge cases", () => {
|
|
491
|
+
it("should handle responses with missing fields gracefully", () => {
|
|
492
|
+
const incomplete = {
|
|
493
|
+
type: "task_response"
|
|
494
|
+
// Missing required fields will be caught by validation
|
|
495
|
+
} as any;
|
|
496
|
+
|
|
497
|
+
expect(() => formatter.formatTaskResponse(incomplete)).toThrow();
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it("should handle very large content", () => {
|
|
501
|
+
const largeContent = "x".repeat(100000);
|
|
502
|
+
const message: TaskResponseMessage = {
|
|
503
|
+
type: "task_response",
|
|
504
|
+
content: largeContent,
|
|
505
|
+
from: "agent-1",
|
|
506
|
+
content_type: "text/plain",
|
|
507
|
+
data: {
|
|
508
|
+
task_id: "task-large",
|
|
509
|
+
success: true
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
const result = formatter.formatTaskResponse(message);
|
|
514
|
+
expect(result.humanized).toContain(largeContent);
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
it("should handle special characters in content", () => {
|
|
518
|
+
const specialContent = 'Hello\nWorld\t"Quoted"\n\n• Bullet';
|
|
519
|
+
const message: TaskResponseMessage = {
|
|
520
|
+
type: "task_response",
|
|
521
|
+
content: specialContent,
|
|
522
|
+
from: "agent-1",
|
|
523
|
+
content_type: "text/plain",
|
|
524
|
+
data: {
|
|
525
|
+
task_id: "task-special",
|
|
526
|
+
success: true
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
const result = formatter.formatTaskResponse(message);
|
|
531
|
+
expect(result.humanized).toContain(specialContent);
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
it("should handle unicode content", () => {
|
|
535
|
+
const unicodeContent = "Hello 世界 🌍 مرحبا мир";
|
|
536
|
+
const message: TaskResponseMessage = {
|
|
537
|
+
type: "task_response",
|
|
538
|
+
content: unicodeContent,
|
|
539
|
+
from: "agent-1",
|
|
540
|
+
content_type: "text/plain",
|
|
541
|
+
data: {
|
|
542
|
+
task_id: "task-unicode",
|
|
543
|
+
success: true
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
const result = formatter.formatTaskResponse(message);
|
|
548
|
+
expect(result.humanized).toContain(unicodeContent);
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
it("should handle circular references gracefully", () => {
|
|
552
|
+
const circular: any = { a: {} };
|
|
553
|
+
circular.a.b = circular;
|
|
554
|
+
|
|
555
|
+
const message: TaskResponseMessage = {
|
|
556
|
+
type: "task_response",
|
|
557
|
+
content: JSON.stringify({ safe: "content" }),
|
|
558
|
+
from: "agent-1",
|
|
559
|
+
content_type: "application/json",
|
|
560
|
+
data: {
|
|
561
|
+
task_id: "task-circular",
|
|
562
|
+
success: true
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
// Should not throw
|
|
567
|
+
const result = formatter.formatTaskResponse(message);
|
|
568
|
+
expect(result.humanized).toBeDefined();
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
});
|