@agentxjs/core 1.9.10-dev → 2.0.1

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 (142) hide show
  1. package/README.md +342 -0
  2. package/dist/{Processor-DT0N1qI6.d.ts → Processor-CeMyXtsX.d.ts} +1 -1
  3. package/dist/RpcClient-CMdhJxjS.d.ts +304 -0
  4. package/dist/agent/engine/internal/index.d.ts +22 -18
  5. package/dist/agent/engine/internal/index.js +1 -2
  6. package/dist/agent/engine/mealy/index.d.ts +4 -4
  7. package/dist/agent/engine/mealy/index.js +1 -2
  8. package/dist/agent/index.d.ts +92 -92
  9. package/dist/agent/index.js +16 -16
  10. package/dist/agent/types/index.d.ts +4 -4
  11. package/dist/agent/types/index.js +1 -2
  12. package/dist/bash/index.d.ts +29 -0
  13. package/dist/bash/index.js +7 -0
  14. package/dist/{bus-uF1DM2ox.d.ts → bus-C9FLWIu8.d.ts} +3 -1
  15. package/dist/{chunk-E5FPOAPO.js → chunk-22NTRYNO.js} +60 -60
  16. package/dist/chunk-22NTRYNO.js.map +1 -0
  17. package/dist/{chunk-7ZDX3O6I.js → chunk-AAFPAF67.js} +2 -2
  18. package/dist/{chunk-7ZDX3O6I.js.map → chunk-AAFPAF67.js.map} +1 -1
  19. package/dist/{chunk-EKHT54KN.js → chunk-APCBNCOW.js} +1 -1
  20. package/dist/{chunk-EKHT54KN.js.map → chunk-APCBNCOW.js.map} +1 -1
  21. package/dist/chunk-BHOD5PKR.js +55 -0
  22. package/dist/chunk-BHOD5PKR.js.map +1 -0
  23. package/dist/chunk-FI7WQFGV.js +37 -0
  24. package/dist/chunk-FI7WQFGV.js.map +1 -0
  25. package/dist/{chunk-AT5P47YA.js → chunk-RWIYC65R.js} +115 -115
  26. package/dist/chunk-RWIYC65R.js.map +1 -0
  27. package/dist/chunk-SKS7S2RY.js +1 -0
  28. package/dist/{chunk-I7GYR3MN.js → chunk-TUFZ2YH6.js} +77 -91
  29. package/dist/chunk-TUFZ2YH6.js.map +1 -0
  30. package/dist/{chunk-K6WXQ2RW.js → chunk-YSZG6XIM.js} +1 -2
  31. package/dist/chunk-YSZG6XIM.js.map +1 -0
  32. package/dist/{combinators-nEa5dD0T.d.ts → combinators-Dy-7lxKV.d.ts} +50 -50
  33. package/dist/common/logger/index.js +14 -16
  34. package/dist/common/logger/index.js.map +1 -1
  35. package/dist/container/index.d.ts +3 -4
  36. package/dist/container/index.js +0 -2
  37. package/dist/container/index.js.map +1 -1
  38. package/dist/driver/index.d.ts +2 -310
  39. package/dist/event/index.d.ts +4 -4
  40. package/dist/event/index.js +2 -3
  41. package/dist/event/types/index.d.ts +202 -208
  42. package/dist/event/types/index.js +1 -2
  43. package/dist/{event-CDuTzs__.d.ts → event-DNsF9EkO.d.ts} +5 -8
  44. package/dist/image/index.d.ts +9 -5
  45. package/dist/image/index.js +5 -2
  46. package/dist/image/index.js.map +1 -1
  47. package/dist/index--gxNpY5W.d.ts +609 -0
  48. package/dist/index.d.ts +4 -4
  49. package/dist/index.js +17 -17
  50. package/dist/{message-BMrMm1pq.d.ts → message-Dn-I2vr0.d.ts} +10 -33
  51. package/dist/mq/index.d.ts +25 -25
  52. package/dist/mq/index.js +1 -3
  53. package/dist/mq/index.js.map +1 -1
  54. package/dist/network/index.d.ts +59 -347
  55. package/dist/network/index.js +30 -41
  56. package/dist/network/index.js.map +1 -1
  57. package/dist/persistence/index.d.ts +2 -155
  58. package/dist/platform/index.d.ts +76 -0
  59. package/dist/platform/index.js.map +1 -0
  60. package/dist/runtime/index.d.ts +26 -59
  61. package/dist/runtime/index.js +117 -33
  62. package/dist/runtime/index.js.map +1 -1
  63. package/dist/session/index.d.ts +4 -52
  64. package/dist/session/index.js +4 -51
  65. package/dist/session/index.js.map +1 -1
  66. package/dist/types-Cb8tKM6Y.d.ts +90 -0
  67. package/package.json +10 -5
  68. package/src/agent/AgentStateMachine.ts +2 -2
  69. package/src/agent/__tests__/AgentStateMachine.test.ts +2 -2
  70. package/src/agent/__tests__/createAgent.test.ts +4 -4
  71. package/src/agent/__tests__/engine/internal/messageAssemblerProcessor.test.ts +301 -97
  72. package/src/agent/__tests__/engine/internal/stateEventProcessor.test.ts +6 -6
  73. package/src/agent/__tests__/engine/internal/turnTrackerProcessor.test.ts +59 -78
  74. package/src/agent/__tests__/engine/mealy/Mealy.test.ts +3 -3
  75. package/src/agent/__tests__/engine/mealy/Store.test.ts +1 -1
  76. package/src/agent/__tests__/engine/mealy/combinators.test.ts +4 -4
  77. package/src/agent/createAgent.ts +15 -15
  78. package/src/agent/engine/AgentProcessor.ts +7 -7
  79. package/src/agent/engine/MealyMachine.ts +4 -4
  80. package/src/agent/engine/internal/index.ts +11 -11
  81. package/src/agent/engine/internal/messageAssemblerProcessor.ts +113 -128
  82. package/src/agent/engine/internal/stateEventProcessor.ts +13 -15
  83. package/src/agent/engine/internal/turnTrackerProcessor.ts +27 -31
  84. package/src/agent/engine/mealy/Mealy.ts +2 -2
  85. package/src/agent/engine/mealy/combinators.ts +10 -10
  86. package/src/agent/engine/mealy/index.ts +9 -11
  87. package/src/agent/index.ts +30 -32
  88. package/src/agent/types/engine.ts +3 -3
  89. package/src/agent/types/event.ts +4 -12
  90. package/src/agent/types/index.ts +86 -88
  91. package/src/agent/types/message.ts +9 -43
  92. package/src/bash/index.ts +21 -0
  93. package/src/bash/tool.ts +57 -0
  94. package/src/bash/types.ts +108 -0
  95. package/src/common/logger/ConsoleLogger.ts +1 -1
  96. package/src/common/logger/LoggerFactoryImpl.ts +14 -14
  97. package/src/common/logger/index.ts +3 -3
  98. package/src/container/index.ts +4 -5
  99. package/src/container/types.ts +1 -1
  100. package/src/driver/index.ts +15 -16
  101. package/src/driver/types.ts +201 -73
  102. package/src/event/EventBus.ts +10 -10
  103. package/src/event/__tests__/EventBus.test.ts +2 -2
  104. package/src/event/index.ts +2 -3
  105. package/src/event/types/agent.ts +186 -191
  106. package/src/event/types/bus.ts +1 -1
  107. package/src/event/types/command.ts +293 -264
  108. package/src/event/types/container.ts +207 -222
  109. package/src/event/types/driver.ts +153 -155
  110. package/src/event/types/index.ts +6 -12
  111. package/src/event/types/session.ts +117 -130
  112. package/src/image/Image.ts +12 -2
  113. package/src/image/index.ts +4 -5
  114. package/src/image/types.ts +8 -2
  115. package/src/mq/OffsetGenerator.ts +1 -1
  116. package/src/mq/__tests__/OffsetGenerator.test.ts +1 -1
  117. package/src/mq/index.ts +4 -5
  118. package/src/network/RpcClient.ts +26 -25
  119. package/src/network/index.ts +41 -44
  120. package/src/network/jsonrpc.ts +5 -5
  121. package/src/persistence/index.ts +5 -5
  122. package/src/persistence/types.ts +5 -2
  123. package/src/platform/index.ts +21 -0
  124. package/src/platform/types.ts +84 -0
  125. package/src/runtime/AgentXRuntime.ts +188 -61
  126. package/src/runtime/__tests__/AgentXRuntime.test.ts +343 -0
  127. package/src/runtime/index.ts +12 -25
  128. package/src/runtime/types.ts +10 -62
  129. package/src/session/index.ts +2 -3
  130. package/dist/chunk-7D4SUZUM.js +0 -38
  131. package/dist/chunk-AT5P47YA.js.map +0 -1
  132. package/dist/chunk-E5FPOAPO.js.map +0 -1
  133. package/dist/chunk-I7GYR3MN.js.map +0 -1
  134. package/dist/chunk-K6WXQ2RW.js.map +0 -1
  135. package/dist/workspace/index.d.ts +0 -111
  136. package/dist/wrapper-Y3UTVU2E.js +0 -3635
  137. package/dist/wrapper-Y3UTVU2E.js.map +0 -1
  138. package/src/workspace/index.ts +0 -27
  139. package/src/workspace/types.ts +0 -131
  140. /package/dist/{workspace → bash}/index.js.map +0 -0
  141. /package/dist/{chunk-7D4SUZUM.js.map → chunk-SKS7S2RY.js.map} +0 -0
  142. /package/dist/{workspace → platform}/index.js +0 -0
