@agentxjs/agent 0.1.6

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.
package/README.md ADDED
@@ -0,0 +1,539 @@
1
+ # @agentxjs/agent
2
+
3
+ > Event Processing Unit for AI Agent conversations
4
+
5
+ ## Overview
6
+
7
+ `@agentxjs/agent` provides the **AgentEngine** - a pure event processor that transforms streaming LLM outputs into structured conversation events using the **Mealy Machine** pattern.
8
+
9
+ **Key Characteristics:**
10
+
11
+ - **Pure Event Processing** - `(state, input) → (state, outputs)`
12
+ - **Independent & Testable** - No dependencies on Runtime infrastructure
13
+ - **4-Layer Event System** - Stream → State → Message → Turn
14
+ - **Composable Architecture** - Build custom processors from primitives
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ pnpm add @agentxjs/agent
20
+ ```
21
+
22
+ ---
23
+
24
+ ## Architecture
25
+
26
+ ```text
27
+ ┌──────────────────────────────────────────────────────────────┐
28
+ │ AgentEngine │
29
+ ├──────────────────────────────────────────────────────────────┤
30
+ │ │
31
+ │ Driver (event producer) │
32
+ │ │ │
33
+ │ │ yields StreamEvent │
34
+ │ ▼ │
35
+ │ ┌─────────────────────────────────────────────────────┐ │
36
+ │ │ MealyMachine │ │
37
+ │ │ (Pure Mealy Machine - stateless transformation) │ │
38
+ │ │ │ │
39
+ │ │ StreamEvent → [process] → AgentOutput │ │
40
+ │ │ │ │
41
+ │ │ Composes: │ │
42
+ │ │ • MessageAssembler (Stream → Message) │ │
43
+ │ │ • StateTracker (Stream → State) │ │
44
+ │ │ • TurnTracker (Message → Turn) │ │
45
+ │ └─────────────────────────────────────────────────────┘ │
46
+ │ │ │
47
+ │ │ emits AgentOutput │
48
+ │ ▼ │
49
+ │ Presenter (event consumer) │
50
+ │ │
51
+ └──────────────────────────────────────────────────────────────┘
52
+ ```
53
+
54
+ **Key Point**: `AgentEngine` is independent of Runtime (Container, Session, Bus). It can be tested in isolation with mock Driver and Presenter.
55
+
56
+ ---
57
+
58
+ ## Quick Start
59
+
60
+ ### Basic Usage
61
+
62
+ ```typescript
63
+ import { createAgent } from "@agentxjs/agent";
64
+ import type { AgentDriver, AgentPresenter } from "@agentxjs/types/agent";
65
+
66
+ // Create a simple driver (produces stream events)
67
+ const driver: AgentDriver = {
68
+ name: "SimpleDriver",
69
+ async *receive(message) {
70
+ yield { type: "message_start", timestamp: Date.now(), data: {} };
71
+ yield { type: "text_delta", timestamp: Date.now(), data: { text: "Hello" } };
72
+ yield { type: "text_delta", timestamp: Date.now(), data: { text: " World" } };
73
+ yield { type: "message_stop", timestamp: Date.now(), data: { stopReason: "end_turn" } };
74
+ },
75
+ interrupt() {},
76
+ };
77
+
78
+ // Create a presenter (consumes output events)
79
+ const presenter: AgentPresenter = {
80
+ name: "ConsolePresenter",
81
+ present(agentId, output) {
82
+ console.log(output.type, output.data);
83
+ },
84
+ };
85
+
86
+ // Create agent
87
+ const agent = createAgent({
88
+ driver,
89
+ presenter,
90
+ context: {
91
+ agentId: "agent_123",
92
+ createdAt: Date.now(),
93
+ },
94
+ });
95
+
96
+ // Subscribe to events
97
+ agent.on("text_delta", (e) => {
98
+ process.stdout.write(e.data.text);
99
+ });
100
+
101
+ agent.on("assistant_message", (e) => {
102
+ console.log("\nAssistant:", e.data.content);
103
+ });
104
+
105
+ // Send message
106
+ await agent.receive("Hello!");
107
+
108
+ // Cleanup
109
+ await agent.destroy();
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Core Concepts
115
+
116
+ ### 1. AgentEngine Interface
117
+
118
+ ```typescript
119
+ interface AgentEngine {
120
+ readonly agentId: string;
121
+ readonly createdAt: number;
122
+ readonly state: AgentState;
123
+ readonly messageQueue: MessageQueue;
124
+
125
+ // Send message
126
+ receive(message: string | UserMessage): Promise<void>;
127
+
128
+ // Subscribe to events
129
+ on(handler: AgentEventHandler): Unsubscribe;
130
+ on(handlers: EventHandlerMap): Unsubscribe;
131
+ on(type: string, handler: AgentEventHandler): Unsubscribe;
132
+ on(types: string[], handler: AgentEventHandler): Unsubscribe;
133
+
134
+ // React-style subscription
135
+ react(handlers: ReactHandlerMap): Unsubscribe;
136
+
137
+ // State changes
138
+ onStateChange(handler: StateChangeHandler): Unsubscribe;
139
+
140
+ // Lifecycle
141
+ onReady(handler: () => void): Unsubscribe;
142
+ onDestroy(handler: () => void): Unsubscribe;
143
+
144
+ // Control
145
+ interrupt(): void;
146
+ destroy(): Promise<void>;
147
+
148
+ // Advanced
149
+ use(middleware: AgentMiddleware): Unsubscribe;
150
+ intercept(interceptor: AgentInterceptor): Unsubscribe;
151
+ }
152
+ ```
153
+
154
+ ### 2. AgentState
155
+
156
+ Agent state machine:
157
+
158
+ ```typescript
159
+ type AgentState =
160
+ | "idle" // Waiting for user input
161
+ | "thinking" // LLM is thinking
162
+ | "responding" // LLM is generating response
163
+ | "planning_tool" // Generating tool call parameters
164
+ | "awaiting_tool_result"; // Waiting for tool execution
165
+
166
+ // State transitions
167
+ agent.onStateChange(({ prev, current }) => {
168
+ console.log(`State: ${prev} → ${current}`);
169
+ });
170
+ ```
171
+
172
+ ### 3. Event Subscription
173
+
174
+ Multiple subscription patterns:
175
+
176
+ ```typescript
177
+ // 1. Subscribe to specific event type
178
+ agent.on("text_delta", (e) => {
179
+ process.stdout.write(e.data.text);
180
+ });
181
+
182
+ // 2. Subscribe to multiple types
183
+ agent.on(["message_start", "message_stop"], (e) => {
184
+ console.log(e.type);
185
+ });
186
+
187
+ // 3. Subscribe to all events
188
+ agent.on((e) => {
189
+ console.log(e.type);
190
+ });
191
+
192
+ // 4. React-style handlers (camelCase with 'on' prefix)
193
+ agent.react({
194
+ onTextDelta: (e) => process.stdout.write(e.data.text),
195
+ onAssistantMessage: (e) => console.log(e.data.content),
196
+ onToolCallMessage: (e) => console.log("Tool:", e.data.toolCall.name),
197
+ });
198
+
199
+ // 5. Batch subscription
200
+ agent.on({
201
+ text_delta: (e) => process.stdout.write(e.data.text),
202
+ assistant_message: (e) => console.log(e.data.content),
203
+ });
204
+ ```
205
+
206
+ ### 4. Middleware & Interceptors
207
+
208
+ ```typescript
209
+ // Middleware: Intercept incoming messages (before driver)
210
+ agent.use(async (message, next) => {
211
+ console.log("User:", message.content);
212
+ return next(message);
213
+ });
214
+
215
+ // Interceptor: Intercept outgoing events (after engine)
216
+ agent.intercept((output, next) => {
217
+ console.log("Event:", output.type);
218
+ return next(output);
219
+ });
220
+ ```
221
+
222
+ ---
223
+
224
+ ## 4-Layer Event System
225
+
226
+ ### Layer 1: Stream Events (Real-time)
227
+
228
+ ```typescript
229
+ // Text streaming
230
+ agent.on("message_start", (e) => {
231
+ /* ... */
232
+ });
233
+ agent.on("text_delta", (e) => process.stdout.write(e.data.text));
234
+ agent.on("message_stop", (e) => {
235
+ /* ... */
236
+ });
237
+
238
+ // Tool use
239
+ agent.on("tool_use_start", (e) => {
240
+ /* ... */
241
+ });
242
+ agent.on("input_json_delta", (e) => {
243
+ /* ... */
244
+ });
245
+ agent.on("tool_use_stop", (e) => {
246
+ /* ... */
247
+ });
248
+ ```
249
+
250
+ ### Layer 2: State Events
251
+
252
+ ```typescript
253
+ // Conversation lifecycle
254
+ agent.on("conversation_start", (e) => {
255
+ /* ... */
256
+ });
257
+ agent.on("conversation_thinking", (e) => {
258
+ /* ... */
259
+ });
260
+ agent.on("conversation_responding", (e) => {
261
+ /* ... */
262
+ });
263
+ agent.on("conversation_end", (e) => {
264
+ /* ... */
265
+ });
266
+
267
+ // Tool lifecycle
268
+ agent.on("tool_planned", (e) => {
269
+ /* ... */
270
+ });
271
+ agent.on("tool_executing", (e) => {
272
+ /* ... */
273
+ });
274
+ agent.on("tool_completed", (e) => {
275
+ /* ... */
276
+ });
277
+ ```
278
+
279
+ ### Layer 3: Message Events
280
+
281
+ ```typescript
282
+ // Complete messages
283
+ agent.on("user_message", (e) => {
284
+ console.log("User:", e.data.content);
285
+ });
286
+
287
+ agent.on("assistant_message", (e) => {
288
+ console.log("Assistant:", e.data.content);
289
+ });
290
+
291
+ agent.on("tool_call_message", (e) => {
292
+ console.log("Tool Call:", e.data.toolCall.name);
293
+ });
294
+
295
+ agent.on("tool_result_message", (e) => {
296
+ console.log("Tool Result:", e.data.toolResult.output);
297
+ });
298
+ ```
299
+
300
+ ### Layer 4: Turn Events (Analytics)
301
+
302
+ ```typescript
303
+ // Turn tracking
304
+ agent.on("turn_request", (e) => {
305
+ console.log("Turn started:", e.data.turnId);
306
+ });
307
+
308
+ agent.on("turn_response", (e) => {
309
+ console.log("Turn completed:", {
310
+ duration: e.data.durationMs,
311
+ tokens: e.data.usage,
312
+ });
313
+ });
314
+ ```
315
+
316
+ ---
317
+
318
+ ## MealyMachine - Pure Event Processor
319
+
320
+ The heart of AgentEngine is the **MealyMachine** - a pure function that transforms events:
321
+
322
+ ```typescript
323
+ import { createMealyMachine } from "@agentxjs/agent";
324
+
325
+ // Create machine
326
+ const machine = createMealyMachine();
327
+
328
+ // Process event
329
+ const result = machine.process(
330
+ {
331
+ /* state */
332
+ },
333
+ { type: "text_delta", timestamp: Date.now(), data: { text: "Hi" } }
334
+ );
335
+
336
+ // Result contains:
337
+ // - state: Updated state
338
+ // - outputs: Generated events (message, state, turn events)
339
+ ```
340
+
341
+ ### Mealy Machine Pattern
342
+
343
+ ```
344
+ (state, input) → (state, outputs)
345
+ ```
346
+
347
+ **Key Properties:**
348
+
349
+ 1. **Pure Function** - No side effects, same input → same output
350
+ 2. **Testable** - No mocks needed, just assertions on outputs
351
+ 3. **Composable** - Build complex machines from simple processors
352
+
353
+ ### Internal Processors
354
+
355
+ MealyMachine composes three processors:
356
+
357
+ 1. **MessageAssembler** - Assembles complete messages from stream chunks
358
+ 2. **StateEventProcessor** - Generates state transition events
359
+ 3. **TurnTracker** - Tracks request-response cycles
360
+
361
+ ```typescript
362
+ import {
363
+ messageAssemblerProcessor,
364
+ stateEventProcessor,
365
+ turnTrackerProcessor,
366
+ } from "@agentxjs/agent";
367
+
368
+ // These are exported for advanced use cases (custom machines)
369
+ ```
370
+
371
+ ---
372
+
373
+ ## Building Custom Processors
374
+
375
+ For advanced use cases, you can build custom event processors:
376
+
377
+ ```typescript
378
+ import {
379
+ type Processor,
380
+ type ProcessorResult,
381
+ combineProcessors,
382
+ filterProcessor,
383
+ mapOutput,
384
+ } from "@agentxjs/agent";
385
+
386
+ // Define custom processor
387
+ const myProcessor: Processor<MyState, MyInput, MyOutput> = (state, input) => {
388
+ // Process input, update state, emit outputs
389
+ return {
390
+ state: updatedState,
391
+ outputs: [output1, output2],
392
+ };
393
+ };
394
+
395
+ // Combine with built-in processors
396
+ const combined = combineProcessors(messageAssemblerProcessor, myProcessor);
397
+
398
+ // Add filters
399
+ const filtered = filterProcessor(myProcessor, (input) => input.type === "text_delta");
400
+
401
+ // Transform outputs
402
+ const mapped = mapOutput(myProcessor, (output) => ({ ...output, extra: true }));
403
+ ```
404
+
405
+ ### Processor Combinators
406
+
407
+ ```typescript
408
+ // Combine multiple processors
409
+ combineProcessors(p1, p2, p3);
410
+
411
+ // Combine initial states
412
+ combineInitialStates(s1, s2, s3);
413
+
414
+ // Chain processors (output of p1 → input of p2)
415
+ chainProcessors(p1, p2);
416
+
417
+ // Filter inputs
418
+ filterProcessor(p, (input) => input.type === "text_delta");
419
+
420
+ // Map outputs
421
+ mapOutput(p, (output) => ({ ...output, extra: true }));
422
+
423
+ // Add logging
424
+ withLogging(p, "MyProcessor");
425
+
426
+ // Identity (pass-through)
427
+ identityProcessor;
428
+ ```
429
+
430
+ ---
431
+
432
+ ## Testing
433
+
434
+ AgentEngine is designed for easy testing:
435
+
436
+ ```typescript
437
+ import { createAgent } from "@agentxjs/agent";
438
+ import { describe, it, expect } from "vitest";
439
+
440
+ describe("AgentEngine", () => {
441
+ it("processes text deltas", async () => {
442
+ const events: any[] = [];
443
+
444
+ const driver = {
445
+ name: "TestDriver",
446
+ async *receive() {
447
+ yield { type: "message_start", timestamp: Date.now(), data: {} };
448
+ yield { type: "text_delta", timestamp: Date.now(), data: { text: "Hi" } };
449
+ yield { type: "message_stop", timestamp: Date.now(), data: {} };
450
+ },
451
+ interrupt() {},
452
+ };
453
+
454
+ const presenter = {
455
+ name: "TestPresenter",
456
+ present: (id, output) => events.push(output),
457
+ };
458
+
459
+ const agent = createAgent({
460
+ driver,
461
+ presenter,
462
+ context: { agentId: "test", createdAt: Date.now() },
463
+ });
464
+
465
+ await agent.receive("Hello");
466
+
467
+ // Assert events
468
+ expect(events).toMatchObject([
469
+ { type: "conversation_start" },
470
+ { type: "message_start" },
471
+ { type: "text_delta", data: { text: "Hi" } },
472
+ { type: "message_stop" },
473
+ { type: "assistant_message" },
474
+ { type: "conversation_end" },
475
+ ]);
476
+
477
+ await agent.destroy();
478
+ });
479
+ });
480
+ ```
481
+
482
+ ---
483
+
484
+ ## Design Decisions
485
+
486
+ ### Why Mealy Machine?
487
+
488
+ The Mealy Machine pattern offers:
489
+
490
+ 1. **Pure Functions** - Testable without mocks
491
+ 2. **Deterministic** - Same input → same output
492
+ 3. **Composable** - Build complex from simple
493
+ 4. **State is Means** - Output is the goal
494
+
495
+ ### Why Independent from Runtime?
496
+
497
+ AgentEngine is separate from Runtime (Container, Session, Bus) to:
498
+
499
+ 1. **Test in Isolation** - No Runtime infrastructure needed
500
+ 2. **Reusable** - Same engine works in Node.js, Browser, Edge
501
+ 3. **Clear Boundaries** - Event processing vs. lifecycle management
502
+
503
+ ### Why 4 Layers?
504
+
505
+ Each layer serves different consumers:
506
+
507
+ - **Stream** - UI (typewriter effect)
508
+ - **State** - State machines, loading indicators
509
+ - **Message** - Chat history, persistence
510
+ - **Turn** - Analytics, billing
511
+
512
+ ---
513
+
514
+ ## Package Dependencies
515
+
516
+ ```text
517
+ @agentxjs/types Type definitions
518
+
519
+ @agentxjs/common Logger facade
520
+
521
+ @agentxjs/agent This package (event processing)
522
+
523
+ @agentxjs/runtime Runtime layer (Container, Session, Bus)
524
+ ```
525
+
526
+ ---
527
+
528
+ ## Related Packages
529
+
530
+ - **[@agentxjs/types](../types)** - Type definitions
531
+ - **[@agentxjs/common](../common)** - Logger facade
532
+ - **[@agentxjs/runtime](../runtime)** - Runtime implementation
533
+ - **[agentxjs](../agentx)** - High-level API
534
+
535
+ ---
536
+
537
+ ## License
538
+
539
+ MIT