@qodo/sdk 0.1.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 (303) hide show
  1. package/LICENSE +118 -0
  2. package/README.md +121 -0
  3. package/dist/api/agent.d.ts +69 -0
  4. package/dist/api/agent.d.ts.map +1 -0
  5. package/dist/api/agent.js +1034 -0
  6. package/dist/api/agent.js.map +1 -0
  7. package/dist/api/analytics.d.ts +43 -0
  8. package/dist/api/analytics.d.ts.map +1 -0
  9. package/dist/api/analytics.js +163 -0
  10. package/dist/api/analytics.js.map +1 -0
  11. package/dist/api/http.d.ts +5 -0
  12. package/dist/api/http.d.ts.map +1 -0
  13. package/dist/api/http.js +59 -0
  14. package/dist/api/http.js.map +1 -0
  15. package/dist/api/index.d.ts +12 -0
  16. package/dist/api/index.d.ts.map +1 -0
  17. package/dist/api/index.js +17 -0
  18. package/dist/api/index.js.map +1 -0
  19. package/dist/api/taskTracking.d.ts +54 -0
  20. package/dist/api/taskTracking.d.ts.map +1 -0
  21. package/dist/api/taskTracking.js +208 -0
  22. package/dist/api/taskTracking.js.map +1 -0
  23. package/dist/api/types.d.ts +92 -0
  24. package/dist/api/types.d.ts.map +1 -0
  25. package/dist/api/types.js +2 -0
  26. package/dist/api/types.js.map +1 -0
  27. package/dist/api/utils.d.ts +8 -0
  28. package/dist/api/utils.d.ts.map +1 -0
  29. package/dist/api/utils.js +54 -0
  30. package/dist/api/utils.js.map +1 -0
  31. package/dist/api/websocket.d.ts +74 -0
  32. package/dist/api/websocket.d.ts.map +1 -0
  33. package/dist/api/websocket.js +685 -0
  34. package/dist/api/websocket.js.map +1 -0
  35. package/dist/auth/index.d.ts +25 -0
  36. package/dist/auth/index.d.ts.map +1 -0
  37. package/dist/auth/index.js +85 -0
  38. package/dist/auth/index.js.map +1 -0
  39. package/dist/clients/index.d.ts +8 -0
  40. package/dist/clients/index.d.ts.map +1 -0
  41. package/dist/clients/index.js +7 -0
  42. package/dist/clients/index.js.map +1 -0
  43. package/dist/clients/info/InfoClient.d.ts +37 -0
  44. package/dist/clients/info/InfoClient.d.ts.map +1 -0
  45. package/dist/clients/info/InfoClient.js +69 -0
  46. package/dist/clients/info/InfoClient.js.map +1 -0
  47. package/dist/clients/info/index.d.ts +4 -0
  48. package/dist/clients/info/index.d.ts.map +1 -0
  49. package/dist/clients/info/index.js +2 -0
  50. package/dist/clients/info/index.js.map +1 -0
  51. package/dist/clients/info/types.d.ts +21 -0
  52. package/dist/clients/info/types.d.ts.map +1 -0
  53. package/dist/clients/info/types.js +2 -0
  54. package/dist/clients/info/types.js.map +1 -0
  55. package/dist/clients/sessions/SessionsClient.d.ts +34 -0
  56. package/dist/clients/sessions/SessionsClient.d.ts.map +1 -0
  57. package/dist/clients/sessions/SessionsClient.js +71 -0
  58. package/dist/clients/sessions/SessionsClient.js.map +1 -0
  59. package/dist/clients/sessions/index.d.ts +4 -0
  60. package/dist/clients/sessions/index.d.ts.map +1 -0
  61. package/dist/clients/sessions/index.js +2 -0
  62. package/dist/clients/sessions/index.js.map +1 -0
  63. package/dist/clients/sessions/types.d.ts +20 -0
  64. package/dist/clients/sessions/types.d.ts.map +1 -0
  65. package/dist/clients/sessions/types.js +2 -0
  66. package/dist/clients/sessions/types.js.map +1 -0
  67. package/dist/config/ConfigManager.d.ts +43 -0
  68. package/dist/config/ConfigManager.d.ts.map +1 -0
  69. package/dist/config/ConfigManager.js +472 -0
  70. package/dist/config/ConfigManager.js.map +1 -0
  71. package/dist/config/index.d.ts +6 -0
  72. package/dist/config/index.d.ts.map +1 -0
  73. package/dist/config/index.js +7 -0
  74. package/dist/config/index.js.map +1 -0
  75. package/dist/config/urlConfig.d.ts +15 -0
  76. package/dist/config/urlConfig.d.ts.map +1 -0
  77. package/dist/config/urlConfig.js +75 -0
  78. package/dist/config/urlConfig.js.map +1 -0
  79. package/dist/constants/errors.d.ts +2 -0
  80. package/dist/constants/errors.d.ts.map +1 -0
  81. package/dist/constants/errors.js +2 -0
  82. package/dist/constants/errors.js.map +1 -0
  83. package/dist/constants/index.d.ts +7 -0
  84. package/dist/constants/index.d.ts.map +1 -0
  85. package/dist/constants/index.js +11 -0
  86. package/dist/constants/index.js.map +1 -0
  87. package/dist/constants/tools.d.ts +4 -0
  88. package/dist/constants/tools.d.ts.map +1 -0
  89. package/dist/constants/tools.js +4 -0
  90. package/dist/constants/tools.js.map +1 -0
  91. package/dist/constants/versions.d.ts +2 -0
  92. package/dist/constants/versions.d.ts.map +1 -0
  93. package/dist/constants/versions.js +2 -0
  94. package/dist/constants/versions.js.map +1 -0
  95. package/dist/context/buildUserContext.d.ts +18 -0
  96. package/dist/context/buildUserContext.d.ts.map +1 -0
  97. package/dist/context/buildUserContext.js +34 -0
  98. package/dist/context/buildUserContext.js.map +1 -0
  99. package/dist/context/index.d.ts +9 -0
  100. package/dist/context/index.d.ts.map +1 -0
  101. package/dist/context/index.js +9 -0
  102. package/dist/context/index.js.map +1 -0
  103. package/dist/context/messageManager.d.ts +42 -0
  104. package/dist/context/messageManager.d.ts.map +1 -0
  105. package/dist/context/messageManager.js +322 -0
  106. package/dist/context/messageManager.js.map +1 -0
  107. package/dist/context/taskFocus.d.ts +2 -0
  108. package/dist/context/taskFocus.d.ts.map +1 -0
  109. package/dist/context/taskFocus.js +26 -0
  110. package/dist/context/taskFocus.js.map +1 -0
  111. package/dist/context/userInput.d.ts +3 -0
  112. package/dist/context/userInput.d.ts.map +1 -0
  113. package/dist/context/userInput.js +20 -0
  114. package/dist/context/userInput.js.map +1 -0
  115. package/dist/index.d.ts +18 -0
  116. package/dist/index.d.ts.map +1 -0
  117. package/dist/index.js +21 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/mcp/MCPManager.d.ts +125 -0
  120. package/dist/mcp/MCPManager.d.ts.map +1 -0
  121. package/dist/mcp/MCPManager.js +616 -0
  122. package/dist/mcp/MCPManager.js.map +1 -0
  123. package/dist/mcp/approvedTools.d.ts +4 -0
  124. package/dist/mcp/approvedTools.d.ts.map +1 -0
  125. package/dist/mcp/approvedTools.js +19 -0
  126. package/dist/mcp/approvedTools.js.map +1 -0
  127. package/dist/mcp/baseServer.d.ts +75 -0
  128. package/dist/mcp/baseServer.d.ts.map +1 -0
  129. package/dist/mcp/baseServer.js +107 -0
  130. package/dist/mcp/baseServer.js.map +1 -0
  131. package/dist/mcp/builtinServers.d.ts +15 -0
  132. package/dist/mcp/builtinServers.d.ts.map +1 -0
  133. package/dist/mcp/builtinServers.js +155 -0
  134. package/dist/mcp/builtinServers.js.map +1 -0
  135. package/dist/mcp/dynamicBEServer.d.ts +20 -0
  136. package/dist/mcp/dynamicBEServer.d.ts.map +1 -0
  137. package/dist/mcp/dynamicBEServer.js +52 -0
  138. package/dist/mcp/dynamicBEServer.js.map +1 -0
  139. package/dist/mcp/index.d.ts +19 -0
  140. package/dist/mcp/index.d.ts.map +1 -0
  141. package/dist/mcp/index.js +24 -0
  142. package/dist/mcp/index.js.map +1 -0
  143. package/dist/mcp/mcpInitialization.d.ts +2 -0
  144. package/dist/mcp/mcpInitialization.d.ts.map +1 -0
  145. package/dist/mcp/mcpInitialization.js +56 -0
  146. package/dist/mcp/mcpInitialization.js.map +1 -0
  147. package/dist/mcp/servers/filesystem.d.ts +75 -0
  148. package/dist/mcp/servers/filesystem.d.ts.map +1 -0
  149. package/dist/mcp/servers/filesystem.js +992 -0
  150. package/dist/mcp/servers/filesystem.js.map +1 -0
  151. package/dist/mcp/servers/gerrit.d.ts +19 -0
  152. package/dist/mcp/servers/gerrit.d.ts.map +1 -0
  153. package/dist/mcp/servers/gerrit.js +515 -0
  154. package/dist/mcp/servers/gerrit.js.map +1 -0
  155. package/dist/mcp/servers/git.d.ts +18 -0
  156. package/dist/mcp/servers/git.d.ts.map +1 -0
  157. package/dist/mcp/servers/git.js +441 -0
  158. package/dist/mcp/servers/git.js.map +1 -0
  159. package/dist/mcp/servers/ripgrep.d.ts +34 -0
  160. package/dist/mcp/servers/ripgrep.d.ts.map +1 -0
  161. package/dist/mcp/servers/ripgrep.js +517 -0
  162. package/dist/mcp/servers/ripgrep.js.map +1 -0
  163. package/dist/mcp/servers/shell.d.ts +20 -0
  164. package/dist/mcp/servers/shell.d.ts.map +1 -0
  165. package/dist/mcp/servers/shell.js +603 -0
  166. package/dist/mcp/servers/shell.js.map +1 -0
  167. package/dist/mcp/serversRegistry.d.ts +55 -0
  168. package/dist/mcp/serversRegistry.d.ts.map +1 -0
  169. package/dist/mcp/serversRegistry.js +410 -0
  170. package/dist/mcp/serversRegistry.js.map +1 -0
  171. package/dist/mcp/toolProcessor.d.ts +42 -0
  172. package/dist/mcp/toolProcessor.d.ts.map +1 -0
  173. package/dist/mcp/toolProcessor.js +200 -0
  174. package/dist/mcp/toolProcessor.js.map +1 -0
  175. package/dist/mcp/types.d.ts +29 -0
  176. package/dist/mcp/types.d.ts.map +1 -0
  177. package/dist/mcp/types.js +2 -0
  178. package/dist/mcp/types.js.map +1 -0
  179. package/dist/parser/index.d.ts +72 -0
  180. package/dist/parser/index.d.ts.map +1 -0
  181. package/dist/parser/index.js +967 -0
  182. package/dist/parser/index.js.map +1 -0
  183. package/dist/parser/types.d.ts +153 -0
  184. package/dist/parser/types.d.ts.map +1 -0
  185. package/dist/parser/types.js +6 -0
  186. package/dist/parser/types.js.map +1 -0
  187. package/dist/parser/utils.d.ts +18 -0
  188. package/dist/parser/utils.d.ts.map +1 -0
  189. package/dist/parser/utils.js +64 -0
  190. package/dist/parser/utils.js.map +1 -0
  191. package/dist/sdk/QodoSDK.d.ts +152 -0
  192. package/dist/sdk/QodoSDK.d.ts.map +1 -0
  193. package/dist/sdk/QodoSDK.js +786 -0
  194. package/dist/sdk/QodoSDK.js.map +1 -0
  195. package/dist/sdk/bootstrap.d.ts +16 -0
  196. package/dist/sdk/bootstrap.d.ts.map +1 -0
  197. package/dist/sdk/bootstrap.js +21 -0
  198. package/dist/sdk/bootstrap.js.map +1 -0
  199. package/dist/sdk/builders.d.ts +54 -0
  200. package/dist/sdk/builders.d.ts.map +1 -0
  201. package/dist/sdk/builders.js +117 -0
  202. package/dist/sdk/builders.js.map +1 -0
  203. package/dist/sdk/defaults.d.ts +11 -0
  204. package/dist/sdk/defaults.d.ts.map +1 -0
  205. package/dist/sdk/defaults.js +39 -0
  206. package/dist/sdk/defaults.js.map +1 -0
  207. package/dist/sdk/discovery.d.ts +2 -0
  208. package/dist/sdk/discovery.d.ts.map +1 -0
  209. package/dist/sdk/discovery.js +25 -0
  210. package/dist/sdk/discovery.js.map +1 -0
  211. package/dist/sdk/events.d.ts +168 -0
  212. package/dist/sdk/events.d.ts.map +1 -0
  213. package/dist/sdk/events.js +52 -0
  214. package/dist/sdk/events.js.map +1 -0
  215. package/dist/sdk/index.d.ts +17 -0
  216. package/dist/sdk/index.d.ts.map +1 -0
  217. package/dist/sdk/index.js +17 -0
  218. package/dist/sdk/index.js.map +1 -0
  219. package/dist/sdk/runner/AgentRunner.d.ts +22 -0
  220. package/dist/sdk/runner/AgentRunner.d.ts.map +1 -0
  221. package/dist/sdk/runner/AgentRunner.js +222 -0
  222. package/dist/sdk/runner/AgentRunner.js.map +1 -0
  223. package/dist/sdk/runner/finalize.d.ts +9 -0
  224. package/dist/sdk/runner/finalize.d.ts.map +1 -0
  225. package/dist/sdk/runner/finalize.js +115 -0
  226. package/dist/sdk/runner/finalize.js.map +1 -0
  227. package/dist/sdk/runner/formats.d.ts +7 -0
  228. package/dist/sdk/runner/formats.d.ts.map +1 -0
  229. package/dist/sdk/runner/formats.js +91 -0
  230. package/dist/sdk/runner/formats.js.map +1 -0
  231. package/dist/sdk/runner/index.d.ts +9 -0
  232. package/dist/sdk/runner/index.d.ts.map +1 -0
  233. package/dist/sdk/runner/index.js +9 -0
  234. package/dist/sdk/runner/index.js.map +1 -0
  235. package/dist/sdk/runner/progress.d.ts +3 -0
  236. package/dist/sdk/runner/progress.d.ts.map +1 -0
  237. package/dist/sdk/runner/progress.js +16 -0
  238. package/dist/sdk/runner/progress.js.map +1 -0
  239. package/dist/sdk/schemas.d.ts +50 -0
  240. package/dist/sdk/schemas.d.ts.map +1 -0
  241. package/dist/sdk/schemas.js +145 -0
  242. package/dist/sdk/schemas.js.map +1 -0
  243. package/dist/session/SessionContext.d.ts +86 -0
  244. package/dist/session/SessionContext.d.ts.map +1 -0
  245. package/dist/session/SessionContext.js +395 -0
  246. package/dist/session/SessionContext.js.map +1 -0
  247. package/dist/session/environment.d.ts +42 -0
  248. package/dist/session/environment.d.ts.map +1 -0
  249. package/dist/session/environment.js +27 -0
  250. package/dist/session/environment.js.map +1 -0
  251. package/dist/session/history.d.ts +3 -0
  252. package/dist/session/history.d.ts.map +1 -0
  253. package/dist/session/history.js +67 -0
  254. package/dist/session/history.js.map +1 -0
  255. package/dist/session/index.d.ts +10 -0
  256. package/dist/session/index.d.ts.map +1 -0
  257. package/dist/session/index.js +9 -0
  258. package/dist/session/index.js.map +1 -0
  259. package/dist/session/serverData.d.ts +38 -0
  260. package/dist/session/serverData.d.ts.map +1 -0
  261. package/dist/session/serverData.js +241 -0
  262. package/dist/session/serverData.js.map +1 -0
  263. package/dist/tracking/Tracker.d.ts +55 -0
  264. package/dist/tracking/Tracker.d.ts.map +1 -0
  265. package/dist/tracking/Tracker.js +217 -0
  266. package/dist/tracking/Tracker.js.map +1 -0
  267. package/dist/tracking/index.d.ts +8 -0
  268. package/dist/tracking/index.d.ts.map +1 -0
  269. package/dist/tracking/index.js +8 -0
  270. package/dist/tracking/index.js.map +1 -0
  271. package/dist/tracking/schemas.d.ts +292 -0
  272. package/dist/tracking/schemas.d.ts.map +1 -0
  273. package/dist/tracking/schemas.js +91 -0
  274. package/dist/tracking/schemas.js.map +1 -0
  275. package/dist/types.d.ts +4 -0
  276. package/dist/types.d.ts.map +1 -0
  277. package/dist/types.js +2 -0
  278. package/dist/types.js.map +1 -0
  279. package/dist/utils/extractSetFlags.d.ts +6 -0
  280. package/dist/utils/extractSetFlags.d.ts.map +1 -0
  281. package/dist/utils/extractSetFlags.js +16 -0
  282. package/dist/utils/extractSetFlags.js.map +1 -0
  283. package/dist/utils/formatTimeAgo.d.ts +2 -0
  284. package/dist/utils/formatTimeAgo.d.ts.map +1 -0
  285. package/dist/utils/formatTimeAgo.js +20 -0
  286. package/dist/utils/formatTimeAgo.js.map +1 -0
  287. package/dist/utils/index.d.ts +12 -0
  288. package/dist/utils/index.d.ts.map +1 -0
  289. package/dist/utils/index.js +12 -0
  290. package/dist/utils/index.js.map +1 -0
  291. package/dist/utils/machineId.d.ts +14 -0
  292. package/dist/utils/machineId.d.ts.map +1 -0
  293. package/dist/utils/machineId.js +66 -0
  294. package/dist/utils/machineId.js.map +1 -0
  295. package/dist/utils/pathUtils.d.ts +22 -0
  296. package/dist/utils/pathUtils.d.ts.map +1 -0
  297. package/dist/utils/pathUtils.js +54 -0
  298. package/dist/utils/pathUtils.js.map +1 -0
  299. package/dist/version.d.ts +2 -0
  300. package/dist/version.d.ts.map +1 -0
  301. package/dist/version.js +23 -0
  302. package/dist/version.js.map +1 -0
  303. package/package.json +93 -0