@@ -14,51 +14,54 @@
14
14
  * - message_stop
15
15
  *
16
16
  * Output Events (Message Layer):
17
- * - tool_call_message (Message - AI's request to call a tool)
17
+ * - assistant_message (Message - includes text and tool calls in content)
18
18
  * - tool_result_message (Message - tool execution result)
19
- * - assistant_message (Message - complete assistant response)
20
19
  */
21
20
 
22
- import type { Processor, ProcessorDefinition } from "../mealy";
23
21
  import type {
24
- // Input: StreamEvent (from agent layer)
25
- StreamEvent,
26
- MessageStartEvent,
27
- TextDeltaEvent,
28
- ToolUseStartEvent,
29
- InputJsonDeltaEvent,
30
- ToolResultEvent,
31
- MessageStopEvent,
32
- // Output: Message events
33
- AssistantMessageEvent,
34
- ToolCallMessageEvent,
35
- ToolResultMessageEvent,
36
- ErrorMessageEvent,
37
22
  // Message types
38
23
  AssistantMessage,
39
- ToolCallMessage,
40
- ToolResultMessage,
24
+ // Output: Message events
25
+ AssistantMessageEvent,
41
26
  ErrorMessage,
27
+ ErrorMessageEvent,
28
+ InputJsonDeltaEvent,
29
+ MessageStartEvent,
30
+ MessageStopEvent,
31
+ // Input: StreamEvent (from agent layer)
32
+ StreamEvent,
33
+ TextDeltaEvent,
42
34
  // Content parts
43
35
  TextPart,
44
36
  ToolCallPart,
37
+ ToolResultEvent,
38
+ ToolResultMessage,
39
+ ToolResultMessageEvent,
45
40
  ToolResultPart,
41
+ ToolUseStartEvent,
46
42
  } from "../../types";
43
+ import type { Processor, ProcessorDefinition } from "../mealy";
47
44
 
48
45
  // ===== State Types =====
49
46
 
50
47
  /**
51
48
  * Pending content accumulator
49
+ *
50
+ * Tracks content blocks in the order they appear in the stream.
51
+ * Text and tool_use blocks may be interleaved.
52
52
  */
53
53
  export interface PendingContent {
54
54
  type: "text" | "tool_use";
55
- index: number;
56
55
  // For text content
57
56
  textDeltas?: string[];
58
57
  // For tool use
59
58
  toolId?: string;
60
59
  toolName?: string;
61
60
  toolInputJson?: string;
61
+ /** True when tool_use_stop has been processed and input is fully parsed */
62
+ assembled?: boolean;
63
+ /** Parsed tool input (set at tool_use_stop time) */
64
+ parsedInput?: Record<string, unknown>;
62
65
  }
63
66
 
64
67
  /**
@@ -86,10 +89,10 @@ export interface MessageAssemblerState {
86
89
  messageStartTime: number | null;
87
90
 
88
91
  /**
89
- * Pending content blocks being accumulated
90
- * Key is the content block index
92
+ * Pending content blocks in stream order.
93
+ * Preserves the interleaved order of text and tool_use blocks.
91
94
  */
92
- pendingContents: Record<number, PendingContent>;
95
+ pendingContents: PendingContent[];
93
96
 
94
97
  /**
95
98
  * Pending tool calls waiting for results
@@ -105,7 +108,7 @@ export function createInitialMessageAssemblerState(): MessageAssemblerState {
105
108
  return {
106
109
  currentMessageId: null,
107
110
  messageStartTime: null,
108
- pendingContents: {},
111
+ pendingContents: [],
109
112
  pendingToolCalls: {},
110
113
  };
111
114
  }
@@ -119,12 +122,21 @@ function generateId(): string {
119
122
  return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
120
123
  }
121
124
 
125
+ /**
126
+ * Find last index matching a predicate (Array.findLastIndex polyfill)
127
+ */
128
+ function findLastIndex<T>(arr: readonly T[], predicate: (item: T) => boolean): number {
129
+ for (let i = arr.length - 1; i >= 0; i--) {
130
+ if (predicate(arr[i])) return i;
131
+ }
132
+ return -1;
133
+ }
134
+
122
135
  /**
123
136
  * Output event types from MessageAssembler
124
137
  */
125
138
  export type MessageAssemblerOutput =
126
139
  | AssistantMessageEvent
127
- | ToolCallMessageEvent
128
140
  | ToolResultMessageEvent
129
141
  | ErrorMessageEvent;
130
142
 
@@ -188,7 +200,7 @@ function handleMessageStart(
188
200
  ...state,
189
201
  currentMessageId: data.messageId,
190
202
  messageStartTime: event.timestamp,
191
- pendingContents: {},
203
+ pendingContents: [],
192
204
  },
193
205
  [],
194
206
  ];
