@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,166 @@
1
+ # @agentxjs/core/mq
2
+
3
+ MessageQueue - Reliable message delivery with persistence guarantee.
4
+
5
+ ## Overview
6
+
7
+ The mq module provides standard interfaces for message queue implementations. It defines platform-agnostic contracts that can be implemented by different providers:
8
+
9
+ - **Node.js**: SQLite persistence
10
+ - **Cloudflare Workers**: Durable Objects Storage
11
+ - **Browser**: IndexedDB
12
+
13
+ ```
14
+ ┌─────────────────────────────────────────────────────────────┐
15
+ │ MessageQueue │
16
+ │ │
17
+ │ publish() ─────► Storage ─────► recover() │
18
+ │ │ │ │
19
+ │ ▼ │ │
20
+ │ RxJS Subject ──────┼────► subscribe() │
21
+ │ │ │
22
+ │ ack() ◄────────────┴────► getOffset() │
23
+ │ │
24
+ └─────────────────────────────────────────────────────────────┘
25
+ ```
26
+
27
+ ## Key Concepts
28
+
29
+ ### At-Least-Once Delivery
30
+
31
+ Messages are guaranteed to be delivered at least once through:
32
+
33
+ 1. **Persistence**: Messages are stored before broadcast
34
+ 2. **Offset Tracking**: Consumer position is persisted via `ack()`
35
+ 3. **Recovery**: Missed messages can be fetched via `recover()`
36
+
37
+ ### Real-time + Recovery
38
+
39
+ ```typescript
40
+ // 1. Subscribe for real-time messages
41
+ queue.subscribe("session-123", (entry) => {
42
+ console.log("Received:", entry.event);
43
+ // After processing, acknowledge
44
+ await queue.ack("client-1", "session-123", entry.offset);
45
+ });
46
+
47
+ // 2. On reconnect, recover missed messages
48
+ const lastOffset = await queue.getOffset("client-1", "session-123");
49
+ const missed = await queue.recover("session-123", lastOffset);
50
+ for (const entry of missed) {
51
+ console.log("Missed:", entry.event);
52
+ }
53
+ ```
54
+
55
+ ## Interfaces
56
+
57
+ ### MessageQueue
58
+
59
+ ```typescript
60
+ interface MessageQueue {
61
+ // Publish message (async for cross-platform support)
62
+ publish(topic: string, event: unknown): Promise<string>;
63
+
64
+ // Subscribe to real-time messages (in-memory pub/sub)
65
+ subscribe(topic: string, handler: (entry: QueueEntry) => void): Unsubscribe;
66
+
67
+ // Acknowledge consumption (persist consumer offset)
68
+ ack(consumerId: string, topic: string, offset: string): Promise<void>;
69
+
70
+ // Get consumer's current offset
71
+ getOffset(consumerId: string, topic: string): Promise<string | null>;
72
+
73
+ // Recover historical messages
74
+ recover(topic: string, afterOffset?: string, limit?: number): Promise<QueueEntry[]>;
75
+
76
+ // Close and release resources
77
+ close(): Promise<void>;
78
+ }
79
+ ```
80
+
81
+ ### QueueEntry
82
+
83
+ ```typescript
84
+ interface QueueEntry {
85
+ readonly offset: string; // Unique, monotonically increasing
86
+ readonly topic: string; // Topic identifier
87
+ readonly event: unknown; // Message payload
88
+ readonly timestamp: number; // Unix milliseconds
89
+ }
90
+ ```
91
+
92
+ ### MessageQueueProvider
93
+
94
+ ```typescript
95
+ interface MessageQueueProvider {
96
+ createQueue(config?: QueueConfig): Promise<MessageQueue>;
97
+ }
98
+ ```
99
+
100
+ ## Usage with Provider
101
+
102
+ ```typescript
103
+ // Node.js
104
+ import { createNodeProvider } from "@agentxjs/node";
105
+
106
+ const provider = createNodeProvider({
107
+ sqlitePath: "./data/queue.db",
108
+ });
109
+ const queue = await provider.createQueue({ retentionMs: 86400000 });
110
+
111
+ // Cloudflare Workers
112
+ import { createCloudflareProvider } from "@agentxjs/cloudflare";
113
+
114
+ const provider = createCloudflareProvider(env);
115
+ const queue = await provider.createQueue();
116
+ ```
117
+
118
+ ## OffsetGenerator
119
+
120
+ Utility for generating monotonically increasing offsets:
121
+
122
+ ```typescript
123
+ import { OffsetGenerator } from "@agentxjs/core/mq";
124
+
125
+ const generator = new OffsetGenerator();
126
+
127
+ const offset1 = generator.generate(); // "lq5x4g2-0000"
128
+ const offset2 = generator.generate(); // "lq5x4g2-0001"
129
+
130
+ // Compare offsets
131
+ OffsetGenerator.compare(offset1, offset2); // -1 (offset1 < offset2)
132
+ ```
133
+
134
+ ### Offset Format
135
+
136
+ ```
137
+ {timestamp_base36}-{sequence_padded}
138
+ lq5x4g2 - 0001
139
+
140
+ - timestamp: Base36 encoded Unix milliseconds
141
+ - sequence: Zero-padded counter for same-millisecond uniqueness
142
+ ```
143
+
144
+ ## Difference from EventBus
145
+
146
+ | | EventBus | MessageQueue |
147
+ | ----------- | -------------------- | ----------------- |
148
+ | Scope | Process-internal | Cross-process |
149
+ | Persistence | None | Platform-specific |
150
+ | Delivery | Best-effort | At-least-once |
151
+ | Recovery | No | Yes (via offset) |
152
+ | Use case | Component decoupling | Reliable delivery |
153
+
154
+ ## Platform Independence
155
+
156
+ This module defines **interfaces only** - no platform-specific code:
157
+
158
+ - No `node:*` imports
159
+ - No `bun:*` imports
160
+ - No SQLite, IndexedDB, or Durable Objects
161
+
162
+ Implementations are provided by platform packages (`@agentxjs/node`, `@agentxjs/cloudflare`).
163
+
164
+ ## License
165
+
166
+ MIT
@@ -0,0 +1,121 @@
1
+ /**
2
+ * OffsetGenerator.test.ts - Unit tests for OffsetGenerator
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach } from "bun:test";
6
+ import { OffsetGenerator } from "../OffsetGenerator";
7
+
8
+ describe("OffsetGenerator", () => {
9
+ let generator: OffsetGenerator;
10
+
11
+ beforeEach(() => {
12
+ generator = new OffsetGenerator();
13
+ });
14
+
15
+ describe("generate", () => {
16
+ it("should generate an offset string", () => {
17
+ const offset = generator.generate();
18
+
19
+ expect(typeof offset).toBe("string");
20
+ expect(offset.length).toBeGreaterThan(0);
21
+ });
22
+
23
+ it("should generate offsets in expected format", () => {
24
+ const offset = generator.generate();
25
+
26
+ // Format: {timestamp_base36}-{sequence_padded}
27
+ const parts = offset.split("-");
28
+ expect(parts).toHaveLength(2);
29
+
30
+ // First part should be base36 timestamp
31
+ const timestamp = parseInt(parts[0], 36);
32
+ expect(timestamp).toBeGreaterThan(0);
33
+
34
+ // Second part should be zero-padded sequence
35
+ expect(parts[1]).toMatch(/^\d{4}$/);
36
+ });
37
+
38
+ it("should generate monotonically increasing offsets", () => {
39
+ const offsets: string[] = [];
40
+
41
+ for (let i = 0; i < 100; i++) {
42
+ offsets.push(generator.generate());
43
+ }
44
+
45
+ // Each offset should be greater than the previous
46
+ for (let i = 1; i < offsets.length; i++) {
47
+ expect(OffsetGenerator.compare(offsets[i], offsets[i - 1])).toBeGreaterThan(0);
48
+ }
49
+ });
50
+
51
+ it("should handle multiple offsets in same millisecond", () => {
52
+ // Generate many offsets rapidly (likely same millisecond)
53
+ const offsets = new Set<string>();
54
+
55
+ for (let i = 0; i < 1000; i++) {
56
+ offsets.add(generator.generate());
57
+ }
58
+
59
+ // All offsets should be unique
60
+ expect(offsets.size).toBe(1000);
61
+ });
62
+ });
63
+
64
+ describe("compare", () => {
65
+ it("should return negative when a < b", () => {
66
+ const a = generator.generate();
67
+ const b = generator.generate();
68
+
69
+ expect(OffsetGenerator.compare(a, b)).toBeLessThan(0);
70
+ });
71
+
72
+ it("should return positive when a > b", () => {
73
+ const a = generator.generate();
74
+ const b = generator.generate();
75
+
76
+ expect(OffsetGenerator.compare(b, a)).toBeGreaterThan(0);
77
+ });
78
+
79
+ it("should return 0 when a == b", () => {
80
+ const offset = generator.generate();
81
+
82
+ expect(OffsetGenerator.compare(offset, offset)).toBe(0);
83
+ });
84
+
85
+ it("should compare by timestamp first", () => {
86
+ // Create offsets with different timestamps
87
+ const earlier = "lq5x4g1-0005"; // Earlier timestamp, higher sequence
88
+ const later = "lq5x4g2-0001"; // Later timestamp, lower sequence
89
+
90
+ expect(OffsetGenerator.compare(earlier, later)).toBeLessThan(0);
91
+ expect(OffsetGenerator.compare(later, earlier)).toBeGreaterThan(0);
92
+ });
93
+
94
+ it("should compare by sequence when timestamps equal", () => {
95
+ const low = "lq5x4g2-0001";
96
+ const high = "lq5x4g2-0005";
97
+
98
+ expect(OffsetGenerator.compare(low, high)).toBeLessThan(0);
99
+ expect(OffsetGenerator.compare(high, low)).toBeGreaterThan(0);
100
+ });
101
+ });
102
+
103
+ describe("ordering consistency", () => {
104
+ it("should be usable for sorting", () => {
105
+ const offsets: string[] = [];
106
+
107
+ for (let i = 0; i < 50; i++) {
108
+ offsets.push(generator.generate());
109
+ }
110
+
111
+ // Shuffle
112
+ const shuffled = [...offsets].sort(() => Math.random() - 0.5);
113
+
114
+ // Sort using compare
115
+ const sorted = shuffled.sort(OffsetGenerator.compare);
116
+
117
+ // Should match original order
118
+ expect(sorted).toEqual(offsets);
119
+ });
120
+ });
121
+ });
@@ -0,0 +1,18 @@
1
+ /**
2
+ * MessageQueue Module
3
+ *
4
+ * Provides standard interfaces for reliable message delivery:
5
+ * - MessageQueue: Core queue interface
6
+ * - MessageQueueProvider: Factory for platform-specific implementations
7
+ * - OffsetGenerator: Utility for generating monotonic offsets
8
+ */
9
+
10
+ export type {
11
+ QueueEntry,
12
+ QueueConfig,
13
+ Unsubscribe,
14
+ MessageQueue,
15
+ MessageQueueProvider,
16
+ } from "./types";
17
+
18
+ export { OffsetGenerator } from "./OffsetGenerator";
@@ -0,0 +1,172 @@
1
+ /**
2
+ * MessageQueue - Reliable message delivery with persistence guarantee
3
+ *
4
+ * Standard interface for message queue implementations:
5
+ * - In-memory pub/sub for real-time delivery (RxJS)
6
+ * - Persistence for recovery guarantee (platform-specific)
7
+ * - Consumer offset tracking for at-least-once delivery
8
+ *
9
+ * Platform implementations:
10
+ * - Node.js: SQLite
11
+ * - Cloudflare Workers: Durable Objects Storage
12
+ * - Browser: IndexedDB
13
+ */
14
+
15
+ // ============================================================================
16
+ // Queue Entry
17
+ // ============================================================================
18
+
19
+ /**
20
+ * QueueEntry - A single entry in the message queue
21
+ */
22
+ export interface QueueEntry {
23
+ /**
24
+ * Unique offset for this entry (monotonically increasing)
25
+ * Format: "{timestamp_base36}-{sequence}"
26
+ * Example: "lq5x4g2-0001"
27
+ */
28
+ readonly offset: string;
29
+
30
+ /**
31
+ * Topic this entry belongs to (e.g., sessionId, agentId)
32
+ */
33
+ readonly topic: string;
34
+
35
+ /**
36
+ * The actual event payload
37
+ */
38
+ readonly event: unknown;
39
+
40
+ /**
41
+ * Timestamp when the event was published (Unix milliseconds)
42
+ */
43
+ readonly timestamp: number;
44
+ }
45
+
46
+ // ============================================================================
47
+ // Queue Configuration
48
+ // ============================================================================
49
+
50
+ /**
51
+ * QueueConfig - Platform-agnostic configuration
52
+ *
53
+ * Platform-specific options (path, namespace, etc.) are handled by Provider.
54
+ */
55
+ export interface QueueConfig {
56
+ /**
57
+ * Message retention timeout (milliseconds)
58
+ * Messages older than this may be deleted during cleanup
59
+ * @default 86400000 (24 hours)
60
+ */
61
+ retentionMs?: number;
62
+
63
+ /**
64
+ * Maximum entries per topic (optional)
65
+ * When exceeded, oldest entries may be removed
66
+ */
67
+ maxEntriesPerTopic?: number;
68
+ }
69
+
70
+ // ============================================================================
71
+ // Unsubscribe
72
+ // ============================================================================
73
+
74
+ /**
75
+ * Unsubscribe function - call to stop receiving messages
76
+ */
77
+ export type Unsubscribe = () => void;
78
+
79
+ // ============================================================================
80
+ // MessageQueue Interface
81
+ // ============================================================================
82
+
83
+ /**
84
+ * MessageQueue - Standard interface for message queue
85
+ *
86
+ * All methods that involve storage are async to support all platforms.
87
+ */
88
+ export interface MessageQueue {
89
+ /**
90
+ * Publish an event to a topic
91
+ *
92
+ * - Persists to storage (async)
93
+ * - Broadcasts to all subscribers (in-memory, sync)
94
+ *
95
+ * @param topic - Topic identifier (e.g., sessionId)
96
+ * @param event - Event payload
97
+ * @returns Offset of the published entry
98
+ */
99
+ publish(topic: string, event: unknown): Promise<string>;
100
+
101
+ /**
102
+ * Subscribe to real-time events on a topic
103
+ *
104
+ * Receives events published after subscription (in-memory pub/sub).
105
+ * For historical events, use recover() first.
106
+ *
107
+ * @param topic - Topic identifier
108
+ * @param handler - Callback for each event entry
109
+ * @returns Unsubscribe function
110
+ */
111
+ subscribe(topic: string, handler: (entry: QueueEntry) => void): Unsubscribe;
112
+
113
+ /**
114
+ * Acknowledge consumption (update consumer offset)
115
+ *
116
+ * Call this after successfully processing an event.
117
+ * The offset is persisted for recovery.
118
+ *
119
+ * @param consumerId - Consumer identifier (e.g., connectionId)
120
+ * @param topic - Topic identifier
121
+ * @param offset - Offset of the consumed entry
122
+ */
123
+ ack(consumerId: string, topic: string, offset: string): Promise<void>;
124
+
125
+ /**
126
+ * Get consumer's current offset
127
+ *
128
+ * @param consumerId - Consumer identifier
129
+ * @param topic - Topic identifier
130
+ * @returns Offset or null if consumer not found
131
+ */
132
+ getOffset(consumerId: string, topic: string): Promise<string | null>;
133
+
134
+ /**
135
+ * Recover historical events from persistence
136
+ *
137
+ * Used for reconnection recovery - fetches events after an offset.
138
+ *
139
+ * @param topic - Topic identifier
140
+ * @param afterOffset - Start offset (exclusive), omit for all history
141
+ * @param limit - Maximum entries to return (default: 1000)
142
+ * @returns Array of event entries
143
+ */
144
+ recover(topic: string, afterOffset?: string, limit?: number): Promise<QueueEntry[]>;
145
+
146
+ /**
147
+ * Close the message queue and release resources
148
+ */
149
+ close(): Promise<void>;
150
+ }
151
+
152
+ // ============================================================================
153
+ // MessageQueue Provider
154
+ // ============================================================================
155
+
156
+ /**
157
+ * MessageQueueProvider - Factory for creating MessageQueue instances
158
+ *
159
+ * Each platform implements its own provider:
160
+ * - SqliteMessageQueueProvider (Node.js)
161
+ * - DurableMessageQueueProvider (Cloudflare Workers)
162
+ * - IndexedDBMessageQueueProvider (Browser)
163
+ */
164
+ export interface MessageQueueProvider {
165
+ /**
166
+ * Create a MessageQueue instance
167
+ *
168
+ * @param config - Platform-agnostic configuration
169
+ * @returns MessageQueue instance
170
+ */
171
+ createQueue(config?: QueueConfig): Promise<MessageQueue>;
172
+ }