@kadi.build/core 0.0.1-alpha.1 → 0.0.1-alpha.11

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 (199) hide show
  1. package/README.md +361 -230
  2. package/dist/abilities/AbilityCache.d.ts +242 -0
  3. package/dist/abilities/AbilityCache.d.ts.map +1 -0
  4. package/dist/abilities/AbilityCache.js +285 -0
  5. package/dist/abilities/AbilityCache.js.map +1 -0
  6. package/dist/abilities/AbilityContext.d.ts +215 -0
  7. package/dist/abilities/AbilityContext.d.ts.map +1 -0
  8. package/dist/abilities/AbilityContext.js +36 -0
  9. package/dist/abilities/AbilityContext.js.map +1 -0
  10. package/dist/abilities/AbilityLoader.d.ts +203 -0
  11. package/dist/abilities/AbilityLoader.d.ts.map +1 -0
  12. package/dist/abilities/AbilityLoader.js +343 -0
  13. package/dist/abilities/AbilityLoader.js.map +1 -0
  14. package/dist/abilities/AbilityProxy.d.ts +496 -0
  15. package/dist/abilities/AbilityProxy.d.ts.map +1 -0
  16. package/dist/abilities/AbilityProxy.js +551 -0
  17. package/dist/abilities/AbilityProxy.js.map +1 -0
  18. package/dist/abilities/AbilityValidator.d.ts +172 -0
  19. package/dist/abilities/AbilityValidator.d.ts.map +1 -0
  20. package/dist/abilities/AbilityValidator.js +253 -0
  21. package/dist/abilities/AbilityValidator.js.map +1 -0
  22. package/dist/abilities/index.d.ts +26 -0
  23. package/dist/abilities/index.d.ts.map +1 -0
  24. package/dist/abilities/index.js +23 -0
  25. package/dist/abilities/index.js.map +1 -0
  26. package/dist/abilities/types.d.ts +223 -0
  27. package/dist/abilities/types.d.ts.map +1 -0
  28. package/dist/abilities/types.js +10 -0
  29. package/dist/abilities/types.js.map +1 -0
  30. package/dist/api/index.d.ts +92 -0
  31. package/dist/api/index.d.ts.map +1 -0
  32. package/dist/api/index.js +124 -0
  33. package/dist/api/index.js.map +1 -0
  34. package/dist/broker/BrokerConnection.d.ts +253 -0
  35. package/dist/broker/BrokerConnection.d.ts.map +1 -0
  36. package/dist/broker/BrokerConnection.js +434 -0
  37. package/dist/broker/BrokerConnection.js.map +1 -0
  38. package/dist/broker/BrokerConnectionManager.d.ts +216 -0
  39. package/dist/broker/BrokerConnectionManager.d.ts.map +1 -0
  40. package/dist/broker/BrokerConnectionManager.js +305 -0
  41. package/dist/broker/BrokerConnectionManager.js.map +1 -0
  42. package/dist/broker/BrokerProtocol.d.ts +280 -0
  43. package/dist/broker/BrokerProtocol.d.ts.map +1 -0
  44. package/dist/broker/BrokerProtocol.js +466 -0
  45. package/dist/broker/BrokerProtocol.js.map +1 -0
  46. package/dist/broker/index.d.ts +9 -0
  47. package/dist/broker/index.d.ts.map +1 -0
  48. package/dist/broker/index.js +9 -0
  49. package/dist/broker/index.js.map +1 -0
  50. package/dist/client/KadiClient.d.ts +459 -0
  51. package/dist/client/KadiClient.d.ts.map +1 -0
  52. package/dist/client/KadiClient.js +902 -0
  53. package/dist/client/KadiClient.js.map +1 -0
  54. package/dist/client/index.d.ts +7 -0
  55. package/dist/client/index.d.ts.map +1 -0
  56. package/dist/client/index.js +7 -0
  57. package/dist/client/index.js.map +1 -0
  58. package/dist/config/ConfigLoader.d.ts +138 -0
  59. package/dist/config/ConfigLoader.d.ts.map +1 -0
  60. package/dist/config/ConfigLoader.js +226 -0
  61. package/dist/config/ConfigLoader.js.map +1 -0
  62. package/dist/config/ConfigResolver.d.ts +135 -0
  63. package/dist/config/ConfigResolver.d.ts.map +1 -0
  64. package/dist/config/ConfigResolver.js +282 -0
  65. package/dist/config/ConfigResolver.js.map +1 -0
  66. package/dist/config/index.d.ts +8 -0
  67. package/dist/config/index.d.ts.map +1 -0
  68. package/dist/config/index.js +8 -0
  69. package/dist/config/index.js.map +1 -0
  70. package/dist/errors/index.d.ts +9 -0
  71. package/dist/errors/index.d.ts.map +1 -0
  72. package/dist/errors/index.js +8 -0
  73. package/dist/errors/index.js.map +1 -0
  74. package/dist/events/EventHub.d.ts +172 -0
  75. package/dist/events/EventHub.d.ts.map +1 -0
  76. package/dist/events/EventHub.js +333 -0
  77. package/dist/events/EventHub.js.map +1 -0
  78. package/dist/events/index.d.ts +7 -0
  79. package/dist/events/index.d.ts.map +1 -0
  80. package/dist/events/index.js +7 -0
  81. package/dist/events/index.js.map +1 -0
  82. package/dist/index.d.ts +50 -0
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/index.js +67 -0
  85. package/dist/index.js.map +1 -0
  86. package/dist/messages/index.d.ts +33 -0
  87. package/dist/messages/index.d.ts.map +1 -0
  88. package/dist/messages/index.js +33 -0
  89. package/dist/messages/index.js.map +1 -0
  90. package/dist/schemas/index.d.ts +22 -0
  91. package/dist/schemas/index.d.ts.map +1 -0
  92. package/dist/schemas/index.js +27 -0
  93. package/dist/schemas/index.js.map +1 -0
  94. package/dist/schemas/kadi-extensions.d.ts +231 -0
  95. package/dist/schemas/kadi-extensions.d.ts.map +1 -0
  96. package/dist/schemas/kadi-extensions.js +14 -0
  97. package/dist/schemas/kadi-extensions.js.map +1 -0
  98. package/dist/schemas/mcp/schema.d.ts +1399 -0
  99. package/dist/schemas/mcp/schema.d.ts.map +1 -0
  100. package/dist/schemas/mcp/schema.js +53 -0
  101. package/dist/schemas/mcp/schema.js.map +1 -0
  102. package/dist/schemas/mcp/version.d.ts +37 -0
  103. package/dist/schemas/mcp/version.d.ts.map +1 -0
  104. package/dist/schemas/mcp/version.js +39 -0
  105. package/dist/schemas/mcp/version.js.map +1 -0
  106. package/dist/schemas/schema-builders.d.ts +178 -0
  107. package/dist/schemas/schema-builders.d.ts.map +1 -0
  108. package/dist/schemas/schema-builders.js +258 -0
  109. package/dist/schemas/schema-builders.js.map +1 -0
  110. package/dist/schemas/zod-helpers.d.ts +129 -0
  111. package/dist/schemas/zod-helpers.d.ts.map +1 -0
  112. package/dist/schemas/zod-helpers.js +225 -0
  113. package/dist/schemas/zod-helpers.js.map +1 -0
  114. package/dist/schemas/zod-to-json-schema.d.ts +159 -0
  115. package/dist/schemas/zod-to-json-schema.d.ts.map +1 -0
  116. package/dist/schemas/zod-to-json-schema.js +154 -0
  117. package/dist/schemas/zod-to-json-schema.js.map +1 -0
  118. package/dist/tools/ToolRegistry.d.ts +256 -0
  119. package/dist/tools/ToolRegistry.d.ts.map +1 -0
  120. package/dist/tools/ToolRegistry.js +340 -0
  121. package/dist/tools/ToolRegistry.js.map +1 -0
  122. package/dist/tools/index.d.ts +7 -0
  123. package/dist/tools/index.d.ts.map +1 -0
  124. package/dist/tools/index.js +7 -0
  125. package/dist/tools/index.js.map +1 -0
  126. package/dist/transports/BrokerTransport.d.ts +151 -0
  127. package/dist/transports/BrokerTransport.d.ts.map +1 -0
  128. package/dist/transports/BrokerTransport.js +261 -0
  129. package/dist/transports/BrokerTransport.js.map +1 -0
  130. package/dist/transports/NativeTransport.d.ts +178 -0
  131. package/dist/transports/NativeTransport.d.ts.map +1 -0
  132. package/dist/transports/NativeTransport.js +397 -0
  133. package/dist/transports/NativeTransport.js.map +1 -0
  134. package/dist/transports/StdioTransport.d.ts +250 -0
  135. package/dist/transports/StdioTransport.d.ts.map +1 -0
  136. package/dist/transports/StdioTransport.js +487 -0
  137. package/dist/transports/StdioTransport.js.map +1 -0
  138. package/dist/transports/index.d.ts +10 -0
  139. package/dist/transports/index.d.ts.map +1 -0
  140. package/dist/transports/index.js +9 -0
  141. package/dist/transports/index.js.map +1 -0
  142. package/dist/types/broker.d.ts +279 -0
  143. package/dist/types/broker.d.ts.map +1 -0
  144. package/dist/types/broker.js +19 -0
  145. package/dist/types/broker.js.map +1 -0
  146. package/dist/types/config.d.ts +325 -0
  147. package/dist/types/config.d.ts.map +1 -0
  148. package/dist/types/config.js +17 -0
  149. package/dist/types/config.js.map +1 -0
  150. package/dist/types/errors.d.ts +178 -0
  151. package/dist/types/errors.d.ts.map +1 -0
  152. package/dist/types/errors.js +165 -0
  153. package/dist/types/errors.js.map +1 -0
  154. package/dist/types/events.d.ts +210 -0
  155. package/dist/types/events.d.ts.map +1 -0
  156. package/dist/types/events.js +8 -0
  157. package/dist/types/events.js.map +1 -0
  158. package/dist/types/index.d.ts +34 -0
  159. package/dist/types/index.d.ts.map +1 -0
  160. package/dist/types/index.js +21 -0
  161. package/dist/types/index.js.map +1 -0
  162. package/dist/types/protocol.d.ts +48 -0
  163. package/dist/types/protocol.d.ts.map +1 -0
  164. package/dist/types/protocol.js +11 -0
  165. package/dist/types/protocol.js.map +1 -0
  166. package/dist/types/tools.d.ts +67 -0
  167. package/dist/types/tools.d.ts.map +1 -0
  168. package/dist/types/tools.js +16 -0
  169. package/dist/types/tools.js.map +1 -0
  170. package/dist/types/transport.d.ts +250 -0
  171. package/dist/types/transport.d.ts.map +1 -0
  172. package/dist/types/transport.js +18 -0
  173. package/dist/types/transport.js.map +1 -0
  174. package/dist/types/zod-tools.d.ts +198 -0
  175. package/dist/types/zod-tools.d.ts.map +1 -0
  176. package/dist/types/zod-tools.js +14 -0
  177. package/dist/types/zod-tools.js.map +1 -0
  178. package/dist/utils/StdioMessageReader.d.ts +122 -0
  179. package/dist/utils/StdioMessageReader.d.ts.map +1 -0
  180. package/dist/utils/StdioMessageReader.js +209 -0
  181. package/dist/utils/StdioMessageReader.js.map +1 -0
  182. package/dist/utils/StdioMessageWriter.d.ts +104 -0
  183. package/dist/utils/StdioMessageWriter.d.ts.map +1 -0
  184. package/dist/utils/StdioMessageWriter.js +162 -0
  185. package/dist/utils/StdioMessageWriter.js.map +1 -0
  186. package/dist/validation/SchemaValidator.d.ts +208 -0
  187. package/dist/validation/SchemaValidator.d.ts.map +1 -0
  188. package/dist/validation/SchemaValidator.js +411 -0
  189. package/dist/validation/SchemaValidator.js.map +1 -0
  190. package/dist/validation/index.d.ts +11 -0
  191. package/dist/validation/index.d.ts.map +1 -0
  192. package/dist/validation/index.js +10 -0
  193. package/dist/validation/index.js.map +1 -0
  194. package/package.json +70 -5
  195. package/agent.json +0 -18
  196. package/broker.js +0 -214
  197. package/index.js +0 -370
  198. package/ipc.js +0 -220
  199. package/ipcInterfaces/pythonAbilityIPC.py +0 -177