@@ -196,34 +208,32 @@ function handleMessageStart(
196
208
 
197
209
  /**
198
210
  * Handle text_delta event
211
+ *
212
+ * Appends to the last text block if one exists, otherwise creates a new one.
213
+ * This preserves the interleaved order: text after a tool_use gets its own block.
199
214
  */
200
215
  function handleTextDelta(
201
216
  state: Readonly<MessageAssemblerState>,
202
217
  event: StreamEvent
203
218
  ): [MessageAssemblerState, MessageAssemblerOutput[]] {
204
219
  const { data } = event as TextDeltaEvent;
205
- const index = 0; // Text content uses index 0
206
- const existingContent = state.pendingContents[index];
207
-
208
- const pendingContent: PendingContent =
209
- existingContent?.type === "text"
210
- ? {
211
- ...existingContent,
212
- textDeltas: [...(existingContent.textDeltas || []), data.text],
213
- }
214
- : {
215
- type: "text",
216
- index,
217
- textDeltas: [data.text],
218
- };
220
+ const lastContent = state.pendingContents[state.pendingContents.length - 1];
221
+
222
+ // Append to last text block if it exists
223
+ if (lastContent?.type === "text") {
224
+ const updated = [...state.pendingContents];
225
+ updated[updated.length - 1] = {
226
+ ...lastContent,
227
+ textDeltas: [...(lastContent.textDeltas || []), data.text],
228
+ };
229
+ return [{ ...state, pendingContents: updated }, []];
230
+ }
219
231
 
232
+ // Create a new text block (preserves position after any preceding tool_use)
220
233
  return [
221
234
  {
222
235
  ...state,
223
- pendingContents: {
224
- ...state.pendingContents,
225
- [index]: pendingContent,
226
- },
236
+ pendingContents: [...state.pendingContents, { type: "text", textDeltas: [data.text] }],
227
237
  },
228
238
  [],
229
239
  ];
@@ -237,23 +247,19 @@ function handleToolUseStart(
237
247
  event: StreamEvent
238
248
  ): [MessageAssemblerState, MessageAssemblerOutput[]] {
239
249
  const { data } = event as ToolUseStartEvent;
240
- const index = 1; // Tool use uses index 1
241
-
242
- const pendingContent: PendingContent = {
243
- type: "tool_use",
244
- index,
245
- toolId: data.toolCallId,
246
- toolName: data.toolName,
247
- toolInputJson: "",
248
- };
249
250
 
250
251
  return [
251
252
  {
252
253
  ...state,
253
- pendingContents: {
254
+ pendingContents: [
254
255
  ...state.pendingContents,
255
- [index]: pendingContent,
256
- },
256
+ {
257
+ type: "tool_use",
258
+ toolId: data.toolCallId,
259
+ toolName: data.toolName,
260
+ toolInputJson: "",
261
+ },
262
+ ],
257
263
  },
258
264
  [],
259
265
  ];
@@ -267,102 +273,76 @@ function handleInputJsonDelta(
267
273
  event: StreamEvent
268
274
  ): [MessageAssemblerState, MessageAssemblerOutput[]] {
269
275
  const { data } = event as InputJsonDeltaEvent;
270
- const index = 1; // Tool use uses index 1
271
- const existingContent = state.pendingContents[index];
272
276
 
273
- if (!existingContent || existingContent.type !== "tool_use") {
274
- // No pending tool_use content, ignore
277
+ // Find the last tool_use content in the array
278
+ const lastToolIndex = findLastIndex(
279
+ state.pendingContents,
280
+ (c) => c.type === "tool_use" && !c.assembled
281
+ );
282
+ if (lastToolIndex === -1) {
275
283
  return [state, []];
276
284
  }
277
285
 
278
- const pendingContent: PendingContent = {
286
+ const existingContent = state.pendingContents[lastToolIndex];
287
+ const updated = [...state.pendingContents];
288
+ updated[lastToolIndex] = {
279
289
  ...existingContent,
280
290
  toolInputJson: (existingContent.toolInputJson || "") + data.partialJson,
281
291
  };
282
292
 
283
- return [
284
- {
285
- ...state,
286
- pendingContents: {
287
- ...state.pendingContents,
288
- [index]: pendingContent,
289
- },
290
- },
291
- [],
292
- ];
293
+ return [{ ...state, pendingContents: updated }, []];
293
294
  }
294
295
 
295
296
  /**
296
297
  * Handle tool_use_stop event
297
298
  *
298
- * Emits:
299
- * - tool_call_message (Message Event) - for UI display and tool execution
299
+ * Marks the tool_use entry as assembled with parsed input.
300
+ * The entry stays in pendingContents to preserve its position.
301
+ * No event is emitted — tool calls are part of the assistant message.
300
302
  */
301
303
  function handleToolUseStop(
302
304
  state: Readonly<MessageAssemblerState>,
303
305
  _event: StreamEvent
304
306
  ): [MessageAssemblerState, MessageAssemblerOutput[]] {
305
- const index = 1;
306
- const pendingContent = state.pendingContents[index];
307
-
308
- if (!pendingContent || pendingContent.type !== "tool_use") {
307
+ // Find the last unassembled tool_use content
308
+ const lastToolIndex = findLastIndex(
309
+ state.pendingContents,
310
+ (c) => c.type === "tool_use" && !c.assembled
311
+ );
312
+ if (lastToolIndex === -1) {
309
313
  return [state, []];
310
314
  }
311
315
 
312
- // Get tool info from pendingContent (saved during tool_use_start)
316
+ const pendingContent = state.pendingContents[lastToolIndex];
313
317
  const toolId = pendingContent.toolId || "";
314
318
  const toolName = pendingContent.toolName || "";
315
319
 
316
- // Parse tool input JSON (accumulated during input_json_delta)
320
+ // Parse tool input JSON
317
321
  let toolInput: Record<string, unknown> = {};
318
322
  try {
319
323
  toolInput = pendingContent.toolInputJson ? JSON.parse(pendingContent.toolInputJson) : {};
320
324
  } catch {
321
- // Failed to parse, use empty object
322
325
  toolInput = {};
323
326
  }
324
327
 
325
- // Create ToolCallPart
326
- const toolCall: ToolCallPart = {
327
- type: "tool-call",
328
- id: toolId,
329
- name: toolName,
330
- input: toolInput,
328
+ // Mark as assembled in-place (preserves position)
329
+ const updated = [...state.pendingContents];
330
+ updated[lastToolIndex] = {
331
+ ...pendingContent,
332
+ assembled: true,
333
+ parsedInput: toolInput,
331
334
  };
332
335
 
333
- // Create ToolCallMessage (complete Message object)
334
- // parentId links this tool call to its parent assistant message
335
- const messageId = generateId();
336
- const timestamp = Date.now();
337
- const toolCallMessage: ToolCallMessage = {
338
- id: messageId,
339
- role: "assistant",
340
- subtype: "tool-call",
341
- toolCall,
342
- timestamp,
343
- parentId: state.currentMessageId || undefined,
344
- };
345
-
346
- // Emit tool_call_message event - data is complete Message object
347
- const toolCallMessageEvent: ToolCallMessageEvent = {
348
- type: "tool_call_message",
349
- timestamp,
350
- data: toolCallMessage,
351
- };
352
-
353
- // Remove from pending contents, add to pending tool calls
354
- const { [index]: _, ...remainingContents } = state.pendingContents;
355
-
356
336
  return [
357
337
  {
358
338
  ...state,
359
- pendingContents: remainingContents,
339
+ pendingContents: updated,
360
340
  pendingToolCalls: {
361
341
  ...state.pendingToolCalls,
362
342
  [toolId]: { id: toolId, name: toolName },
363
343
  },
364
344
  },
365
- [toolCallMessageEvent],
345
+ [], // No event emitted
366
346
  ];
367
347
  }
368
348
 
@@ -427,6 +407,9 @@ function handleToolResult(
427
407
 
428
408
  /**
429
409
  * Handle message_stop event
410
+ *
411
+ * Assembles the complete AssistantMessage from pendingContents in stream order.
412
+ * Text and tool call parts are interleaved as they appeared in the stream.
430
413
  */
431
414
  function handleMessageStop(
432
415
  state: Readonly<MessageAssemblerState>,
@@ -438,21 +421,31 @@ function handleMessageStop(
438
421
  return [state, []];
439
422
  }
440
423
 
441
- // Assemble all text content
442
- const textParts: string[] = [];
443
- const sortedContents = Object.values(state.pendingContents).sort((a, b) => a.index - b.index);
424
+ // Build content parts in stream order from pendingContents
425
+ const contentParts: Array<TextPart | ToolCallPart> = [];
444
426
 
445
- for (const pending of sortedContents) {
427
+ for (const pending of state.pendingContents) {
446
428
  if (pending.type === "text" && pending.textDeltas) {
447
- textParts.push(pending.textDeltas.join(""));
429
+ const text = pending.textDeltas.join("");
430
+ if (text.trim().length > 0) {
431
+ contentParts.push({ type: "text", text });
432
+ }
433
+ } else if (pending.type === "tool_use" && pending.assembled) {
434
+ contentParts.push({
435
+ type: "tool-call",
436
+ id: pending.toolId || "",
437
+ name: pending.toolName || "",
438
+ input: pending.parsedInput || {},
439
+ });
448
440
  }
449
441
  }
450
442
 
451
- const textContent = textParts.join("");
443
+ const hasToolCalls = contentParts.some((p) => p.type === "tool-call");
444
+ const hasText = contentParts.some((p) => p.type === "text");
452
445
 
453
- // Skip empty messages (but preserve pendingToolCalls if stopReason is "tool_use")
446
+ // Skip truly empty messages (no text AND no tool calls)
454
447
  const stopReason = data.stopReason;
455
- if (!textContent || textContent.trim().length === 0) {
448
+ if (!hasText && !hasToolCalls) {
456
449
  const shouldPreserveToolCalls = stopReason === "tool_use";
457
450
  return [
458
451
  {
@@ -463,15 +456,7 @@ function handleMessageStop(
463
456
  ];
464
457
  }
465
458
 
466
- // Create content parts (new structure uses ContentPart[])
467
- const contentParts: TextPart[] = [
468
- {
469
- type: "text",
470
- text: textContent,
471
- },
472
- ];
473
-
474
- // Create AssistantMessage (complete Message object)
459
+ // Create AssistantMessage with interleaved content
475
460
  const timestamp = state.messageStartTime || Date.now();
476
461
  const assistantMessage: AssistantMessage = {
477
462
  id: state.currentMessageId,
@@ -481,7 +466,7 @@ function handleMessageStop(
481
466
  timestamp,
482
467
  };
483
468
 
484
- // Emit AssistantMessageEvent - data is complete Message object
469
+ // Emit AssistantMessageEvent
485
470
  const assistantEvent: AssistantMessageEvent = {
486
471
  type: "assistant_message",
487
472
  timestamp,
@@ -18,25 +18,25 @@
18
18
  * - tool_executing
19
19
  */
20
20
 
21
- import type { Processor, ProcessorDefinition } from "../mealy";
21
+ import { createLogger } from "commonxjs/logger";
22
22
  import type {
23
+ ConversationEndEvent,
24
+ ConversationInterruptedEvent,
25
+ ConversationRespondingEvent,
26
+ // Output: State events
27
+ ConversationStartEvent,
23
28
  // Base type
24
29
  EngineEvent,
25
- // Input: StreamEvent (from agent layer)
26
- StreamEvent,
30
+ ErrorOccurredEvent,
27
31
  MessageStartEvent,
28
32
  MessageStopEvent,
29
- ToolUseStartEvent,
30
- // Output: State events
31
- ConversationStartEvent,
32
- ConversationRespondingEvent,
33
- ConversationEndEvent,
34
- ConversationInterruptedEvent,
35
- ToolPlannedEvent,
33
+ // Input: StreamEvent (from agent layer)
34
+ StreamEvent,
36
35
  ToolExecutingEvent,
37
- ErrorOccurredEvent,
36
+ ToolPlannedEvent,
37
+ ToolUseStartEvent,
38
38
  } from "../../types";
39
- import { createLogger } from "commonxjs/logger";
39
+ import type { Processor, ProcessorDefinition } from "../mealy";
40
40
 
41
41
  const logger = createLogger("engine/stateEventProcessor");
42
42
 
@@ -50,9 +50,7 @@ const logger = createLogger("engine/stateEventProcessor");
50
50
  *
51
51
  * Currently empty - no context needed as all information comes from events.
52
52
  */
53
- export interface StateEventProcessorContext {
54
- // Empty - all information comes from events
55
- }
53
+ export type StateEventProcessorContext = {};
56
54
 
57
55
  /**
58
56
  * Initial context factory for StateEventProcessor
@@ -2,30 +2,28 @@
2
2
  * turnTrackerProcessor
3
3
  *
4
4
  * Pure Mealy transition function that tracks request-response turn pairs.
5
+ * Derives turn events entirely from stream-layer events (no external injection).
5
6
  *
6
- * Input Events:
7
- * - user_message (Message Layer)
8
- * - message_stop (Stream Layer - contains stop reason)
9
- * - assistant_message (Message Layer)
7
+ * Input Events (Stream Layer):
8
+ * - message_start → emit turn_request (a new turn begins)
9
+ * - message_stop emit turn_response (turn completes, based on stop reason)
10
10
  *
11
11
  * Output Events (Turn Layer):
12
12
  * - turn_request
13
13
  * - turn_response
14
14
  */
15
15
 
16
- import type { Processor, ProcessorDefinition } from "../mealy";
17
16
  import type {
18
- // Input: combined stream and message events
19
- StreamEvent,
20
- AgentMessageEvent,
17
+ MessageStartEvent,
21
18
  MessageStopEvent,
22
- UserMessageEvent,
19
+ StreamEvent,
20
+ // Data types
21
+ TokenUsage,
23
22
  // Output: Turn events
24
23
  TurnRequestEvent,
25
24
  TurnResponseEvent,
26
- // Data types
27
- TokenUsage,
28
25
  } from "../../types";
26
+ import type { Processor, ProcessorDefinition } from "../mealy";
29
27
 
30
28
  // ===== State Types =====
31
29
 
@@ -88,9 +86,9 @@ export type TurnTrackerOutput = TurnRequestEvent | TurnResponseEvent;
88
86
 
89
87
  /**
90
88
  * Input event types for TurnTracker
91
- * Accepts both Stream and Message layer events
89
+ * Only stream-layer events turn events are derived, not injected.
92
90
  */
93
- export type TurnTrackerInput = StreamEvent | AgentMessageEvent;
91
+ export type TurnTrackerInput = StreamEvent;
94
92
 
95
93
  /**
96
94
  * turnTrackerProcessor
@@ -104,39 +102,37 @@ export const turnTrackerProcessor: Processor<
104
102
  TurnTrackerOutput
105
103
  > = (state, input): [TurnTrackerState, TurnTrackerOutput[]] => {
106
104
  switch (input.type) {
107
- case "user_message":
108
- return handleUserMessage(state, input as AgentMessageEvent);
105
+ case "message_start":
106
+ return handleMessageStart(state, input as MessageStartEvent);
109
107
 
110
108
  case "message_stop":
111
109
  return handleMessageStop(state, input as StreamEvent);
112
110
 
113
- case "assistant_message":
114
- // Turn completion is handled in message_stop
115
- // This handler is kept for potential future use
116
- return [state, []];
117
-
118
111
  default:
119
112
  return [state, []];
120
113
  }
121
114
  };
122
115
 
123
116
  /**
124
- * Handle user_message event
117
+ * Handle message_start event — a new turn begins
125
118
  */
126
- function handleUserMessage(
119
+ function handleMessageStart(
127
120
  state: Readonly<TurnTrackerState>,
128
- event: AgentMessageEvent
121
+ event: MessageStartEvent
129
122
  ): [TurnTrackerState, TurnTrackerOutput[]] {
130
- const { data } = event as UserMessageEvent;
131
- const turnId = generateId();
123
+ // If there's already a pending turn (e.g. tool_use didn't end the turn),
124
+ // don't start a new one
125
+ if (state.pendingTurn) {
126
+ return [state, []];
127
+ }
132
128
 
133
- // Extract content as string (UserMessage.content can be string or array)
134
- const contentText = typeof data.content === "string" ? data.content : "";
129
+ const turnId = generateId();
130
+ const messageId = event.data.messageId ?? "";
135
131
 
136
132
  const pendingTurn: PendingTurn = {
137
133
  turnId,
138
- messageId: data.id,
139
- content: contentText,
134
+ messageId,
135
+ content: "",
140
136
  requestedAt: event.timestamp,
141
137
  };
142
138
 
@@ -145,8 +141,8 @@ function handleUserMessage(
145
141
  timestamp: Date.now(),
146
142
  data: {
147
143
  turnId,
148
- messageId: data.id,
149
- content: contentText,
144
+ messageId,
145
+ content: "",
150
146
  timestamp: event.timestamp,
151
147
  },
152
148
  };
@@ -31,10 +31,10 @@
31
31
  * ```
32
32
  */
33
33
 
34
+ import { createLogger } from "commonxjs/logger";
34
35
  import type { Processor } from "./Processor";
35
- import type { Store } from "./Store";
36
36
  import type { Sink, SinkDefinition } from "./Sink";
37
- import { createLogger } from "commonxjs/logger";
37
+ import type { Store } from "./Store";
38
38
 
39
39
  const logger = createLogger("engine/Mealy");
40
40
 
@@ -28,13 +28,11 @@ import type { Processor } from "./Processor";
28
28
  * });
29
29
  * ```
30
30
  */
31
- export function combineProcessors<
32
- TState extends Record<string, unknown>,
33
- TInput,
34
- TOutput,
35
- >(processors: {
36
- [K in keyof TState]: Processor<TState[K], TInput, TOutput>;
37
- }): Processor<TState, TInput, TOutput> {
31
+ export function combineProcessors<TState extends Record<string, unknown>, TInput, TOutput>(
32
+ processors: {
33
+ [K in keyof TState]: Processor<TState[K], TInput, TOutput>;
34
+ }
35
+ ): Processor<TState, TInput, TOutput> {
38
36
  return (state: Readonly<TState>, event: TInput): [TState, TOutput[]] => {
39
37
  const newState = {} as TState;
40
38
  const allOutputs: TOutput[] = [];
@@ -55,9 +53,11 @@ export function combineProcessors<
55
53
  /**
56
54
  * combineInitialStates - Helper to create initial state for combined processors
57
55
  */
58
- export function combineInitialStates<TState extends Record<string, unknown>>(initialStates: {
59
- [K in keyof TState]: () => TState[K];
60
- }): () => TState {
56
+ export function combineInitialStates<TState extends Record<string, unknown>>(
57
+ initialStates: {
58
+ [K in keyof TState]: () => TState[K];
59
+ }
60
+ ): () => TState {
61
61
  return () => {
62
62
  const state = {} as TState;
63
63
  for (const key in initialStates) {
@@ -16,30 +16,28 @@
16
16
 
17
17
  // ===== Core Components =====
18
18
 
19
- // Source - Input (input adapter)
20
- export { type Source, type SourceDefinition } from "./Source";
21
-
22
19
  // Processor - Processing (pure Mealy transition function)
23
- export { type Processor, type ProcessorResult, type ProcessorDefinition } from "./Processor";
24
-
20
+ export type { Processor, ProcessorDefinition, ProcessorResult } from "./Processor";
25
21
  // Sink - Output (output adapter)
26
- export { type Sink, type SinkDefinition } from "./Sink";
22
+ export type { Sink, SinkDefinition } from "./Sink";
23
+ // Source - Input (input adapter)
24
+ export type { Source, SourceDefinition } from "./Source";
27
25
 
28
26
  // Store - State storage
29
- export { type Store, MemoryStore } from "./Store";
27
+ export { MemoryStore, type Store } from "./Store";
30
28
 
31
29
  // ===== Mealy Runtime =====
32
30
 
33
- export { Mealy, createMealy, type MealyConfig, type ProcessResult } from "./Mealy";
31
+ export { createMealy, Mealy, type MealyConfig, type ProcessResult } from "./Mealy";
34
32
 
35
33
  // ===== Combinators =====
36
34
 
37
35
  export {
38
- combineProcessors,
39
- combineInitialStates,
40
36
  chainProcessors,
37
+ combineInitialStates,
38
+ combineProcessors,
41
39
  filterProcessor,
40
+ identityProcessor,
42
41
  mapOutput,
43
42
  withLogging,
44
- identityProcessor,
45
43
  } from "./combinators";