@agentxjs/core 1.9.1-dev

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 (77) hide show
  1. package/package.json +31 -0
  2. package/src/agent/AgentStateMachine.ts +151 -0
  3. package/src/agent/README.md +296 -0
  4. package/src/agent/__tests__/AgentStateMachine.test.ts +346 -0
  5. package/src/agent/__tests__/createAgent.test.ts +728 -0
  6. package/src/agent/__tests__/engine/internal/messageAssemblerProcessor.test.ts +567 -0
  7. package/src/agent/__tests__/engine/internal/stateEventProcessor.test.ts +315 -0
  8. package/src/agent/__tests__/engine/internal/turnTrackerProcessor.test.ts +340 -0
  9. package/src/agent/__tests__/engine/mealy/Mealy.test.ts +370 -0
  10. package/src/agent/__tests__/engine/mealy/Store.test.ts +123 -0
  11. package/src/agent/__tests__/engine/mealy/combinators.test.ts +322 -0
  12. package/src/agent/createAgent.ts +467 -0
  13. package/src/agent/engine/AgentProcessor.ts +106 -0
  14. package/src/agent/engine/MealyMachine.ts +184 -0
  15. package/src/agent/engine/internal/index.ts +35 -0
  16. package/src/agent/engine/internal/messageAssemblerProcessor.ts +550 -0
  17. package/src/agent/engine/internal/stateEventProcessor.ts +313 -0
  18. package/src/agent/engine/internal/turnTrackerProcessor.ts +239 -0
  19. package/src/agent/engine/mealy/Mealy.ts +308 -0
  20. package/src/agent/engine/mealy/Processor.ts +70 -0
  21. package/src/agent/engine/mealy/Sink.ts +56 -0
  22. package/src/agent/engine/mealy/Source.ts +51 -0
  23. package/src/agent/engine/mealy/Store.ts +98 -0
  24. package/src/agent/engine/mealy/combinators.ts +176 -0
  25. package/src/agent/engine/mealy/index.ts +45 -0
  26. package/src/agent/index.ts +106 -0
  27. package/src/agent/types/engine.ts +395 -0
  28. package/src/agent/types/event.ts +478 -0
  29. package/src/agent/types/index.ts +197 -0
  30. package/src/agent/types/message.ts +387 -0
  31. package/src/common/index.ts +8 -0
  32. package/src/common/logger/ConsoleLogger.ts +137 -0
  33. package/src/common/logger/LoggerFactoryImpl.ts +123 -0
  34. package/src/common/logger/index.ts +26 -0
  35. package/src/common/logger/types.ts +98 -0
  36. package/src/container/Container.ts +185 -0
  37. package/src/container/index.ts +44 -0
  38. package/src/container/types.ts +71 -0
  39. package/src/driver/index.ts +42 -0
  40. package/src/driver/types.ts +363 -0
  41. package/src/event/EventBus.ts +260 -0
  42. package/src/event/README.md +237 -0
  43. package/src/event/__tests__/EventBus.test.ts +251 -0
  44. package/src/event/index.ts +46 -0
  45. package/src/event/types/agent.ts +512 -0
  46. package/src/event/types/base.ts +241 -0
  47. package/src/event/types/bus.ts +429 -0
  48. package/src/event/types/command.ts +749 -0
  49. package/src/event/types/container.ts +471 -0
  50. package/src/event/types/driver.ts +452 -0
  51. package/src/event/types/index.ts +26 -0
  52. package/src/event/types/session.ts +314 -0
  53. package/src/image/Image.ts +203 -0
  54. package/src/image/index.ts +36 -0
  55. package/src/image/types.ts +77 -0
  56. package/src/index.ts +20 -0
  57. package/src/mq/OffsetGenerator.ts +48 -0
  58. package/src/mq/README.md +166 -0
  59. package/src/mq/__tests__/OffsetGenerator.test.ts +121 -0
  60. package/src/mq/index.ts +18 -0
  61. package/src/mq/types.ts +172 -0
  62. package/src/network/RpcClient.ts +455 -0
  63. package/src/network/index.ts +76 -0
  64. package/src/network/jsonrpc.ts +336 -0
  65. package/src/network/protocol.ts +90 -0
  66. package/src/network/types.ts +284 -0
  67. package/src/persistence/index.ts +27 -0
  68. package/src/persistence/types.ts +226 -0
  69. package/src/runtime/AgentXRuntime.ts +501 -0
  70. package/src/runtime/index.ts +56 -0
  71. package/src/runtime/types.ts +236 -0
  72. package/src/session/Session.ts +71 -0
  73. package/src/session/index.ts +25 -0
  74. package/src/session/types.ts +77 -0
  75. package/src/workspace/index.ts +27 -0
  76. package/src/workspace/types.ts +131 -0
  77. package/tsconfig.json +10 -0