@@ -0,0 +1,253 @@
1
+ /**
2
+ * Broker Connection
3
+ *
4
+ * Wraps a single WebSocket connection to a KADI broker.
5
+ * Handles connection lifecycle, message sending/receiving, and state management.
6
+ *
7
+ * @module broker/BrokerConnection
8
+ */
9
+ import { EventEmitter } from 'events';
10
+ import type { BrokerConfig, JsonRpcRequest, JsonRpcResponse, JsonRpcNotification } from '../types/index.js';
11
+ import { ConnectionState } from '../types/index.js';
12
+ /**
13
+ * Broker Connection Events
14
+ */
15
+ export interface BrokerConnectionEvents {
16
+ /**
17
+ * Emitted when connection is established
18
+ */
19
+ connected: () => void;
20
+ /**
21
+ * Emitted when connection is closed
22
+ */
23
+ disconnected: (code: number, reason: string) => void;
24
+ /**
25
+ * Emitted when an error occurs
26
+ */
27
+ error: (error: Error) => void;
28
+ /**
29
+ * Emitted when a message is received
30
+ */
31
+ message: (message: JsonRpcResponse | JsonRpcNotification) => void;
32
+ /**
33
+ * Emitted when connection state changes
34
+ */
35
+ stateChange: (oldState: ConnectionState, newState: ConnectionState) => void;
36
+ }
37
+ /**
38
+ * Broker Connection
39
+ *
40
+ * Manages a single WebSocket connection to a broker.
41
+ * Handles low-level connection lifecycle and message framing.
42
+ *
43
+ * Does NOT handle:
44
+ * - Protocol logic (handshake, auth) - that's BrokerProtocol's job
45
+ * - Multiple connections - that's BrokerConnectionManager's job
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * const connection = new BrokerConnection({
50
+ * url: 'ws://localhost:8080',
51
+ * name: 'local',
52
+ * connectionTimeout: 10000
53
+ * });
54
+ *
55
+ * connection.on('connected', () => {
56
+ * console.log('Connected to broker');
57
+ * });
58
+ *
59
+ * connection.on('message', (message) => {
60
+ * console.log('Received:', message);
61
+ * });
62
+ *
63
+ * await connection.connect();
64
+ * ```
65
+ */
66
+ export declare class BrokerConnection extends EventEmitter {
67
+ /**
68
+ * Broker configuration
69
+ */
70
+ private readonly config;
71
+ /**
72
+ * WebSocket instance
73
+ */
74
+ private ws;
75
+ /**
76
+ * Current connection state
77
+ */
78
+ private _state;
79
+ /**
80
+ * Assigned agent ID (set after handshake)
81
+ */
82
+ private _agentId;
83
+ /**
84
+ * Heartbeat timer
85
+ */
86
+ private heartbeatTimer;
87
+ /**
88
+ * Connection timestamp
89
+ */
90
+ private _connectedAt;
91
+ /**
92
+ * Last heartbeat timestamp
93
+ */
94
+ private _lastHeartbeat;
95
+ /**
96
+ * Pending requests (for matching responses)
97
+ */
98
+ private pendingRequests;
99
+ /**
100
+ * Request ID counter
101
+ */
102
+ private requestIdCounter;
103
+ /**
104
+ * Create a new BrokerConnection
105
+ *
106
+ * @param config - Broker configuration
107
+ */
108
+ constructor(config: BrokerConfig);
109
+ /**
110
+ * Connect to the broker
111
+ *
112
+ * @throws {KadiError} If connection fails
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * await connection.connect();
117
+ * console.log('Connected!');
118
+ * ```
119
+ */
120
+ connect(): Promise<void>;
121
+ /**
122
+ * Disconnect from the broker
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * await connection.disconnect();
127
+ * ```
128
+ */
129
+ disconnect(): Promise<void>;
130
+ /**
131
+ * Send a JSON-RPC request and wait for response
132
+ *
133
+ * @template T - Response result type
134
+ * @param request - JSON-RPC request
135
+ * @returns Promise resolving to the response result
136
+ *
137
+ * @throws {KadiError} If request fails or times out
138
+ *
139
+ * @example
140
+ * ```typescript
141
+ * const result = await connection.sendRequest({
142
+ * jsonrpc: '2.0',
143
+ * method: 'kadi.handshake',
144
+ * params: { name: 'my-agent' },
145
+ * id: 1
146
+ * });
147
+ * ```
148
+ */
149
+ sendRequest<T = unknown>(request: JsonRpcRequest): Promise<T>;
150
+ /**
151
+ * Send a JSON-RPC notification (no response expected)
152
+ *
153
+ * @param notification - JSON-RPC notification
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * connection.sendNotification({
158
+ * jsonrpc: '2.0',
159
+ * method: 'kadi.heartbeat',
160
+ * params: { timestamp: Date.now() }
161
+ * });
162
+ * ```
163
+ */
164
+ sendNotification(notification: JsonRpcNotification): void;
165
+ /**
166
+ * Send a tool result back to broker as JSON-RPC response
167
+ *
168
+ * Used when this client acts as a tool provider and needs to respond to invocations.
169
+ * The requestId must match the incoming request's id field for proper routing.
170
+ *
171
+ * @param requestId - Request ID to respond to (CRITICAL: must match incoming request id)
172
+ * @param result - Result data to send back
173
+ */
174
+ sendResponse(requestId: string | number | undefined, result: unknown): void;
175
+ /**
176
+ * Send a tool error back to broker as JSON-RPC error response
177
+ *
178
+ * Used when tool execution fails and we need to report the error back.
179
+ * The requestId must match the incoming request's id field for proper routing.
180
+ *
181
+ * @param requestId - Request ID to respond to (CRITICAL: must match incoming request id)
182
+ * @param code - Error code (e.g., ErrorCode.TOOL_NOT_FOUND)
183
+ * @param message - Human-readable error message
184
+ * @param data - Optional additional error context
185
+ */
186
+ sendError(requestId: string | number | undefined, code: string, message: string, data?: Record<string, unknown>): void;
187
+ /**
188
+ * Send raw data to broker
189
+ *
190
+ * @param data - Data to send (request, response, or notification)
191
+ */
192
+ private send;
193
+ /**
194
+ * Handle incoming WebSocket message
195
+ *
196
+ * @param data - Raw message data
197
+ */
198
+ private handleMessage;
199
+ /**
200
+ * Handle WebSocket close event
201
+ *
202
+ * @param code - Close code
203
+ * @param reason - Close reason
204
+ */
205
+ private handleClose;
206
+ /**
207
+ * Setup heartbeat timer
208
+ */
209
+ private setupHeartbeat;
210
+ /**
211
+ * Cleanup connection resources
212
+ */
213
+ private cleanup;
214
+ /**
215
+ * Set connection state and emit event
216
+ *
217
+ * @param newState - New state
218
+ */
219
+ private setState;
220
+ /**
221
+ * Get broker name
222
+ */
223
+ get name(): string;
224
+ /**
225
+ * Get broker URL
226
+ */
227
+ get url(): string;
228
+ /**
229
+ * Get current connection state
230
+ */
231
+ get state(): ConnectionState;
232
+ /**
233
+ * Check if connected
234
+ */
235
+ get isConnected(): boolean;
236
+ /**
237
+ * Get assigned agent ID
238
+ */
239
+ get agentId(): string | null;
240
+ /**
241
+ * Set agent ID (called after handshake)
242
+ */
243
+ set agentId(id: string | null);
244
+ /**
245
+ * Get connection timestamp
246
+ */
247
+ get connectedAt(): number | null;
248
+ /**
249
+ * Get last heartbeat timestamp
250
+ */
251
+ get lastHeartbeat(): number | null;
252
+ }
253
+ //# sourceMappingURL=BrokerConnection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrokerConnection.d.ts","sourceRoot":"","sources":["../../src/broker/BrokerConnection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,434 @@
1
+ /**
2
+ * Broker Connection
3
+ *
4
+ * Wraps a single WebSocket connection to a KADI broker.
5
+ * Handles connection lifecycle, message sending/receiving, and state management.
6
+ *
7
+ * @module broker/BrokerConnection
8
+ */
9
+ import { EventEmitter } from 'events';
10
+ import WebSocket from 'ws';
11
+ import { ConnectionState } from '../types/index.js';
12
+ import { KadiError, ErrorCode } from '../types/index.js';
13
+ /**
14
+ * Broker Connection
15
+ *
16
+ * Manages a single WebSocket connection to a broker.
17
+ * Handles low-level connection lifecycle and message framing.
18
+ *
19
+ * Does NOT handle:
20
+ * - Protocol logic (handshake, auth) - that's BrokerProtocol's job
21
+ * - Multiple connections - that's BrokerConnectionManager's job
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const connection = new BrokerConnection({
26
+ * url: 'ws://localhost:8080',
27
+ * name: 'local',
28
+ * connectionTimeout: 10000
29
+ * });
30
+ *
31
+ * connection.on('connected', () => {
32
+ * console.log('Connected to broker');
33
+ * });
34
+ *
35
+ * connection.on('message', (message) => {
36
+ * console.log('Received:', message);
37
+ * });
38
+ *
39
+ * await connection.connect();
40
+ * ```
41
+ */
42
+ export class BrokerConnection extends EventEmitter {
43
+ /**
44
+ * Broker configuration
45
+ */
46
+ config;
47
+ /**
48
+ * WebSocket instance
49
+ */
50
+ ws = null;
51
+ /**
52
+ * Current connection state
53
+ */
54
+ _state = ConnectionState.DISCONNECTED;
55
+ /**
56
+ * Assigned agent ID (set after handshake)
57
+ */
58
+ _agentId = null;
59
+ /**
60
+ * Heartbeat timer
61
+ */
62
+ heartbeatTimer = null;
63
+ /**
64
+ * Connection timestamp
65
+ */
66
+ _connectedAt = null;
67
+ /**
68
+ * Last heartbeat timestamp
69
+ */
70
+ _lastHeartbeat = null;
71
+ /**
72
+ * Pending requests (for matching responses)
73
+ */
74
+ pendingRequests = new Map();
75
+ /**
76
+ * Request ID counter
77
+ */
78
+ requestIdCounter = 0;
79
+ /**
80
+ * Create a new BrokerConnection
81
+ *
82
+ * @param config - Broker configuration
83
+ */
84
+ constructor(config) {
85
+ super();
86
+ this.config = {
87
+ ...config,
88
+ connectionTimeout: config.connectionTimeout ?? 10000,
89
+ requestTimeout: config.requestTimeout ?? 30000,
90
+ heartbeatInterval: config.heartbeatInterval ?? 30000,
91
+ autoReconnect: config.autoReconnect ?? true,
92
+ maxReconnectAttempts: config.maxReconnectAttempts ?? 5
93
+ };
94
+ }
95
+ /**
96
+ * Connect to the broker
97
+ *
98
+ * @throws {KadiError} If connection fails
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * await connection.connect();
103
+ * console.log('Connected!');
104
+ * ```
105
+ */
106
+ async connect() {
107
+ if (this._state !== ConnectionState.DISCONNECTED) {
108
+ throw new KadiError('Connection already in progress or established', ErrorCode.CONNECTION_FAILED, 400, { currentState: this._state, broker: this.config.name });
109
+ }
110
+ this.setState(ConnectionState.CONNECTING);
111
+ return new Promise((resolve, reject) => {
112
+ const timeout = setTimeout(() => {
113
+ this.cleanup();
114
+ reject(new KadiError(`Connection timeout after ${this.config.connectionTimeout}ms`, ErrorCode.CONNECTION_TIMEOUT, 408, { broker: this.config.name, url: this.config.url }));
115
+ }, this.config.connectionTimeout);
116
+ try {
117
+ this.ws = new WebSocket(this.config.url);
118
+ this.ws.on('open', () => {
119
+ clearTimeout(timeout);
120
+ this._connectedAt = Date.now();
121
+ this.setState(ConnectionState.CONNECTED);
122
+ this.setupHeartbeat();
123
+ this.emit('connected');
124
+ resolve();
125
+ });
126
+ this.ws.on('error', (error) => {
127
+ clearTimeout(timeout);
128
+ this.emit('error', error);
129
+ reject(new KadiError(`WebSocket error: ${error.message}`, ErrorCode.CONNECTION_FAILED, 500, { broker: this.config.name, url: this.config.url, originalError: error.message }));
130
+ });
131
+ this.ws.on('close', (code, reason) => {
132
+ clearTimeout(timeout);
133
+ this.handleClose(code, reason.toString());
134
+ });
135
+ this.ws.on('message', (data) => {
136
+ this.handleMessage(data);
137
+ });
138
+ }
139
+ catch (error) {
140
+ clearTimeout(timeout);
141
+ this.cleanup();
142
+ reject(KadiError.from(error, ErrorCode.CONNECTION_FAILED));
143
+ }
144
+ });
145
+ }
146
+ /**
147
+ * Disconnect from the broker
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * await connection.disconnect();
152
+ * ```
153
+ */
154
+ async disconnect() {
155
+ this.cleanup();
156
+ }
157
+ /**
158
+ * Send a JSON-RPC request and wait for response
159
+ *
160
+ * @template T - Response result type
161
+ * @param request - JSON-RPC request
162
+ * @returns Promise resolving to the response result
163
+ *
164
+ * @throws {KadiError} If request fails or times out
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * const result = await connection.sendRequest({
169
+ * jsonrpc: '2.0',
170
+ * method: 'kadi.handshake',
171
+ * params: { name: 'my-agent' },
172
+ * id: 1
173
+ * });
174
+ * ```
175
+ */
176
+ async sendRequest(request) {
177
+ if (!this.isConnected) {
178
+ throw new KadiError('Not connected to broker', ErrorCode.BROKER_NOT_CONNECTED, 503, { broker: this.config.name });
179
+ }
180
+ // Generate ID if not provided
181
+ if (request.id === undefined) {
182
+ request.id = ++this.requestIdCounter;
183
+ }
184
+ return new Promise((resolve, reject) => {
185
+ const timeout = setTimeout(() => {
186
+ this.pendingRequests.delete(request.id);
187
+ reject(new KadiError(`Request timeout after ${this.config.requestTimeout}ms`, ErrorCode.BROKER_TIMEOUT, 408, { broker: this.config.name, method: request.method, requestId: request.id }));
188
+ }, this.config.requestTimeout);
189
+ this.pendingRequests.set(request.id, {
190
+ id: request.id,
191
+ resolve: resolve,
192
+ reject,
193
+ timeout,
194
+ timestamp: Date.now()
195
+ });
196
+ this.send(request);
197
+ });
198
+ }
199
+ /**
200
+ * Send a JSON-RPC notification (no response expected)
201
+ *
202
+ * @param notification - JSON-RPC notification
203
+ *
204
+ * @example
205
+ * ```typescript
206
+ * connection.sendNotification({
207
+ * jsonrpc: '2.0',
208
+ * method: 'kadi.heartbeat',
209
+ * params: { timestamp: Date.now() }
210
+ * });
211
+ * ```
212
+ */
213
+ sendNotification(notification) {
214
+ if (!this.isConnected) {
215
+ throw new KadiError('Not connected to broker', ErrorCode.BROKER_NOT_CONNECTED, 503, { broker: this.config.name });
216
+ }
217
+ this.send(notification);
218
+ }
219
+ /**
220
+ * Send a tool result back to broker as JSON-RPC response
221
+ *
222
+ * Used when this client acts as a tool provider and needs to respond to invocations.
223
+ * The requestId must match the incoming request's id field for proper routing.
224
+ *
225
+ * @param requestId - Request ID to respond to (CRITICAL: must match incoming request id)
226
+ * @param result - Result data to send back
227
+ */
228
+ sendResponse(requestId, result) {
229
+ if (!requestId) {
230
+ // No ID = broker can't route response, so silently drop
231
+ return;
232
+ }
233
+ if (!this.isConnected) {
234
+ throw new KadiError('Not connected to broker', ErrorCode.BROKER_NOT_CONNECTED, 503, { broker: this.config.name });
235
+ }
236
+ // Send proper JSON-RPC response with matching id
237
+ const response = {
238
+ jsonrpc: '2.0',
239
+ result,
240
+ id: requestId
241
+ };
242
+ this.send(response);
243
+ }
244
+ /**
245
+ * Send a tool error back to broker as JSON-RPC error response
246
+ *
247
+ * Used when tool execution fails and we need to report the error back.
248
+ * The requestId must match the incoming request's id field for proper routing.
249
+ *
250
+ * @param requestId - Request ID to respond to (CRITICAL: must match incoming request id)
251
+ * @param code - Error code (e.g., ErrorCode.TOOL_NOT_FOUND)
252
+ * @param message - Human-readable error message
253
+ * @param data - Optional additional error context
254
+ */
255
+ sendError(requestId, code, message, data) {
256
+ if (!requestId) {
257
+ // No ID = broker can't route error, so silently drop
258
+ return;
259
+ }
260
+ if (!this.isConnected) {
261
+ throw new KadiError('Not connected to broker', ErrorCode.BROKER_NOT_CONNECTED, 503, { broker: this.config.name });
262
+ }
263
+ // Send proper JSON-RPC error response with matching id
264
+ const response = {
265
+ jsonrpc: '2.0',
266
+ error: {
267
+ code: -32000, // Application error code range
268
+ message: `${code}: ${message}`,
269
+ data
270
+ },
271
+ id: requestId
272
+ };
273
+ this.send(response);
274
+ }
275
+ /**
276
+ * Send raw data to broker
277
+ *
278
+ * @param data - Data to send (request, response, or notification)
279
+ */
280
+ send(data) {
281
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
282
+ throw new KadiError('WebSocket not open', ErrorCode.BROKER_NOT_CONNECTED, 503, { broker: this.config.name });
283
+ }
284
+ try {
285
+ const message = JSON.stringify(data);
286
+ this.ws.send(message);
287
+ }
288
+ catch (error) {
289
+ throw new KadiError(`Failed to send message: ${error instanceof Error ? error.message : String(error)}`, ErrorCode.TRANSPORT_SEND_FAILED, 500, { broker: this.config.name });
290
+ }
291
+ }
292
+ /**
293
+ * Handle incoming WebSocket message
294
+ *
295
+ * @param data - Raw message data
296
+ */
297
+ handleMessage(data) {
298
+ try {
299
+ const message = JSON.parse(data.toString());
300
+ // Check if this is a response to a pending request
301
+ if ('id' in message && message.id !== undefined) {
302
+ const pending = this.pendingRequests.get(message.id);
303
+ if (pending) {
304
+ clearTimeout(pending.timeout);
305
+ this.pendingRequests.delete(message.id);
306
+ const response = message;
307
+ if (response.error) {
308
+ pending.reject(new KadiError(response.error.message, ErrorCode.BROKER_RESPONSE_ERROR, 500, { broker: this.config.name, errorCode: response.error.code, errorData: response.error.data }));
309
+ }
310
+ else {
311
+ pending.resolve(response.result);
312
+ }
313
+ return;
314
+ }
315
+ }
316
+ // Emit as general message (notification or unsolicited response)
317
+ this.emit('message', message);
318
+ }
319
+ catch (error) {
320
+ this.emit('error', new KadiError(`Failed to parse message: ${error instanceof Error ? error.message : String(error)}`, ErrorCode.INVALID_MESSAGE_FORMAT, 400, { broker: this.config.name }));
321
+ }
322
+ }
323
+ /**
324
+ * Handle WebSocket close event
325
+ *
326
+ * @param code - Close code
327
+ * @param reason - Close reason
328
+ */
329
+ handleClose(code, reason) {
330
+ this.setState(ConnectionState.DISCONNECTED);
331
+ this.cleanup();
332
+ this.emit('disconnected', code, reason);
333
+ }
334
+ /**
335
+ * Setup heartbeat timer
336
+ */
337
+ setupHeartbeat() {
338
+ if (this.heartbeatTimer) {
339
+ clearInterval(this.heartbeatTimer);
340
+ }
341
+ if (this.config.heartbeatInterval && this.config.heartbeatInterval > 0) {
342
+ this.heartbeatTimer = setInterval(() => {
343
+ this._lastHeartbeat = Date.now();
344
+ // Heartbeat logic handled by BrokerProtocol
345
+ }, this.config.heartbeatInterval);
346
+ }
347
+ }
348
+ /**
349
+ * Cleanup connection resources
350
+ */
351
+ cleanup() {
352
+ if (this.heartbeatTimer) {
353
+ clearInterval(this.heartbeatTimer);
354
+ this.heartbeatTimer = null;
355
+ }
356
+ // Reject all pending requests
357
+ for (const [id, pending] of this.pendingRequests) {
358
+ clearTimeout(pending.timeout);
359
+ pending.reject(new KadiError('Connection closed', ErrorCode.CONNECTION_CLOSED, 503, { broker: this.config.name, requestId: id }));
360
+ }
361
+ this.pendingRequests.clear();
362
+ if (this.ws) {
363
+ this.ws.removeAllListeners();
364
+ if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {
365
+ this.ws.close();
366
+ }
367
+ this.ws = null;
368
+ }
369
+ this._connectedAt = null;
370
+ this._lastHeartbeat = null;
371
+ this.setState(ConnectionState.DISCONNECTED);
372
+ }
373
+ /**
374
+ * Set connection state and emit event
375
+ *
376
+ * @param newState - New state
377
+ */
378
+ setState(newState) {
379
+ const oldState = this._state;
380
+ if (oldState !== newState) {
381
+ this._state = newState;
382
+ this.emit('stateChange', oldState, newState);
383
+ }
384
+ }
385
+ /**
386
+ * Get broker name
387
+ */
388
+ get name() {
389
+ return this.config.name;
390
+ }
391
+ /**
392
+ * Get broker URL
393
+ */
394
+ get url() {
395
+ return this.config.url;
396
+ }
397
+ /**
398
+ * Get current connection state
399
+ */
400
+ get state() {
401
+ return this._state;
402
+ }
403
+ /**
404
+ * Check if connected
405
+ */
406
+ get isConnected() {
407
+ return this._state === ConnectionState.CONNECTED || this._state === ConnectionState.AUTHENTICATED;
408
+ }
409
+ /**
410
+ * Get assigned agent ID
411
+ */
412
+ get agentId() {
413
+ return this._agentId;
414
+ }
415
+ /**
416
+ * Set agent ID (called after handshake)
417
+ */
418
+ set agentId(id) {
419
+ this._agentId = id;
420
+ }
421
+ /**
422
+ * Get connection timestamp
423
+ */
424
+ get connectedAt() {
425
+ return this._connectedAt;
426
+ }
427
+ /**
428
+ * Get last heartbeat timestamp
429
+ */
430
+ get lastHeartbeat() {
431
+ return this._lastHeartbeat;
432
+ }
433
+ }
434
+ //# sourceMappingURL=BrokerConnection.js.map