@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,308 @@
1
+ /**
2
+ * Mealy - The Mealy Machine runtime
3
+ *
4
+ * A Mealy Machine is a finite-state machine where outputs depend on
5
+ * both the current state AND the input: (state, input) => (state, output)
6
+ *
7
+ * This runtime orchestrates the complete processing pipeline:
8
+ * 1. Sources receive external input (side effects)
9
+ * 2. Processors process inputs (pure Mealy transition functions)
10
+ * 3. Sinks produce output effects (side effects)
11
+ *
12
+ * Architecture:
13
+ * - Inputs enter through Sources (input adapters)
14
+ * - Processors transform inputs (pure functions, state is means)
15
+ * - Sinks produce actions (output adapters)
16
+ *
17
+ * @template TState - The state type (accumulator, means to an end)
18
+ * @template TInput - The input/output type for Processors
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const mealy = createMealy({
23
+ * processor: messageProcessor,
24
+ * store: new MemoryStore(),
25
+ * initialState: { text: '' },
26
+ * sinks: [sseSink, logSink],
27
+ * });
28
+ *
29
+ * // Process an input
30
+ * mealy.process('agent_123', input);
31
+ * ```
32
+ */
33
+
34
+ import type { Processor } from "./Processor";
35
+ import type { Store } from "./Store";
36
+ import type { Sink, SinkDefinition } from "./Sink";
37
+ import { createLogger } from "commonxjs/logger";
38
+
39
+ const logger = createLogger("engine/Mealy");
40
+
41
+ /**
42
+ * MealyConfig - Configuration for creating a Mealy instance
43
+ */
44
+ export interface MealyConfig<TState, TInput> {
45
+ /**
46
+ * The processor function to execute (pure Mealy transition function)
47
+ */
48
+ processor: Processor<TState, TInput, TInput>;
49
+
50
+ /**
51
+ * The store for state persistence
52
+ */
53
+ store: Store<TState>;
54
+
55
+ /**
56
+ * Initial state for new IDs
57
+ */
58
+ initialState: TState;
59
+
60
+ /**
61
+ * Sinks to receive outputs
62
+ * Can be simple Sink functions or SinkDefinitions with filter/name
63
+ */
64
+ sinks?: (Sink<TInput> | SinkDefinition<TInput>)[];
65
+
66
+ /**
67
+ * Whether to recursively process outputs
68
+ * If true, outputs are fed back into the processor
69
+ *
70
+ * @default true
71
+ */
72
+ recursive?: boolean;
73
+
74
+ /**
75
+ * Maximum recursion depth to prevent infinite loops
76
+ *
77
+ * @default 100
78
+ */
79
+ maxDepth?: number;
80
+ }
81
+
82
+ /**
83
+ * ProcessResult - Result of processing an input
84
+ */
85
+ export interface ProcessResult<TState, TOutput> {
86
+ /**
87
+ * The new state after processing
88
+ */
89
+ state: TState;
90
+
91
+ /**
92
+ * All outputs produced (including from recursion)
93
+ */
94
+ outputs: TOutput[];
95
+
96
+ /**
97
+ * Number of processor invocations (including recursion)
98
+ */
99
+ processCount: number;
100
+ }
101
+
102
+ /**
103
+ * Mealy - Mealy Machine runtime
104
+ *
105
+ * Implements the Mealy Machine pattern: (state, input) => (state, output)
106
+ * where output depends on both current state and input.
107
+ */
108
+ export class Mealy<TState, TInput> {
109
+ private readonly processor: Processor<TState, TInput, TInput>;
110
+ private readonly store: Store<TState>;
111
+ private readonly initialState: TState;
112
+ private readonly sinks: (Sink<TInput> | SinkDefinition<TInput>)[];
113
+ private readonly recursive: boolean;
114
+ private readonly maxDepth: number;
115
+
116
+ constructor(config: MealyConfig<TState, TInput>) {
117
+ this.processor = config.processor;
118
+ this.store = config.store;
119
+ this.initialState = config.initialState;
120
+ this.sinks = config.sinks ?? [];
121
+ this.recursive = config.recursive ?? true;
122
+ this.maxDepth = config.maxDepth ?? 100;
123
+
124
+ logger.debug("Mealy instance created", {
125
+ sinkCount: this.sinks.length,
126
+ recursive: this.recursive,
127
+ maxDepth: this.maxDepth,
128
+ });
129
+ }
130
+
131
+ /**
132
+ * Process an input through the Mealy Machine
133
+ *
134
+ * @param id - Unique identifier (e.g., agentId)
135
+ * @param input - The input to process
136
+ * @returns Result containing new state and all outputs
137
+ */
138
+ process(id: string, input: TInput): ProcessResult<TState, TInput> {
139
+ return this.processInternal(id, input, 0);
140
+ }
141
+
142
+ /**
143
+ * Internal process with depth tracking
144
+ */
145
+ private processInternal(id: string, input: TInput, depth: number): ProcessResult<TState, TInput> {
146
+ // Guard against infinite recursion
147
+ if (depth >= this.maxDepth) {
148
+ logger.warn("Max recursion depth reached", {
149
+ id,
150
+ maxDepth: this.maxDepth,
151
+ depth,
152
+ });
153
+ return {
154
+ state: this.store.get(id) ?? this.initialState,
155
+ outputs: [],
156
+ processCount: 0,
157
+ };
158
+ }
159
+
160
+ // 1. Get current state (or initialize)
161
+ let state = this.store.get(id);
162
+ if (state === undefined) {
163
+ state = this.initialState;
164
+ }
165
+
166
+ // 2. Execute pure processor function (Mealy transition)
167
+ const [newState, outputs] = this.processor(state, input);
168
+
169
+ // 3. Save new state to store
170
+ this.store.set(id, newState);
171
+
172
+ // 4. Collect all outputs
173
+ const allOutputs: TInput[] = [...outputs];
174
+ let processCount = 1;
175
+
176
+ // 5. Send outputs to sinks
177
+ if (outputs.length > 0) {
178
+ this.sendToSinks(id, outputs);
179
+ }
180
+
181
+ // 6. Optionally recurse on outputs
182
+ if (this.recursive) {
183
+ for (const output of outputs) {
184
+ const result = this.processInternal(id, output, depth + 1);
185
+ allOutputs.push(...result.outputs);
186
+ processCount += result.processCount;
187
+ }
188
+ }
189
+
190
+ return {
191
+ state: newState,
192
+ outputs: allOutputs,
193
+ processCount,
194
+ };
195
+ }
196
+
197
+ /**
198
+ * Send outputs to all sinks
199
+ */
200
+ private sendToSinks(id: string, outputs: TInput[]): void {
201
+ for (const sink of this.sinks) {
202
+ // Check if sink is a function or SinkDefinition
203
+ if (typeof sink === "function") {
204
+ // Simple Sink function: (id, outputs) => void
205
+ try {
206
+ const result = sink(id, outputs);
207
+ if (result instanceof Promise) {
208
+ result.catch((error) => {
209
+ logger.error("Sink error (async)", { id, error });
210
+ });
211
+ }
212
+ } catch (error) {
213
+ logger.error("Sink error (sync)", { id, error });
214
+ }
215
+ } else {
216
+ // SinkDefinition with filter/name
217
+ const filteredOutputs = sink.filter ? outputs.filter(sink.filter) : outputs;
218
+
219
+ if (filteredOutputs.length === 0) {
220
+ continue;
221
+ }
222
+
223
+ try {
224
+ const result = sink.sink(id, filteredOutputs);
225
+ if (result instanceof Promise) {
226
+ result.catch((error) => {
227
+ logger.error("Named sink error (async)", {
228
+ id,
229
+ sinkName: sink.name,
230
+ error,
231
+ });
232
+ });
233
+ }
234
+ } catch (error) {
235
+ logger.error("Named sink error (sync)", {
236
+ id,
237
+ sinkName: sink.name,
238
+ error,
239
+ });
240
+ }
241
+ }
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Get current state for an ID (without processing)
247
+ */
248
+ getState(id: string): TState | undefined {
249
+ return this.store.get(id);
250
+ }
251
+
252
+ /**
253
+ * Check if state exists for an ID
254
+ */
255
+ hasState(id: string): boolean {
256
+ return this.store.has(id);
257
+ }
258
+
259
+ /**
260
+ * Delete state for an ID (cleanup)
261
+ */
262
+ cleanup(id: string): void {
263
+ logger.debug("Cleaning up state", { id });
264
+ this.store.delete(id);
265
+ }
266
+
267
+ /**
268
+ * Add a sink at runtime
269
+ */
270
+ addSink(sink: Sink<TInput> | SinkDefinition<TInput>): void {
271
+ const sinkName = typeof sink === "function" ? "(anonymous)" : sink.name;
272
+ logger.debug("Adding sink", { sinkName });
273
+ this.sinks.push(sink);
274
+ }
275
+
276
+ /**
277
+ * Remove a sink by name (only works for SinkDefinitions)
278
+ */
279
+ removeSink(name: string): boolean {
280
+ const index = this.sinks.findIndex((s) => typeof s !== "function" && s.name === name);
281
+ if (index !== -1) {
282
+ this.sinks.splice(index, 1);
283
+ logger.debug("Removed sink", { name });
284
+ return true;
285
+ }
286
+ logger.debug("Sink not found for removal", { name });
287
+ return false;
288
+ }
289
+ }
290
+
291
+ /**
292
+ * createMealy - Factory function for creating Mealy Machine instances
293
+ *
294
+ * @example
295
+ * ```typescript
296
+ * const mealy = createMealy({
297
+ * processor: myProcessor,
298
+ * store: new MemoryStore(),
299
+ * initialState: { count: 0 },
300
+ * sinks: [logSink],
301
+ * });
302
+ * ```
303
+ */
304
+ export function createMealy<TState, TInput>(
305
+ config: MealyConfig<TState, TInput>
306
+ ): Mealy<TState, TInput> {
307
+ return new Mealy(config);
308
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Processor - Core pure function type for stream processing
3
+ *
4
+ * A Processor is a pure function that takes a state and an input,
5
+ * and returns a new state along with outputs.
6
+ *
7
+ * Pattern: (state, input) => [newState, outputs]
8
+ *
9
+ * Key properties:
10
+ * - Pure function (no side effects)
11
+ * - Deterministic (same input → same output)
12
+ * - State is a means (accumulator), outputs are the goal
13
+ *
14
+ * @template TState - The state type (internal accumulator)
15
+ * @template TInput - The input type
16
+ * @template TOutput - The output type (emissions)
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const messageProcessor: Processor<MsgState, StreamEvent, MsgEvent> =
21
+ * (state, input) => {
22
+ * switch (input.type) {
23
+ * case 'text_delta':
24
+ * return [{ ...state, buffer: state.buffer + input.data.text }, []];
25
+ * case 'message_stop':
26
+ * return [{ buffer: '' }, [{ type: 'assistant_message', content: state.buffer }]];
27
+ * default:
28
+ * return [state, []];
29
+ * }
30
+ * };
31
+ * ```
32
+ */
33
+ export type Processor<TState, TInput, TOutput> = (
34
+ state: Readonly<TState>,
35
+ input: TInput
36
+ ) => [TState, TOutput[]];
37
+
38
+ /**
39
+ * ProcessorResult - The return type of a Processor
40
+ *
41
+ * A tuple containing:
42
+ * - [0] newState: The updated state after processing
43
+ * - [1] outputs: Array of outputs to emit
44
+ */
45
+ export type ProcessorResult<TState, TOutput> = [TState, TOutput[]];
46
+
47
+ /**
48
+ * ProcessorDefinition - Metadata for a Processor
49
+ */
50
+ export interface ProcessorDefinition<TState, TInput, TOutput> {
51
+ /**
52
+ * Unique name for this processor
53
+ */
54
+ name: string;
55
+
56
+ /**
57
+ * The pure processor function
58
+ */
59
+ processor: Processor<TState, TInput, TOutput>;
60
+
61
+ /**
62
+ * Initial state factory
63
+ */
64
+ initialState: () => TState;
65
+
66
+ /**
67
+ * Optional description
68
+ */
69
+ description?: string;
70
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Sink - Output adapter for Mealy Machine
3
+ *
4
+ * A Sink receives outputs from Processors and produces external effects.
5
+ * This is a pure function type - lifecycle management belongs to higher layers.
6
+ *
7
+ * Pattern: (id, outputs) => void | Promise<void>
8
+ *
9
+ * @template TOutput - The output type received from Processors
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * // Sync sink (logging)
14
+ * const logSink: Sink<AgentEvent> = (id, outputs) => {
15
+ * outputs.forEach(output => console.log(`[${id}]`, output));
16
+ * };
17
+ *
18
+ * // Async sink (network)
19
+ * const sseSink: Sink<AgentEvent> = async (id, outputs) => {
20
+ * for (const output of outputs) {
21
+ * await sseConnection.send(id, output);
22
+ * }
23
+ * };
24
+ * ```
25
+ */
26
+ export type Sink<TOutput> = (id: string, outputs: TOutput[]) => void | Promise<void>;
27
+
28
+ /**
29
+ * SinkDefinition - Named Sink with metadata
30
+ *
31
+ * Use this when you need to identify sinks by name.
32
+ */
33
+ export interface SinkDefinition<TOutput> {
34
+ /**
35
+ * Unique name for this sink
36
+ */
37
+ name: string;
38
+
39
+ /**
40
+ * Optional description
41
+ */
42
+ description?: string;
43
+
44
+ /**
45
+ * Optional filter to select which outputs to process
46
+ *
47
+ * If not provided, all outputs are processed.
48
+ * Return true to process the output, false to skip.
49
+ */
50
+ filter?: (output: TOutput) => boolean;
51
+
52
+ /**
53
+ * The sink function
54
+ */
55
+ sink: Sink<TOutput>;
56
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Source - Input adapter for Mealy Machine
3
+ *
4
+ * A Source transforms external requests into internal events.
5
+ * This is a pure function type - lifecycle management belongs to higher layers.
6
+ *
7
+ * Pattern: (request) => AsyncIterable<input>
8
+ *
9
+ * @template TInput - The event type produced for Processors
10
+ * @template TRequest - The request type received from external (default: void)
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * // Simple source (no request)
15
+ * const timerSource: Source<TimerEvent> = async function* () {
16
+ * while (true) {
17
+ * yield { type: 'tick', timestamp: Date.now() };
18
+ * await sleep(1000);
19
+ * }
20
+ * };
21
+ *
22
+ * // Source with request
23
+ * const apiSource: Source<ApiEvent, ApiRequest> = async function* (request) {
24
+ * const response = await fetch(request.url);
25
+ * yield { type: 'response', data: await response.json() };
26
+ * };
27
+ * ```
28
+ */
29
+ export type Source<TInput, TRequest = void> = (request: TRequest) => AsyncIterable<TInput>;
30
+
31
+ /**
32
+ * SourceDefinition - Named Source with metadata
33
+ *
34
+ * Use this when you need to identify sources by name.
35
+ */
36
+ export interface SourceDefinition<TInput, TRequest = void> {
37
+ /**
38
+ * Unique name for this source
39
+ */
40
+ name: string;
41
+
42
+ /**
43
+ * Optional description
44
+ */
45
+ description?: string;
46
+
47
+ /**
48
+ * The source function
49
+ */
50
+ source: Source<TInput, TRequest>;
51
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Store - State storage interface for stream processing
3
+ *
4
+ * A Store abstracts state persistence, allowing processors to be stateless
5
+ * while maintaining state externally.
6
+ *
7
+ * @template T - The state type to store
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const store = new MemoryStore<AgentState>();
12
+ * store.set('agent_123', { count: 0 });
13
+ * const state = store.get('agent_123');
14
+ * ```
15
+ */
16
+ export interface Store<T> {
17
+ /**
18
+ * Get state by ID
19
+ * @param id - Unique identifier (e.g., agentId, sessionId)
20
+ * @returns The stored state or undefined if not found
21
+ */
22
+ get(id: string): T | undefined;
23
+
24
+ /**
25
+ * Set state for an ID
26
+ * @param id - Unique identifier
27
+ * @param state - The state to store
28
+ */
29
+ set(id: string, state: T): void;
30
+
31
+ /**
32
+ * Delete state for an ID
33
+ * @param id - Unique identifier
34
+ */
35
+ delete(id: string): void;
36
+
37
+ /**
38
+ * Check if state exists for an ID
39
+ * @param id - Unique identifier
40
+ * @returns True if state exists
41
+ */
42
+ has(id: string): boolean;
43
+ }
44
+
45
+ /**
46
+ * MemoryStore - In-memory implementation of Store
47
+ *
48
+ * Stores state in a Map. Suitable for development and single-process deployments.
49
+ * For production multi-process scenarios, use RedisStore or PostgresStore.
50
+ *
51
+ * @template T - The state type to store
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * const store = new MemoryStore<MyState>();
56
+ * store.set('session_1', { count: 0 });
57
+ * ```
58
+ */
59
+ export class MemoryStore<T> implements Store<T> {
60
+ private states = new Map<string, T>();
61
+
62
+ get(id: string): T | undefined {
63
+ return this.states.get(id);
64
+ }
65
+
66
+ set(id: string, state: T): void {
67
+ this.states.set(id, state);
68
+ }
69
+
70
+ delete(id: string): void {
71
+ this.states.delete(id);
72
+ }
73
+
74
+ has(id: string): boolean {
75
+ return this.states.has(id);
76
+ }
77
+
78
+ /**
79
+ * Clear all stored states
80
+ */
81
+ clear(): void {
82
+ this.states.clear();
83
+ }
84
+
85
+ /**
86
+ * Get the number of stored states
87
+ */
88
+ get size(): number {
89
+ return this.states.size;
90
+ }
91
+
92
+ /**
93
+ * Get all stored IDs
94
+ */
95
+ keys(): IterableIterator<string> {
96
+ return this.states.keys();
97
+ }
98
+ }