@@ -0,0 +1,685 @@
1
+ import WebSocket from "ws";
2
+ import { EventEmitter } from "events";
3
+ import { v4 as uuid } from "uuid";
4
+ import { getAuthConfig } from "../auth/index.js";
5
+ import { ServerData } from "../session/index.js";
6
+ import { getCurrentEnvironment } from "../session/index.js";
7
+ // WebSocket-specific configuration
8
+ const WS_CONFIG = {
9
+ IDLE_TIMEOUT: 600000,
10
+ HEARTBEAT_INTERVAL: 30000,
11
+ HEARTBEAT_TIMEOUT: 60000,
12
+ CONNECTION_TIMEOUT: 15000,
13
+ INITIAL_RECONNECT_DELAY: 1000,
14
+ MAX_RECONNECT_DELAY: 30000,
15
+ RECONNECT_BACKOFF_FACTOR: 1.5,
16
+ MESSAGE_QUEUE_MAX_SIZE: 1000,
17
+ READY_TIMEOUT: 300000,
18
+ };
19
+ export var ConnectionState;
20
+ (function (ConnectionState) {
21
+ ConnectionState["DISCONNECTED"] = "DISCONNECTED";
22
+ ConnectionState["CONNECTING"] = "CONNECTING";
23
+ ConnectionState["CONNECTED"] = "CONNECTED";
24
+ ConnectionState["RECONNECTING"] = "RECONNECTING";
25
+ ConnectionState["DISCONNECTING"] = "DISCONNECTING";
26
+ ConnectionState["FAILED"] = "FAILED";
27
+ })(ConnectionState || (ConnectionState = {}));
28
+ export var ReadyState;
29
+ (function (ReadyState) {
30
+ ReadyState["IDLE"] = "IDLE";
31
+ ReadyState["WAITING_INITIAL_READY"] = "WAITING_INITIAL_READY";
32
+ ReadyState["READY"] = "READY";
33
+ ReadyState["MESSAGE_SENT"] = "MESSAGE_SENT";
34
+ ReadyState["WAITING_READY"] = "WAITING_READY";
35
+ ReadyState["CHECKPOINT_RECOVERY"] = "CHECKPOINT_RECOVERY";
36
+ })(ReadyState || (ReadyState = {}));
37
+ export class WebSocketClient extends EventEmitter {
38
+ ws;
39
+ shouldDebugLog() {
40
+ const env = getCurrentEnvironment();
41
+ // In SDK mode, default is silent unless sdkDebug=true or QODO_DEBUG=true.
42
+ if (env?.sdkMode) {
43
+ return !!env.sdkDebug || process.env.QODO_DEBUG === 'true';
44
+ }
45
+ return process.env.QODO_DEBUG === 'true';
46
+ }
47
+ debug(...args) {
48
+ if (this.shouldDebugLog()) {
49
+ // eslint-disable-next-line no-console
50
+ console.debug(...args);
51
+ }
52
+ }
53
+ state = ConnectionState.DISCONNECTED;
54
+ sessionId;
55
+ requestId;
56
+ connectionId;
57
+ // Idle management
58
+ idleTimer;
59
+ autoDisconnected = false;
60
+ // Heartbeat management
61
+ heartbeatInterval;
62
+ lastPongReceived = Date.now();
63
+ lastPingSent = 0;
64
+ // Reconnection management
65
+ reconnectAttempts = 0;
66
+ reconnectTimer;
67
+ isReconnecting = false;
68
+ // Message queue for handling during disconnection
69
+ messageQueue = [];
70
+ isProcessingQueue = false;
71
+ // Ready protocol state management
72
+ readyState = ReadyState.IDLE;
73
+ latestCheckpointId;
74
+ lastSentMessage;
75
+ readyTimer;
76
+ pendingOutbox = [];
77
+ receivedResponsesSinceMessage = false; // Track if any responses received after sending message
78
+ hasReceivedResponsesSinceLastMessage() {
79
+ return this.receivedResponsesSinceMessage;
80
+ }
81
+ // Connection-timeout timer (ensure we clear/unref to avoid keeping event loop alive)
82
+ connectionTimeoutTimer;
83
+ constructor() {
84
+ super();
85
+ }
86
+ setState(newState) {
87
+ if (this.state !== newState) {
88
+ const oldState = this.state;
89
+ this.state = newState;
90
+ this.debug(`[WebSocketClient] Connection state transition: ${oldState} → ${newState} | ` +
91
+ `ReadyState: ${this.readyState} | ` +
92
+ `Session: ${this.sessionId?.substring(0, 8)}...`);
93
+ this.emit('stateChanged', newState);
94
+ }
95
+ }
96
+ setReadyState(newState, context) {
97
+ if (this.readyState !== newState) {
98
+ const oldState = this.readyState;
99
+ this.readyState = newState;
100
+ this.debug(`[WebSocketClient] Ready state transition: ${oldState} → ${newState}` +
101
+ (context ? ` | Context: ${context}` : '') +
102
+ ` | Session: ${this.sessionId?.substring(0, 8)}...` +
103
+ ` | Checkpoint: ${this.latestCheckpointId?.substring(0, 8) || 'none'}`);
104
+ this.emit('readyStateChanged', { oldState, newState, context });
105
+ }
106
+ }
107
+ async handleReadyMessage(message) {
108
+ const checkpointId = message.data?.tool_args?.checkpoint_id;
109
+ const previousCheckpoint = this.latestCheckpointId;
110
+ const previousReadyState = this.readyState;
111
+ this.debug(`[WebSocketClient] Ready message received | ` +
112
+ `State: ${this.readyState} | ` +
113
+ `Checkpoint: ${checkpointId?.substring(0, 8) || 'none'} | ` +
114
+ `Previous: ${previousCheckpoint?.substring(0, 8) || 'none'}`);
115
+ // Update checkpoint if provided and changed
116
+ if (checkpointId && checkpointId !== previousCheckpoint) {
117
+ this.latestCheckpointId = checkpointId;
118
+ this.debug(`[WebSocketClient] Checkpoint ID updated: ${previousCheckpoint?.substring(0, 8) || 'none'} → ` +
119
+ `${checkpointId.substring(0, 8)}`);
120
+ }
121
+ // Handle based on current state
122
+ switch (this.readyState) {
123
+ case ReadyState.WAITING_INITIAL_READY:
124
+ this.clearReadyTimer();
125
+ this.setReadyState(ReadyState.READY, 'Initial Ready received after connection');
126
+ await this.processOutbox();
127
+ break;
128
+ case ReadyState.WAITING_READY:
129
+ this.clearReadyTimer();
130
+ this.setReadyState(ReadyState.READY, 'Ready received after responses completed');
131
+ await this.processOutbox();
132
+ break;
133
+ case ReadyState.MESSAGE_SENT:
134
+ // Ready in MESSAGE_SENT should only be accepted if responses were received
135
+ if (!this.receivedResponsesSinceMessage) {
136
+ this.debug(`[WebSocketClient] Unexpected Ready without responses | ` +
137
+ `Checkpoint: ${checkpointId?.substring(0, 8) || 'none'} | ` +
138
+ `This indicates server failed to process the message`);
139
+ // Trigger checkpoint recovery - server acknowledged but didn't process
140
+ this.clearReadyTimer();
141
+ await this.initiateCheckpointRecovery('Ready received without any responses (server processing failure)');
142
+ return;
143
+ }
144
+ // Normal case: Ready after responses signals server finished processing
145
+ this.debug('[WebSocketClient] Ready received, responses complete');
146
+ this.clearReadyTimer();
147
+ this.setReadyState(ReadyState.READY, 'Ready received, server finished processing');
148
+ await this.processOutbox();
149
+ break;
150
+ case ReadyState.READY:
151
+ this.debug('[WebSocketClient] Ready received while already in READY state (duplicate)');
152
+ break;
153
+ case ReadyState.IDLE:
154
+ this.debug('[WebSocketClient] Ready received while IDLE - transitioning to READY');
155
+ this.setReadyState(ReadyState.READY, 'Ready received while idle');
156
+ break;
157
+ case ReadyState.CHECKPOINT_RECOVERY:
158
+ this.debug('[WebSocketClient] Ready received during recovery - resuming normal flow');
159
+ this.clearReadyTimer();
160
+ this.setReadyState(ReadyState.READY, 'Recovery completed, Ready received');
161
+ await this.processOutbox();
162
+ break;
163
+ }
164
+ // Emit event for monitoring.
165
+ // Include previous ready state so listeners can distinguish initial Ready
166
+ // from completion Ready after MESSAGE_SENT.
167
+ this.emit('readyReceived', checkpointId, previousReadyState);
168
+ }
169
+ async connect(sessionId, requestId) {
170
+ // Update session info if provided
171
+ if (sessionId)
172
+ this.sessionId = sessionId;
173
+ if (requestId)
174
+ this.requestId = requestId;
175
+ if (this.state === ConnectionState.CONNECTED) {
176
+ this.resetIdleTimer();
177
+ return;
178
+ }
179
+ // If we auto-disconnected due to idle, reconnect
180
+ if (this.autoDisconnected || this.state === ConnectionState.DISCONNECTED) {
181
+ this.autoDisconnected = false;
182
+ await this.establishConnection();
183
+ }
184
+ }
185
+ async sendMessage(type, data) {
186
+ // Reset idle timer on any outgoing message
187
+ this.resetIdleTimer();
188
+ const messageToSend = { type, data, timestamp: Date.now() };
189
+ this.debug(`[WebSocketClient] sendMessage called | ` +
190
+ `Type: ${type} | ` +
191
+ `ReadyState: ${this.readyState} | ` +
192
+ `ConnectionState: ${this.state}`);
193
+ // Save as last sent message for potential recovery
194
+ this.lastSentMessage = messageToSend;
195
+ this.debug(`[WebSocketClient] Last sent message cached | ` +
196
+ `Type: ${type} | ` +
197
+ `Timestamp: ${messageToSend.timestamp}`);
198
+ if (this.readyState === ReadyState.READY && this.ws && this.state === ConnectionState.CONNECTED) {
199
+ // Send immediately
200
+ this.receivedResponsesSinceMessage = false; // Reset flag when sending new message
201
+ await this.sendImmediately(messageToSend);
202
+ this.setReadyState(ReadyState.MESSAGE_SENT, `Sent ${type} message`);
203
+ this.startReadyTimer(); // Expect Ready after server processes message
204
+ }
205
+ else {
206
+ // Queue until Ready
207
+ this.pendingOutbox.push(messageToSend);
208
+ this.debug(`[WebSocketClient] Message queued | ` +
209
+ `Type: ${type} | ` +
210
+ `Queue size: ${this.pendingOutbox.length} | ` +
211
+ `Reason: ReadyState=${this.readyState}, Connected=${this.state === ConnectionState.CONNECTED}`);
212
+ if (this.state === ConnectionState.DISCONNECTED) {
213
+ this.debug('[WebSocketClient] Initiating connection due to queued message');
214
+ await this.connect();
215
+ }
216
+ }
217
+ }
218
+ async sendImmediately(msg) {
219
+ const formatted = this.formatMessage(msg.type, msg.data);
220
+ this.ws.send(formatted);
221
+ this.debug(`[WebSocketClient] Message sent immediately | ` +
222
+ `Type: ${msg.type} | ` +
223
+ `Size: ${formatted.length} bytes`);
224
+ }
225
+ async processOutbox() {
226
+ this.debug(`[WebSocketClient] Processing outbox | ` +
227
+ `Queue size: ${this.pendingOutbox.length} | ` +
228
+ `ReadyState: ${this.readyState}`);
229
+ if (this.pendingOutbox.length > 0 && this.readyState === ReadyState.READY) {
230
+ const msg = this.pendingOutbox.shift();
231
+ this.debug(`[WebSocketClient] Sending queued message | ` +
232
+ `Type: ${msg.type} | ` +
233
+ `Queued at: ${msg.timestamp} | ` +
234
+ `Wait time: ${Date.now() - msg.timestamp}ms | ` +
235
+ `Remaining in queue: ${this.pendingOutbox.length}`);
236
+ this.receivedResponsesSinceMessage = false; // Reset flag when sending queued message
237
+ await this.sendImmediately(msg);
238
+ this.setReadyState(ReadyState.MESSAGE_SENT, `Sent queued ${msg.type} message`);
239
+ this.startReadyTimer(); // Expect Ready after server processes message
240
+ }
241
+ else if (this.pendingOutbox.length === 0) {
242
+ this.debug('[WebSocketClient] Outbox is empty, no messages to process');
243
+ }
244
+ else {
245
+ this.debug(`[WebSocketClient] Cannot process outbox | ` +
246
+ `ReadyState: ${this.readyState} (expected READY)`);
247
+ }
248
+ }
249
+ startReadyTimer() {
250
+ this.clearReadyTimer();
251
+ this.readyTimer = setTimeout(async () => {
252
+ this.debug(`[WebSocketClient] Ready timeout expired | ` +
253
+ `State: ${this.readyState} | ` +
254
+ `Last message: ${this.lastSentMessage?.type} | ` +
255
+ `Checkpoint: ${this.latestCheckpointId?.substring(0, 8) || 'none'}`);
256
+ await this.initiateCheckpointRecovery('Ready timeout');
257
+ }, WS_CONFIG.READY_TIMEOUT);
258
+ // Do not keep the event loop alive solely because of this timer
259
+ this.readyTimer.unref?.();
260
+ }
261
+ clearReadyTimer() {
262
+ if (this.readyTimer) {
263
+ clearTimeout(this.readyTimer);
264
+ this.readyTimer = undefined;
265
+ }
266
+ }
267
+ async initiateCheckpointRecovery(reason) {
268
+ this.debug(`[WebSocketClient] ===== CHECKPOINT RECOVERY INITIATED ===== | ` +
269
+ `Reason: ${reason} | ` +
270
+ `Checkpoint: ${this.latestCheckpointId?.substring(0, 8) || 'none'} | ` +
271
+ `Last message type: ${this.lastSentMessage?.type} | ` +
272
+ `Current state: ${this.readyState}`);
273
+ this.setReadyState(ReadyState.CHECKPOINT_RECOVERY, reason);
274
+ // Disconnect current connection
275
+ this.debug('[WebSocketClient] Disconnecting for checkpoint recovery');
276
+ this.disconnect();
277
+ // Reconnect with checkpoint (URL will include checkpoint_id)
278
+ this.debug(`[WebSocketClient] Reconnecting with checkpoint | ` +
279
+ `Checkpoint ID: ${this.latestCheckpointId?.substring(0, 8) || 'none'} | ` +
280
+ `Session ID: ${this.sessionId?.substring(0, 8)}...`);
281
+ try {
282
+ await this.establishConnection();
283
+ this.debug('[WebSocketClient] Reconnection successful during recovery');
284
+ // Wait for Ready state is set in handleReadyMessage when initial Ready is received
285
+ // Resend last message once Ready received
286
+ if (this.lastSentMessage) {
287
+ this.pendingOutbox.unshift(this.lastSentMessage);
288
+ this.debug(`[WebSocketClient] Last message re-queued for recovery | ` +
289
+ `Type: ${this.lastSentMessage.type} | ` +
290
+ `Original timestamp: ${this.lastSentMessage.timestamp}`);
291
+ }
292
+ else {
293
+ this.debug('[WebSocketClient] No last message to resend during recovery');
294
+ }
295
+ // Emit event for monitoring
296
+ this.emit('checkpointRecovery', { reason, checkpoint: this.latestCheckpointId });
297
+ }
298
+ catch (error) {
299
+ this.debug(`[WebSocketClient] Checkpoint recovery failed | ` +
300
+ `Error: ${error instanceof Error ? error.message : error}`);
301
+ this.setReadyState(ReadyState.IDLE, 'Recovery failed');
302
+ throw error;
303
+ }
304
+ this.debug('[WebSocketClient] ===== CHECKPOINT RECOVERY COMPLETED =====');
305
+ }
306
+ disconnect() {
307
+ // Hold a reference to the current socket to close it safely
308
+ const wsRef = this.ws;
309
+ // Stop all timers and reconnection attempts
310
+ this.clearTimers();
311
+ this.autoDisconnected = false;
312
+ this.isReconnecting = false;
313
+ // Invalidate the current connection so any late events from wsRef are ignored
314
+ // Event handlers compare this.connectionId to their captured id
315
+ this.connectionId = uuid();
316
+ if (wsRef) {
317
+ try {
318
+ // Attempt a graceful close when open; otherwise force terminate to avoid races
319
+ if (wsRef.readyState === WebSocket.OPEN) {
320
+ this.setState(ConnectionState.DISCONNECTING);
321
+ wsRef.close(1000, 'Normal closure');
322
+ }
323
+ else if (wsRef.readyState === WebSocket.CONNECTING || wsRef.readyState === WebSocket.CLOSING) {
324
+ // Avoid an "open" event racing in after manual disconnect
325
+ try {
326
+ wsRef.terminate?.();
327
+ }
328
+ catch { }
329
+ }
330
+ }
331
+ catch { }
332
+ }
333
+ // Explicitly mark as disconnected and reset internal refs
334
+ this.setState(ConnectionState.DISCONNECTED);
335
+ this.ws = undefined;
336
+ this.setReadyState(ReadyState.IDLE, 'Manual disconnect');
337
+ this.lastPingSent = 0;
338
+ }
339
+ getState() {
340
+ return this.state;
341
+ }
342
+ keepAlive() {
343
+ if (this.state === ConnectionState.CONNECTED) {
344
+ this.resetIdleTimer();
345
+ }
346
+ }
347
+ // === Private Methods ===
348
+ async establishConnection() {
349
+ if (this.state === ConnectionState.CONNECTING) {
350
+ await this.waitForConnection();
351
+ return;
352
+ }
353
+ return new Promise((resolve, reject) => {
354
+ try {
355
+ this.setState(ConnectionState.CONNECTING);
356
+ this.connectionId = uuid();
357
+ const currentConnectionId = this.connectionId;
358
+ const wsUrl = this.buildWebSocketUrl();
359
+ const { token } = getAuthConfig();
360
+ this.debug(`Connecting to WebSocket: ${wsUrl}`);
361
+ const ws = new WebSocket(wsUrl, {
362
+ headers: {
363
+ 'Authorization': `Bearer ${token}`,
364
+ },
365
+ handshakeTimeout: WS_CONFIG.CONNECTION_TIMEOUT,
366
+ });
367
+ this.ws = ws;
368
+ // Set up event handlers
369
+ ws.on('open', () => {
370
+ if (this.connectionId !== currentConnectionId) {
371
+ ws.close();
372
+ return;
373
+ }
374
+ // Clear pending connection timeout timer if any
375
+ if (this.connectionTimeoutTimer) {
376
+ clearTimeout(this.connectionTimeoutTimer);
377
+ this.connectionTimeoutTimer = undefined;
378
+ }
379
+ this.setState(ConnectionState.CONNECTED);
380
+ this.reconnectAttempts = 0;
381
+ this.emit('connected');
382
+ this.debug(`[WebSocketClient] WebSocket connected | ` +
383
+ `Session: ${this.sessionId?.substring(0, 8)}... | ` +
384
+ `Checkpoint: ${this.latestCheckpointId?.substring(0, 8) || 'none'}`);
385
+ // Transition to waiting for initial Ready
386
+ this.setReadyState(ReadyState.WAITING_INITIAL_READY, 'Connection opened, awaiting initial Ready');
387
+ this.startReadyTimer(); // Wait for initial Ready
388
+ // Start idle timer immediately upon connection
389
+ this.startIdleTimer();
390
+ // Start heartbeat
391
+ this.startHeartbeat();
392
+ // Process any queued messages (old mechanism, will be replaced by pendingOutbox)
393
+ this.processQueuedMessages();
394
+ this.debug('[WebSocketClient] Connection established, waiting for initial Ready signal');
395
+ resolve();
396
+ });
397
+ ws.on('message', (data) => {
398
+ if (this.connectionId !== currentConnectionId)
399
+ return;
400
+ this.resetIdleTimer();
401
+ this.lastPongReceived = Date.now(); // any message is also a ping
402
+ const message = data.toString();
403
+ // Try to detect Ready messages
404
+ try {
405
+ const parsed = JSON.parse(message);
406
+ if (parsed && parsed.data && parsed.data.tool === 'Ready') {
407
+ // Handle Ready message separately
408
+ this.handleReadyMessage(parsed);
409
+ return;
410
+ }
411
+ }
412
+ catch (e) {
413
+ // Not JSON or not a Ready message, continue with normal flow
414
+ }
415
+ // Track that we received a non-Ready message (response)
416
+ if (this.readyState === ReadyState.MESSAGE_SENT) {
417
+ this.receivedResponsesSinceMessage = true;
418
+ }
419
+ // Emit non-Ready messages to AgentAPI
420
+ this.emit('message', message);
421
+ });
422
+ ws.on('close', (code, reason) => {
423
+ if (this.connectionId !== currentConnectionId)
424
+ return;
425
+ // Clear pending connection-timeout timer
426
+ if (this.connectionTimeoutTimer) {
427
+ clearTimeout(this.connectionTimeoutTimer);
428
+ this.connectionTimeoutTimer = undefined;
429
+ }
430
+ this.handleClose(code, reason.toString());
431
+ if (this.state === ConnectionState.CONNECTING) {
432
+ reject(new Error(`Connection closed during setup: ${code} ${reason}`));
433
+ }
434
+ });
435
+ ws.on('error', (error) => {
436
+ if (this.connectionId !== currentConnectionId)
437
+ return;
438
+ // Clear pending connection-timeout timer
439
+ if (this.connectionTimeoutTimer) {
440
+ clearTimeout(this.connectionTimeoutTimer);
441
+ this.connectionTimeoutTimer = undefined;
442
+ }
443
+ this.debug('WebSocket error:', error);
444
+ this.emit('error', error);
445
+ if (this.state === ConnectionState.CONNECTING) {
446
+ reject(error);
447
+ }
448
+ });
449
+ ws.on('pong', () => {
450
+ if (this.connectionId === currentConnectionId) {
451
+ this.lastPongReceived = Date.now();
452
+ this.debug('Pong received, heartbeat OK');
453
+ }
454
+ });
455
+ // Connection timeout
456
+ this.connectionTimeoutTimer = setTimeout(() => {
457
+ if (this.connectionId === currentConnectionId && this.state === ConnectionState.CONNECTING) {
458
+ ws.close();
459
+ reject(new Error('Connection timeout'));
460
+ }
461
+ }, WS_CONFIG.CONNECTION_TIMEOUT);
462
+ this.connectionTimeoutTimer.unref?.();
463
+ }
464
+ catch (error) {
465
+ this.setState(ConnectionState.FAILED);
466
+ reject(error);
467
+ }
468
+ });
469
+ }
470
+ async waitForConnection(timeout = WS_CONFIG.CONNECTION_TIMEOUT) {
471
+ const startTime = Date.now();
472
+ while (this.state === ConnectionState.CONNECTING || this.state === ConnectionState.RECONNECTING) {
473
+ if (Date.now() - startTime > timeout) {
474
+ throw new Error('Connection timeout while waiting');
475
+ }
476
+ await new Promise(resolve => setTimeout(resolve, 100));
477
+ }
478
+ if (this.state !== ConnectionState.CONNECTED) {
479
+ throw new Error('Failed to establish connection');
480
+ }
481
+ }
482
+ buildWebSocketUrl() {
483
+ const baseUrl = ServerData.getInstance().getBaseUrl();
484
+ const wsUrl = baseUrl.replace(/^http/, 'ws');
485
+ const trimmedBase = wsUrl.replace(/\/+$/, '');
486
+ const url = new URL(`${trimmedBase}/v2/agentic/ws/connect`);
487
+ // Use current session ID, or generate one if not set
488
+ const currentSessionId = this.sessionId || uuid();
489
+ url.searchParams.set('session_id', currentSessionId);
490
+ if (this.requestId) {
491
+ url.searchParams.set('request_id', this.requestId);
492
+ }
493
+ // Include checkpoint_id if available for recovery
494
+ if (this.latestCheckpointId && this.readyState === ReadyState.CHECKPOINT_RECOVERY) {
495
+ url.searchParams.set('checkpoint_id', this.latestCheckpointId);
496
+ this.debug(`[WebSocketClient] Building URL with checkpoint | ` +
497
+ `Checkpoint: ${this.latestCheckpointId.substring(0, 8)}...`);
498
+ }
499
+ else {
500
+ this.debug('[WebSocketClient] Building URL without checkpoint (fresh connection)');
501
+ }
502
+ return url.toString();
503
+ }
504
+ formatMessage(type, data) {
505
+ return `${type} ${JSON.stringify(data)}\n`;
506
+ }
507
+ handleClose(code, reason) {
508
+ this.clearTimers();
509
+ if (this.autoDisconnected) {
510
+ this.debug('WebSocket closed due to idle timeout');
511
+ this.setState(ConnectionState.DISCONNECTED);
512
+ this.emit('disconnected', { reason: 'idle', code });
513
+ return;
514
+ }
515
+ this.debug(`WebSocket closed: ${code} ${reason}`);
516
+ // Determine if we should reconnect
517
+ if (code === 1000 || code === 1001) {
518
+ // Normal closure
519
+ this.setState(ConnectionState.DISCONNECTED);
520
+ this.emit('disconnected', { reason: 'normal', code });
521
+ }
522
+ else if (code === 1008) {
523
+ // Authentication failure - don't reconnect
524
+ this.setState(ConnectionState.FAILED);
525
+ this.emit('error', new Error('Authentication failed'));
526
+ }
527
+ else {
528
+ // Unexpected closure - attempt reconnect
529
+ this.setState(ConnectionState.FAILED);
530
+ this.attemptReconnection().catch((error) => {
531
+ this.debug('Reconnection attempt failed:', error instanceof Error ? error.message : error);
532
+ });
533
+ }
534
+ }
535
+ async attemptReconnection() {
536
+ if (this.isReconnecting)
537
+ return;
538
+ this.isReconnecting = true;
539
+ this.setState(ConnectionState.RECONNECTING);
540
+ this.reconnectAttempts++;
541
+ const delay = this.calculateBackoffDelay(this.reconnectAttempts);
542
+ this.debug(`Attempting reconnection ${this.reconnectAttempts} in ${delay}ms`);
543
+ this.reconnectTimer = setTimeout(async () => {
544
+ try {
545
+ await this.establishConnection();
546
+ this.isReconnecting = false;
547
+ this.emit('reconnected');
548
+ }
549
+ catch (error) {
550
+ this.debug('Reconnection failed:', error instanceof Error ? error.message : error);
551
+ this.isReconnecting = false;
552
+ // Infinite retry - always attempt again
553
+ setTimeout(() => this.attemptReconnection(), 1000);
554
+ }
555
+ }, delay);
556
+ this.reconnectTimer.unref?.();
557
+ }
558
+ calculateBackoffDelay(attempt) {
559
+ const delay = WS_CONFIG.INITIAL_RECONNECT_DELAY *
560
+ Math.pow(WS_CONFIG.RECONNECT_BACKOFF_FACTOR, attempt - 1);
561
+ const jitter = delay * 0.25 * (Math.random() - 0.5);
562
+ return Math.min(delay + jitter, WS_CONFIG.MAX_RECONNECT_DELAY);
563
+ }
564
+ // === Idle Timer Management ===
565
+ startIdleTimer() {
566
+ this.clearIdleTimer();
567
+ this.idleTimer = setTimeout(() => {
568
+ this.debug('Connection idle for 10 minutes, disconnecting...');
569
+ this.autoDisconnected = true;
570
+ // Clean disconnect - ready for next request
571
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
572
+ this.ws.close(1000, 'Idle timeout');
573
+ }
574
+ this.setState(ConnectionState.DISCONNECTED);
575
+ this.emit('disconnected', { reason: 'idle' });
576
+ }, WS_CONFIG.IDLE_TIMEOUT);
577
+ // Let process exit if this is the only pending timer
578
+ this.idleTimer.unref?.();
579
+ }
580
+ clearIdleTimer() {
581
+ if (this.idleTimer) {
582
+ clearTimeout(this.idleTimer);
583
+ this.idleTimer = undefined;
584
+ }
585
+ }
586
+ resetIdleTimer() {
587
+ this.startIdleTimer();
588
+ }
589
+ // === Heartbeat Management ===
590
+ startHeartbeat() {
591
+ if (this.heartbeatInterval) {
592
+ clearInterval(this.heartbeatInterval);
593
+ }
594
+ this.heartbeatInterval = setInterval(() => {
595
+ if (this.state === ConnectionState.CONNECTED && this.ws) {
596
+ const now = Date.now();
597
+ const timeSinceLastPong = now - this.lastPongReceived;
598
+ // If we haven't sent a ping recently, or if it's time to send another one
599
+ if (this.lastPingSent === 0 || (now - this.lastPingSent) >= WS_CONFIG.HEARTBEAT_INTERVAL) {
600
+ try {
601
+ this.ws.ping();
602
+ this.lastPingSent = now;
603
+ this.debug('Ping sent, waiting for pong...');
604
+ }
605
+ catch (error) {
606
+ this.debug('Error sending ping:', error);
607
+ }
608
+ }
609
+ // If we sent a ping and enough time has passed without a pong, timeout
610
+ else if (this.lastPingSent > 0 && timeSinceLastPong > WS_CONFIG.HEARTBEAT_TIMEOUT) {
611
+ this.debug('Heartbeat timeout, closing connection');
612
+ this.ws.close();
613
+ return;
614
+ }
615
+ }
616
+ }, WS_CONFIG.HEARTBEAT_INTERVAL);
617
+ // Allow process to exit if heartbeat is the last thing running
618
+ this.heartbeatInterval.unref?.();
619
+ }
620
+ stopHeartbeat() {
621
+ if (this.heartbeatInterval) {
622
+ clearInterval(this.heartbeatInterval);
623
+ this.heartbeatInterval = undefined;
624
+ }
625
+ }
626
+ async processQueuedMessages() {
627
+ if (this.isProcessingQueue || this.messageQueue.length === 0)
628
+ return;
629
+ this.isProcessingQueue = true;
630
+ this.debug(`Processing ${this.messageQueue.length} queued messages`);
631
+ try {
632
+ while (this.messageQueue.length > 0 && this.state === ConnectionState.CONNECTED) {
633
+ const queuedMessage = this.messageQueue.shift();
634
+ try {
635
+ const message = this.formatMessage(queuedMessage.type, queuedMessage.data);
636
+ this.ws.send(message);
637
+ this.debug(`Sent queued ${queuedMessage.type} message`);
638
+ }
639
+ catch (error) {
640
+ console.error('Error processing queued message:', error);
641
+ if (queuedMessage.retryCount < 3) {
642
+ queuedMessage.retryCount++;
643
+ this.messageQueue.unshift(queuedMessage);
644
+ break;
645
+ }
646
+ }
647
+ }
648
+ }
649
+ finally {
650
+ this.isProcessingQueue = false;
651
+ }
652
+ }
653
+ // === Cleanup ===
654
+ clearTimers() {
655
+ this.clearIdleTimer();
656
+ this.stopHeartbeat();
657
+ this.clearReadyTimer();
658
+ if (this.reconnectTimer) {
659
+ clearTimeout(this.reconnectTimer);
660
+ this.reconnectTimer = undefined;
661
+ }
662
+ if (this.connectionTimeoutTimer) {
663
+ clearTimeout(this.connectionTimeoutTimer);
664
+ this.connectionTimeoutTimer = undefined;
665
+ }
666
+ }
667
+ cleanup() {
668
+ this.debug(`[WebSocketClient] Cleanup called | ` +
669
+ `State: ${this.state} | ` +
670
+ `ReadyState: ${this.readyState} | ` +
671
+ `Queued messages: ${this.messageQueue.length} | ` +
672
+ `Pending outbox: ${this.pendingOutbox.length}`);
673
+ this.clearTimers();
674
+ this.disconnect();
675
+ this.messageQueue = [];
676
+ this.pendingOutbox = [];
677
+ this.isReconnecting = false;
678
+ this.latestCheckpointId = undefined;
679
+ this.lastSentMessage = undefined;
680
+ this.setReadyState(ReadyState.IDLE, 'Cleanup performed');
681
+ this.removeAllListeners();
682
+ this.debug('[WebSocketClient] Cleanup completed');
683
+ }
684
+ }
685
+ //# sourceMappingURL=websocket.js.map