@@ -0,0 +1,455 @@
1
+ /**
2
+ * RpcClient - JSON-RPC 2.0 Client over WebSocket
3
+ *
4
+ * Provides:
5
+ * - Request/Response with automatic ID matching
6
+ * - Notification handling (stream events)
7
+ * - Timeout management
8
+ * - Reconnection support
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const client = new RpcClient({ url: "ws://localhost:5200" });
13
+ * await client.connect();
14
+ *
15
+ * // RPC call
16
+ * const result = await client.call("container.list", {});
17
+ *
18
+ * // Stream events
19
+ * client.onNotification("stream.event", (params) => {
20
+ * console.log("Event:", params.event);
21
+ * });
22
+ *
23
+ * // Subscribe to topic
24
+ * client.notify("subscribe", { topic: "session-123" });
25
+ * ```
26
+ */
27
+
28
+ import {
29
+ createRequest,
30
+ createNotification,
31
+ parseMessage,
32
+ isSuccessResponse,
33
+ isErrorResponse,
34
+ isNotification,
35
+ isStreamEvent,
36
+ type RpcMethod,
37
+ type NotificationMethod,
38
+ type StreamEventParams,
39
+ } from "./jsonrpc";
40
+ import type { SystemEvent } from "../event/types/base";
41
+
42
+ /**
43
+ * Check if running in browser environment
44
+ */
45
+ function isBrowser(): boolean {
46
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
+ const globalWindow = typeof globalThis !== "undefined" ? (globalThis as any).window : undefined;
48
+ return globalWindow?.document !== undefined;
49
+ }
50
+
51
+ // ============================================================================
52
+ // Types
53
+ // ============================================================================
54
+
55
+ /**
56
+ * RpcClient configuration
57
+ */
58
+ export interface RpcClientConfig {
59
+ /**
60
+ * WebSocket URL
61
+ */
62
+ url: string;
63
+
64
+ /**
65
+ * Request timeout in milliseconds (default: 30000)
66
+ */
67
+ timeout?: number;
68
+
69
+ /**
70
+ * Auto reconnect on disconnect (default: true)
71
+ */
72
+ autoReconnect?: boolean;
73
+
74
+ /**
75
+ * Reconnect delay in milliseconds (default: 3000)
76
+ */
77
+ reconnectDelay?: number;
78
+
79
+ /**
80
+ * Headers for authentication (Node.js only, sent in first message for browser)
81
+ */
82
+ headers?:
83
+ | Record<string, string>
84
+ | (() => Record<string, string> | Promise<Record<string, string>>);
85
+
86
+ /**
87
+ * Debug logging
88
+ */
89
+ debug?: boolean;
90
+ }
91
+
92
+ /**
93
+ * Pending request state
94
+ */
95
+ interface PendingRequest {
96
+ resolve: (result: unknown) => void;
97
+ reject: (error: Error) => void;
98
+ timer: ReturnType<typeof setTimeout>;
99
+ }
100
+
101
+ /**
102
+ * Notification handler
103
+ */
104
+ type NotificationHandler = (method: string, params: unknown) => void;
105
+
106
+ /**
107
+ * Stream event handler
108
+ */
109
+ type StreamEventHandler = (topic: string, event: SystemEvent) => void;
110
+
111
+ /**
112
+ * Connection state
113
+ */
114
+ export type RpcClientState = "disconnected" | "connecting" | "connected";
115
+
116
+ // ============================================================================
117
+ // RpcClient Implementation
118
+ // ============================================================================
119
+
120
+ /**
121
+ * JSON-RPC 2.0 Client
122
+ */
123
+ export class RpcClient {
124
+ private readonly config: RpcClientConfig;
125
+ private readonly timeout: number;
126
+ private readonly pendingRequests = new Map<string | number, PendingRequest>();
127
+ private readonly notificationHandlers = new Set<NotificationHandler>();
128
+ private readonly streamEventHandlers = new Set<StreamEventHandler>();
129
+
130
+ private ws: WebSocket | null = null;
131
+ private state: RpcClientState = "disconnected";
132
+ private requestId = 0;
133
+ private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
134
+ private disposed = false;
135
+
136
+ constructor(config: RpcClientConfig) {
137
+ this.config = config;
138
+ this.timeout = config.timeout ?? 30000;
139
+ }
140
+
141
+ // ==================== Properties ====================
142
+
143
+ /**
144
+ * Current connection state
145
+ */
146
+ get connectionState(): RpcClientState {
147
+ return this.state;
148
+ }
149
+
150
+ /**
151
+ * Whether client is connected
152
+ */
153
+ get connected(): boolean {
154
+ return this.state === "connected";
155
+ }
156
+
157
+ // ==================== Connection ====================
158
+
159
+ /**
160
+ * Connect to server
161
+ */
162
+ async connect(): Promise<void> {
163
+ if (this.disposed) {
164
+ throw new Error("Client has been disposed");
165
+ }
166
+
167
+ if (this.state === "connected") {
168
+ return;
169
+ }
170
+
171
+ this.state = "connecting";
172
+
173
+ const url = this.config.url;
174
+
175
+ // Create WebSocket (browser or Node.js)
176
+ let ws: WebSocket;
177
+ if (isBrowser()) {
178
+ ws = new WebSocket(url);
179
+ } else {
180
+ const { default: WS } = await import("ws");
181
+ ws = new WS(url) as unknown as WebSocket;
182
+ }
183
+
184
+ this.ws = ws;
185
+
186
+ return new Promise((resolve, reject) => {
187
+ ws.onopen = async () => {
188
+ this.state = "connected";
189
+
190
+ if (this.config.debug) {
191
+ console.log("[RpcClient] Connected to", url);
192
+ }
193
+
194
+ // Send auth if in browser (headers not supported in WebSocket API)
195
+ if (isBrowser() && this.config.headers) {
196
+ const headers =
197
+ typeof this.config.headers === "function"
198
+ ? await this.config.headers()
199
+ : this.config.headers;
200
+ this.notify("auth" as NotificationMethod, { headers });
201
+ }
202
+
203
+ resolve();
204
+ };
205
+
206
+ ws.onclose = () => {
207
+ this.state = "disconnected";
208
+
209
+ if (this.config.debug) {
210
+ console.log("[RpcClient] Disconnected");
211
+ }
212
+
213
+ // Auto reconnect
214
+ if (!this.disposed && this.config.autoReconnect !== false) {
215
+ this.scheduleReconnect();
216
+ }
217
+ };
218
+
219
+ ws.onerror = (err) => {
220
+ if (this.config.debug) {
221
+ console.error("[RpcClient] WebSocket error:", err);
222
+ }
223
+
224
+ if (this.state === "connecting") {
225
+ this.state = "disconnected";
226
+ reject(new Error("Failed to connect to server"));
227
+ }
228
+ };
229
+
230
+ ws.onmessage = (event) => {
231
+ this.handleMessage(event.data as string);
232
+ };
233
+ });
234
+ }
235
+
236
+ /**
237
+ * Disconnect from server
238
+ */
239
+ disconnect(): void {
240
+ if (this.reconnectTimer) {
241
+ clearTimeout(this.reconnectTimer);
242
+ this.reconnectTimer = null;
243
+ }
244
+
245
+ if (this.ws) {
246
+ this.ws.close();
247
+ this.ws = null;
248
+ }
249
+
250
+ this.state = "disconnected";
251
+ }
252
+
253
+ /**
254
+ * Dispose client and clean up resources
255
+ */
256
+ dispose(): void {
257
+ this.disposed = true;
258
+
259
+ // Reject all pending requests
260
+ for (const [id, pending] of this.pendingRequests) {
261
+ clearTimeout(pending.timer);
262
+ pending.reject(new Error("Client disposed"));
263
+ this.pendingRequests.delete(id);
264
+ }
265
+
266
+ this.disconnect();
267
+ this.notificationHandlers.clear();
268
+ this.streamEventHandlers.clear();
269
+
270
+ if (this.config.debug) {
271
+ console.log("[RpcClient] Disposed");
272
+ }
273
+ }
274
+
275
+ private scheduleReconnect(): void {
276
+ if (this.reconnectTimer) return;
277
+
278
+ const delay = this.config.reconnectDelay ?? 3000;
279
+
280
+ this.reconnectTimer = setTimeout(async () => {
281
+ this.reconnectTimer = null;
282
+
283
+ if (!this.disposed && this.state === "disconnected") {
284
+ if (this.config.debug) {
285
+ console.log("[RpcClient] Attempting to reconnect...");
286
+ }
287
+
288
+ try {
289
+ await this.connect();
290
+ } catch {
291
+ this.scheduleReconnect();
292
+ }
293
+ }
294
+ }, delay);
295
+ }
296
+
297
+ // ==================== RPC Methods ====================
298
+
299
+ /**
300
+ * Call an RPC method and wait for response
301
+ */
302
+ async call<T = unknown>(method: RpcMethod, params: unknown): Promise<T> {
303
+ if (!this.connected || !this.ws) {
304
+ throw new Error("Not connected to server");
305
+ }
306
+
307
+ const id = ++this.requestId;
308
+ const request = createRequest(id, method, params);
309
+
310
+ return new Promise((resolve, reject) => {
311
+ const timer = setTimeout(() => {
312
+ this.pendingRequests.delete(id);
313
+ reject(new Error(`Request timeout: ${method}`));
314
+ }, this.timeout);
315
+
316
+ this.pendingRequests.set(id, {
317
+ resolve: resolve as (result: unknown) => void,
318
+ reject,
319
+ timer,
320
+ });
321
+
322
+ if (this.config.debug) {
323
+ console.log("[RpcClient] Sending request:", method, params);
324
+ }
325
+
326
+ this.ws!.send(JSON.stringify(request));
327
+ });
328
+ }
329
+
330
+ /**
331
+ * Send a notification (no response expected)
332
+ */
333
+ notify(method: NotificationMethod | string, params: unknown): void {
334
+ if (!this.connected || !this.ws) {
335
+ if (this.config.debug) {
336
+ console.warn("[RpcClient] Cannot send notification: not connected");
337
+ }
338
+ return;
339
+ }
340
+
341
+ const notification = createNotification(method as NotificationMethod, params);
342
+
343
+ if (this.config.debug) {
344
+ console.log("[RpcClient] Sending notification:", method, params);
345
+ }
346
+
347
+ this.ws.send(JSON.stringify(notification));
348
+ }
349
+
350
+ /**
351
+ * Subscribe to a topic (convenience method)
352
+ */
353
+ subscribe(topic: string): void {
354
+ this.notify("subscribe", { topic });
355
+ }
356
+
357
+ /**
358
+ * Unsubscribe from a topic (convenience method)
359
+ */
360
+ unsubscribe(topic: string): void {
361
+ this.notify("unsubscribe", { topic });
362
+ }
363
+
364
+ // ==================== Event Handlers ====================
365
+
366
+ /**
367
+ * Register handler for all notifications
368
+ */
369
+ onNotification(handler: NotificationHandler): () => void {
370
+ this.notificationHandlers.add(handler);
371
+ return () => this.notificationHandlers.delete(handler);
372
+ }
373
+
374
+ /**
375
+ * Register handler for stream events
376
+ */
377
+ onStreamEvent(handler: StreamEventHandler): () => void {
378
+ this.streamEventHandlers.add(handler);
379
+ return () => this.streamEventHandlers.delete(handler);
380
+ }
381
+
382
+ // ==================== Message Handling ====================
383
+
384
+ private handleMessage(data: string): void {
385
+ try {
386
+ const parsed = parseMessage(data);
387
+
388
+ // Handle single message
389
+ if (!Array.isArray(parsed)) {
390
+ this.handleParsedMessage(parsed);
391
+ } else {
392
+ // Batch response (rare)
393
+ for (const item of parsed) {
394
+ this.handleParsedMessage(item);
395
+ }
396
+ }
397
+ } catch (err) {
398
+ if (this.config.debug) {
399
+ console.error("[RpcClient] Failed to parse message:", err);
400
+ }
401
+ }
402
+ }
403
+
404
+ private handleParsedMessage(parsed: import("jsonrpc-lite").IParsedObject): void {
405
+ if (isSuccessResponse(parsed)) {
406
+ // Success response - resolve pending request
407
+ const payload = parsed.payload as { id: string | number; result: unknown };
408
+ const pending = this.pendingRequests.get(payload.id);
409
+
410
+ if (pending) {
411
+ this.pendingRequests.delete(payload.id);
412
+ clearTimeout(pending.timer);
413
+ pending.resolve(payload.result);
414
+ }
415
+ } else if (isErrorResponse(parsed)) {
416
+ // Error response - reject pending request
417
+ const payload = parsed.payload as {
418
+ id: string | number | null;
419
+ error: { code: number; message: string; data?: unknown };
420
+ };
421
+
422
+ if (payload.id !== null) {
423
+ const pending = this.pendingRequests.get(payload.id);
424
+
425
+ if (pending) {
426
+ this.pendingRequests.delete(payload.id);
427
+ clearTimeout(pending.timer);
428
+ pending.reject(new Error(payload.error.message));
429
+ }
430
+ }
431
+ } else if (isNotification(parsed)) {
432
+ // Notification - stream event or control message
433
+ const payload = parsed.payload as { method: string; params: unknown };
434
+
435
+ if (this.config.debug) {
436
+ console.log("[RpcClient] Received notification:", payload.method);
437
+ }
438
+
439
+ // Notify all handlers
440
+ for (const handler of this.notificationHandlers) {
441
+ handler(payload.method, payload.params);
442
+ }
443
+
444
+ // Handle stream events specially
445
+ if (isStreamEvent(parsed)) {
446
+ const streamPayload = parsed.payload as { params: StreamEventParams };
447
+ const { topic, event } = streamPayload.params;
448
+
449
+ for (const handler of this.streamEventHandlers) {
450
+ handler(topic, event);
451
+ }
452
+ }
453
+ }
454
+ }
455
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Network Module
3
+ *
4
+ * Provides standard interfaces for client-server communication:
5
+ * - ChannelServer: Server that accepts connections
6
+ * - ChannelClient: Client that connects to server
7
+ * - ChannelConnection: Server-side representation of a client
8
+ * - Reliable Message Protocol: At-least-once delivery
9
+ *
10
+ * Implementations are provided by platform packages:
11
+ * - @agentxjs/node: WebSocket (ws library)
12
+ * - @agentxjs/cloudflare: Durable Objects WebSocket
13
+ */
14
+
15
+ // Types
16
+ export type {
17
+ Unsubscribe,
18
+ MinimalHTTPServer,
19
+ SendReliableOptions,
20
+ ConnectionState,
21
+ ChannelConnection,
22
+ ChannelServer,
23
+ ChannelServerOptions,
24
+ ChannelClient,
25
+ ChannelClientOptions,
26
+ ChannelServerProvider,
27
+ ChannelClientProvider,
28
+ } from "./types";
29
+
30
+ // Protocol (reliable delivery)
31
+ export type { ReliableWrapper, AckMessage } from "./protocol";
32
+ export {
33
+ isReliableWrapper,
34
+ isAckMessage,
35
+ wrapMessage,
36
+ createAck,
37
+ unwrapMessage,
38
+ generateMessageId,
39
+ } from "./protocol";
40
+
41
+ // JSON-RPC 2.0 Protocol
42
+ export type {
43
+ RpcMethod,
44
+ NotificationMethod,
45
+ RpcRequest,
46
+ RpcSuccessResponse,
47
+ RpcErrorResponse,
48
+ RpcNotification,
49
+ StreamEventParams,
50
+ ControlAckParams,
51
+ } from "./jsonrpc";
52
+ export {
53
+ JsonRpcError,
54
+ RpcErrorCodes,
55
+ createRequest,
56
+ createNotification,
57
+ createStreamEvent,
58
+ createAckNotification,
59
+ createSuccessResponse,
60
+ createErrorResponse,
61
+ parseMessage,
62
+ parseMessageObject,
63
+ isRequest,
64
+ isNotification,
65
+ isSuccessResponse,
66
+ isErrorResponse,
67
+ isInvalid,
68
+ isStreamEvent,
69
+ isControlAck,
70
+ eventTypeToRpcMethod,
71
+ rpcMethodToResponseType,
72
+ } from "./jsonrpc";
73
+
74
+ // RPC Client
75
+ export type { RpcClientConfig, RpcClientState } from "./RpcClient";
76
+ export { RpcClient } from "./RpcClient";