@falai/agent 1.2.0 → 1.2.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 (72) hide show
  1. package/dist/cjs/core/ResponseEngine.d.ts +0 -2
  2. package/dist/cjs/core/ResponseEngine.d.ts.map +1 -1
  3. package/dist/cjs/core/ResponseEngine.js +2 -6
  4. package/dist/cjs/core/ResponseEngine.js.map +1 -1
  5. package/dist/cjs/core/ResponseModal.d.ts.map +1 -1
  6. package/dist/cjs/core/ResponseModal.js +45 -54
  7. package/dist/cjs/core/ResponseModal.js.map +1 -1
  8. package/dist/cjs/core/ResponsePipeline.d.ts +2 -1
  9. package/dist/cjs/core/ResponsePipeline.d.ts.map +1 -1
  10. package/dist/cjs/core/ResponsePipeline.js +17 -19
  11. package/dist/cjs/core/ResponsePipeline.js.map +1 -1
  12. package/dist/cjs/core/RoutingEngine.js +2 -2
  13. package/dist/cjs/core/RoutingEngine.js.map +1 -1
  14. package/dist/cjs/providers/AnthropicProvider.d.ts +7 -0
  15. package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -1
  16. package/dist/cjs/providers/AnthropicProvider.js +101 -12
  17. package/dist/cjs/providers/AnthropicProvider.js.map +1 -1
  18. package/dist/cjs/providers/GeminiProvider.d.ts +7 -0
  19. package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
  20. package/dist/cjs/providers/GeminiProvider.js +81 -2
  21. package/dist/cjs/providers/GeminiProvider.js.map +1 -1
  22. package/dist/cjs/providers/OpenAIProvider.d.ts +5 -0
  23. package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
  24. package/dist/cjs/providers/OpenAIProvider.js +51 -12
  25. package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
  26. package/dist/cjs/providers/OpenRouterProvider.d.ts +5 -0
  27. package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
  28. package/dist/cjs/providers/OpenRouterProvider.js +50 -12
  29. package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
  30. package/dist/cjs/types/ai.d.ts +2 -2
  31. package/dist/cjs/types/ai.d.ts.map +1 -1
  32. package/dist/core/ResponseEngine.d.ts +0 -2
  33. package/dist/core/ResponseEngine.d.ts.map +1 -1
  34. package/dist/core/ResponseEngine.js +2 -6
  35. package/dist/core/ResponseEngine.js.map +1 -1
  36. package/dist/core/ResponseModal.d.ts.map +1 -1
  37. package/dist/core/ResponseModal.js +46 -55
  38. package/dist/core/ResponseModal.js.map +1 -1
  39. package/dist/core/ResponsePipeline.d.ts +2 -1
  40. package/dist/core/ResponsePipeline.d.ts.map +1 -1
  41. package/dist/core/ResponsePipeline.js +18 -20
  42. package/dist/core/ResponsePipeline.js.map +1 -1
  43. package/dist/core/RoutingEngine.js +3 -3
  44. package/dist/core/RoutingEngine.js.map +1 -1
  45. package/dist/providers/AnthropicProvider.d.ts +7 -0
  46. package/dist/providers/AnthropicProvider.d.ts.map +1 -1
  47. package/dist/providers/AnthropicProvider.js +101 -12
  48. package/dist/providers/AnthropicProvider.js.map +1 -1
  49. package/dist/providers/GeminiProvider.d.ts +7 -0
  50. package/dist/providers/GeminiProvider.d.ts.map +1 -1
  51. package/dist/providers/GeminiProvider.js +81 -2
  52. package/dist/providers/GeminiProvider.js.map +1 -1
  53. package/dist/providers/OpenAIProvider.d.ts +5 -0
  54. package/dist/providers/OpenAIProvider.d.ts.map +1 -1
  55. package/dist/providers/OpenAIProvider.js +51 -12
  56. package/dist/providers/OpenAIProvider.js.map +1 -1
  57. package/dist/providers/OpenRouterProvider.d.ts +5 -0
  58. package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
  59. package/dist/providers/OpenRouterProvider.js +50 -12
  60. package/dist/providers/OpenRouterProvider.js.map +1 -1
  61. package/dist/types/ai.d.ts +2 -2
  62. package/dist/types/ai.d.ts.map +1 -1
  63. package/package.json +1 -1
  64. package/src/core/ResponseEngine.ts +2 -9
  65. package/src/core/ResponseModal.ts +56 -67
  66. package/src/core/ResponsePipeline.ts +22 -22
  67. package/src/core/RoutingEngine.ts +3 -3
  68. package/src/providers/AnthropicProvider.ts +110 -12
  69. package/src/providers/GeminiProvider.ts +91 -2
  70. package/src/providers/OpenAIProvider.ts +56 -12
  71. package/src/providers/OpenRouterProvider.ts +55 -12
  72. package/src/types/ai.ts +2 -2
@@ -12,13 +12,11 @@ import type {
12
12
  HistoryItem,
13
13
  Tool,
14
14
  Event,
15
- ToolEventData,
16
15
  AgentStructuredResponse,
17
16
  Term,
18
17
  StoppedReason,
19
18
  ToolCallRequest,
20
19
  } from "../types";
21
- import { EventKind, MessageRole } from "../types";
22
20
  import type { Agent } from "./Agent";
23
21
  import type { Route } from "./Route";
24
22
  import { Step } from "./Step";
@@ -26,7 +24,7 @@ import { ResponseEngine } from "./ResponseEngine";
26
24
  import { ResponsePipeline } from "./ResponsePipeline";
27
25
  import { BatchExecutor, type HookFunction } from "./BatchExecutor";
28
26
  import { BatchPromptBuilder } from "./BatchPromptBuilder";
29
- import { cloneDeep, mergeCollected, enterStep, getLastMessageFromHistory, render, logger, historyToEvents } from "../utils";
27
+ import { cloneDeep, mergeCollected, enterStep, getLastMessageFromHistory, render, logger, historyToEvents, eventsToHistory } from "../utils";
30
28
  import { createTemplateContext } from "../utils/template";
31
29
  import type { ToolManager } from "./ToolManager";
32
30
  import { END_ROUTE_ID } from "../constants";
@@ -669,12 +667,15 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
669
667
  `Return ONLY the extracted data as JSON. If no data can be extracted, return an empty object {}.`
670
668
  );
671
669
 
670
+ // Convert Event[] to HistoryItem[] for provider call
671
+ const historyItems = eventsToHistory(history);
672
+
672
673
  // Call AI to extract data
673
674
  const agentOptions = this.agent.getAgentOptions();
674
675
  try {
675
676
  const result = await agentOptions.provider.generateMessage<TContext, Partial<TData>>({
676
677
  prompt: extractionPrompt.join('\n'),
677
- history,
678
+ history: historyItems,
678
679
  context: {} as TContext, // Passed as empty object so AI doesn't "extract" from context
679
680
  // NOTE: context is intentionally NOT passed here.
680
681
  // Passing context caused the AI to "extract" data from the lead's context
@@ -716,7 +717,6 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
716
717
  // Get last user message (needed for both route and completion handling)
717
718
  // Convert HistoryItem[] to Event[] for internal processing
718
719
  const historyEvents = historyToEvents(history);
719
- const lastMessageText = getLastMessageFromHistory(historyEvents);
720
720
 
721
721
  let message: string;
722
722
  let toolCalls: Array<{ toolName: string; arguments: Record<string, unknown> }> | undefined = undefined;
@@ -757,7 +757,6 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
757
757
  session,
758
758
  history,
759
759
  context: effectiveContext,
760
- lastMessageText,
761
760
  historyEvents,
762
761
  signal: undefined,
763
762
  });
@@ -785,7 +784,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
785
784
  selectedRoute,
786
785
  session,
787
786
  context: effectiveContext,
788
- lastMessageText,
787
+ history,
789
788
  historyEvents,
790
789
  signal: undefined,
791
790
  });
@@ -806,7 +805,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
806
805
  // Fallback: No routes defined, generate a simple response
807
806
 
808
807
  message = await this.generateFallbackResponse({
809
- history: historyEvents, // Use Event[] for fallback response
808
+ history,
810
809
  context: effectiveContext,
811
810
  session,
812
811
  });
@@ -926,7 +925,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
926
925
  const agentOptions = this.agent.getAgentOptions();
927
926
  const result = await agentOptions.provider.generateMessage({
928
927
  prompt: batchPromptResult.prompt,
929
- history: historyEvents,
928
+ history, // Use HistoryItem[] for AI provider
930
929
  context,
931
930
  tools: availableTools,
932
931
  signal,
@@ -1125,7 +1124,6 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1125
1124
  // Get last user message (needed for both route and completion handling)
1126
1125
  // Convert HistoryItem[] to Event[] for internal processing
1127
1126
  const historyEvents = historyToEvents(history);
1128
- const lastMessageText = getLastMessageFromHistory(historyEvents);
1129
1127
 
1130
1128
  if (selectedRoute && !isRouteComplete) {
1131
1129
  // Check if we have batch steps to execute
@@ -1153,7 +1151,6 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1153
1151
  session,
1154
1152
  history,
1155
1153
  context: effectiveContext,
1156
- lastMessageText,
1157
1154
  historyEvents,
1158
1155
  });
1159
1156
  }
@@ -1164,14 +1161,14 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1164
1161
  selectedRoute,
1165
1162
  session,
1166
1163
  context: effectiveContext,
1167
- lastMessageText,
1164
+ history,
1168
1165
  historyEvents,
1169
1166
  });
1170
1167
 
1171
1168
  } else {
1172
1169
  // Fallback: No routes defined, stream a simple response
1173
1170
  yield* this.streamFallbackResponse({
1174
- history: historyEvents, // Use Event[] for fallback response
1171
+ history,
1175
1172
  context: effectiveContext,
1176
1173
  session,
1177
1174
  });
@@ -1196,7 +1193,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1196
1193
  batchStoppedReason?: StoppedReason;
1197
1194
  signal?: AbortSignal;
1198
1195
  }): AsyncGenerator<AgentResponseStreamChunk<TData>> {
1199
- const { selectedRoute, batchSteps, context, historyEvents, batchStoppedReason, signal } = params;
1196
+ const { selectedRoute, batchSteps, history, context, historyEvents, batchStoppedReason, signal } = params;
1200
1197
  let session = params.session;
1201
1198
 
1202
1199
  // Create hook executor function
@@ -1251,7 +1248,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1251
1248
  const agentOptions = this.agent.getAgentOptions();
1252
1249
  const stream = agentOptions.provider.generateMessageStream({
1253
1250
  prompt: batchPromptResult.prompt,
1254
- history: historyEvents,
1251
+ history, // Use HistoryItem[] for AI provider
1255
1252
  context,
1256
1253
  tools: availableTools,
1257
1254
  signal,
@@ -1380,17 +1377,16 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1380
1377
  selectedStep?: Step<TContext, TData>;
1381
1378
  responseDirectives?: string[];
1382
1379
  session: SessionState<TData>;
1383
- history: HistoryItem[]; // Keep as HistoryItem[] for AI provider compatibility
1380
+ history: HistoryItem[];
1384
1381
  context: TContext;
1385
- lastMessageText: string; // String version for buildResponsePrompt
1386
- historyEvents: Event[]; // Event[] version for buildResponsePrompt
1382
+ historyEvents: Event[];
1387
1383
  signal?: AbortSignal;
1388
1384
  }): Promise<{
1389
1385
  message: string;
1390
1386
  toolCalls?: Array<{ toolName: string; arguments: Record<string, unknown> }>;
1391
1387
  session: SessionState<TData>;
1392
1388
  }> {
1393
- const { selectedRoute, selectedStep, responseDirectives, history, context, lastMessageText, historyEvents, signal } = params;
1389
+ const { selectedRoute, selectedStep, responseDirectives, history, context, historyEvents, signal } = params;
1394
1390
  let session = params.session;
1395
1391
 
1396
1392
  // Determine next step
@@ -1466,8 +1462,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1466
1462
  rules: selectedRoute.getRules(),
1467
1463
  prohibitions: selectedRoute.getProhibitions(),
1468
1464
  directives: responseDirectives,
1469
- history: historyEvents, // Use Event[] for buildResponsePrompt
1470
- lastMessage: lastMessageText, // Use string for buildResponsePrompt
1465
+ history: historyEvents,
1471
1466
  agentOptions: this.agent.getAgentOptions(),
1472
1467
  combinedGuidelines: [...this.agent.getGuidelines(), ...selectedRoute.getGuidelines()],
1473
1468
  combinedTerms: this.mergeTerms(this.agent.getTerms(), selectedRoute.getTerms()),
@@ -1483,7 +1478,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1483
1478
  const agentOptions = this.agent.getAgentOptions();
1484
1479
  const result = await agentOptions.provider.generateMessage({
1485
1480
  prompt: responsePrompt,
1486
- history: historyEvents, // Use Event[] for AI provider
1481
+ history, // Use HistoryItem[] for AI provider
1487
1482
  context,
1488
1483
  tools: availableTools,
1489
1484
  signal,
@@ -1538,11 +1533,10 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1538
1533
  session: SessionState<TData>;
1539
1534
  history: HistoryItem[];
1540
1535
  context: TContext;
1541
- lastMessageText: string; // String version for buildResponsePrompt
1542
- historyEvents: Event[]; // Event[] version for buildResponsePrompt
1536
+ historyEvents: Event[];
1543
1537
  signal?: AbortSignal;
1544
1538
  }): AsyncGenerator<AgentResponseStreamChunk<TData>> {
1545
- const { selectedRoute, selectedStep, responseDirectives, history, context, lastMessageText, historyEvents, signal } = params;
1539
+ const { selectedRoute, selectedStep, responseDirectives, history, context, historyEvents, signal } = params;
1546
1540
  let session = params.session;
1547
1541
 
1548
1542
  // Determine next step (same logic as non-streaming)
@@ -1609,8 +1603,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1609
1603
  rules: selectedRoute.getRules(),
1610
1604
  prohibitions: selectedRoute.getProhibitions(),
1611
1605
  directives: responseDirectives,
1612
- history: historyEvents, // Use Event[] for buildResponsePrompt
1613
- lastMessage: lastMessageText, // Use string for buildResponsePrompt
1606
+ history: historyEvents,
1614
1607
  agentOptions: this.agent.getAgentOptions(),
1615
1608
  combinedGuidelines: [...this.agent.getGuidelines(), ...selectedRoute.getGuidelines()],
1616
1609
  combinedTerms: this.mergeTerms(this.agent.getTerms(), selectedRoute.getTerms()),
@@ -1626,7 +1619,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1626
1619
  const agentOptions = this.agent.getAgentOptions();
1627
1620
  const stream = agentOptions.provider.generateMessageStream({
1628
1621
  prompt: responsePrompt,
1629
- history: historyEvents, // Use Event[] for AI provider
1622
+ history, // Use HistoryItem[] for AI provider
1630
1623
  context,
1631
1624
  tools: availableTools,
1632
1625
  signal,
@@ -1863,34 +1856,34 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1863
1856
  toolLoopCount++;
1864
1857
  logger.debug(`[ResponseModal] Starting tool loop ${toolLoopCount}/${MAX_TOOL_LOOPS} with ${toolCalls?.length || 0} tool calls`);
1865
1858
 
1866
- // Create tool result events with proper Event format structure
1867
- const toolResultEvents: Event<ToolEventData>[] = [];
1859
+ // Create tool result history items
1860
+ const toolResultHistoryItems: HistoryItem[] = [];
1868
1861
  for (const toolCall of toolCalls || []) {
1869
1862
  const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
1870
1863
  if (tool) {
1871
- // Create proper Event format for tool results
1872
- const toolResultEvent: Event<ToolEventData> = {
1873
- kind: EventKind.TOOL,
1874
- source: MessageRole.AGENT,
1875
- timestamp: new Date().toISOString(),
1876
- data: {
1877
- tool_calls: [
1878
- {
1879
- tool_id: toolCall.toolName,
1880
- arguments: toolCall.arguments,
1881
- result: {
1882
- data: "Tool executed successfully",
1883
- },
1884
- },
1885
- ],
1886
- },
1887
- };
1888
- toolResultEvents.push(toolResultEvent);
1864
+ // Create HistoryItem format for tool results
1865
+ // Add assistant message with tool_calls
1866
+ toolResultHistoryItems.push({
1867
+ role: "assistant" as const,
1868
+ content: null,
1869
+ tool_calls: [{
1870
+ id: toolCall.toolName,
1871
+ name: toolCall.toolName,
1872
+ arguments: toolCall.arguments,
1873
+ }],
1874
+ });
1875
+ // Add tool result
1876
+ toolResultHistoryItems.push({
1877
+ role: "tool" as const,
1878
+ tool_call_id: toolCall.toolName,
1879
+ name: toolCall.toolName,
1880
+ content: "Tool executed successfully",
1881
+ });
1889
1882
  }
1890
1883
  }
1891
1884
 
1892
- // Create updated history with tool results (combine Event arrays)
1893
- const updatedHistoryEvents = [...historyEvents, ...toolResultEvents];
1885
+ // Create updated history with tool results
1886
+ const updatedHistory = [...history, ...toolResultHistoryItems];
1894
1887
 
1895
1888
  // Make follow-up AI call to see if more tools are needed
1896
1889
  // After first iteration, don't provide tools to force a text response
@@ -1905,7 +1898,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1905
1898
 
1906
1899
  const followUpResult = await agentOptions.provider.generateMessage({
1907
1900
  prompt: responsePrompt + (toolLoopCount > 1 ? "\n\nProvide a text response to the user based on the tool results." : ""),
1908
- history: updatedHistoryEvents, // Use Event[] for AI provider
1901
+ history: updatedHistory, // Use HistoryItem[] for AI provider
1909
1902
  context,
1910
1903
  tools: shouldProvideTools ? availableTools : [], // Only provide tools on first iteration
1911
1904
  parameters: responseSchema ? {
@@ -1949,7 +1942,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
1949
1942
  context,
1950
1943
  updateContext: this.agent.updateContext.bind(this.agent),
1951
1944
  updateData: this.agent.updateCollectedData.bind(this.agent),
1952
- history: updatedHistoryEvents, // Use Event[] for tool execution
1945
+ history: historyToEvents(updatedHistory), // Convert to Event[] for tool execution
1953
1946
  data: session.data,
1954
1947
  toolArguments: toolCall.arguments,
1955
1948
  });
@@ -2121,11 +2114,11 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
2121
2114
  selectedRoute: Route<TContext, TData>;
2122
2115
  session: SessionState<TData>;
2123
2116
  context: TContext;
2124
- lastMessageText: string; // String version for buildResponsePrompt
2125
- historyEvents: Event[]; // Event[] version for buildResponsePrompt
2117
+ history: HistoryItem[];
2118
+ historyEvents: Event[];
2126
2119
  signal?: AbortSignal;
2127
2120
  }): Promise<string> {
2128
- const { selectedRoute, session, context, lastMessageText, historyEvents, signal } = params;
2121
+ const { selectedRoute, session, context, history, historyEvents, signal } = params;
2129
2122
 
2130
2123
  // Get endStep spec from route
2131
2124
  const endStepSpec = selectedRoute.endStepSpec;
@@ -2181,7 +2174,6 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
2181
2174
  completitionPrompt,
2182
2175
  ],
2183
2176
  history: historyEvents,
2184
- lastMessage: lastMessageText,
2185
2177
  agentOptions: this.agent.getAgentOptions(),
2186
2178
  combinedGuidelines: alwaysActiveGuidelines, // Only non-conditional guidelines
2187
2179
  combinedTerms: this.mergeTerms(this.agent.getTerms(), selectedRoute.getTerms()),
@@ -2196,7 +2188,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
2196
2188
 
2197
2189
  const completionResult = await agentOptions.provider.generateMessage({
2198
2190
  prompt: completionPrompt,
2199
- history: historyEvents,
2191
+ history, // Use HistoryItem[] for AI provider
2200
2192
  context,
2201
2193
  signal,
2202
2194
  parameters: { jsonSchema: completionSchema, schemaName: "completion_message" },
@@ -2240,11 +2232,11 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
2240
2232
  selectedRoute: Route<TContext, TData>;
2241
2233
  session: SessionState<TData>;
2242
2234
  context: TContext;
2243
- lastMessageText: string; // String version for buildResponsePrompt
2244
- historyEvents: Event[]; // Event[] version for buildResponsePrompt
2235
+ history: HistoryItem[];
2236
+ historyEvents: Event[];
2245
2237
  signal?: AbortSignal;
2246
2238
  }): AsyncGenerator<AgentResponseStreamChunk<TData>> {
2247
- const { selectedRoute, context, lastMessageText, historyEvents, signal } = params;
2239
+ const { selectedRoute, context, history, historyEvents, signal } = params;
2248
2240
  let session = params.session;
2249
2241
 
2250
2242
  // Get endStep spec from route
@@ -2270,8 +2262,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
2270
2262
  rules: selectedRoute.getRules(),
2271
2263
  prohibitions: selectedRoute.getProhibitions(),
2272
2264
  directives: undefined, // No directives for completion
2273
- history: historyEvents, // Use Event[] for buildResponsePrompt
2274
- lastMessage: lastMessageText, // Use string for buildResponsePrompt
2265
+ history: historyEvents,
2275
2266
  agentOptions: this.agent.getAgentOptions(),
2276
2267
  combinedGuidelines: [...this.agent.getGuidelines(), ...selectedRoute.getGuidelines()],
2277
2268
  combinedTerms: this.mergeTerms(this.agent.getTerms(), selectedRoute.getTerms()),
@@ -2284,7 +2275,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
2284
2275
  const agentOptions = this.agent.getAgentOptions();
2285
2276
  const stream = agentOptions.provider.generateMessageStream({
2286
2277
  prompt: completionPrompt,
2287
- history: historyEvents, // Use Event[] for AI provider
2278
+ history, // Use HistoryItem[] for AI provider
2288
2279
  context,
2289
2280
  signal,
2290
2281
  parameters: { jsonSchema: responseSchema, schemaName: "completion_message_stream" },
@@ -2353,7 +2344,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
2353
2344
  * @private
2354
2345
  */
2355
2346
  private async generateFallbackResponse(params: {
2356
- history: Event[]; // Use Event[] for buildFallbackPrompt
2347
+ history: HistoryItem[];
2357
2348
  context: TContext;
2358
2349
  session: SessionState<TData>;
2359
2350
  signal?: AbortSignal;
@@ -2364,7 +2355,6 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
2364
2355
 
2365
2356
  // Build basic response prompt without route context
2366
2357
  const fallbackPrompt = await this.responseEngine.buildFallbackPrompt({
2367
- history,
2368
2358
  agentOptions: this.agent.getAgentOptions(),
2369
2359
  terms: this.agent.getTerms(),
2370
2360
  guidelines: this.agent.getGuidelines(),
@@ -2397,7 +2387,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
2397
2387
  * @private
2398
2388
  */
2399
2389
  private async *streamFallbackResponse(params: {
2400
- history: Event[]; // Use Event[] for buildFallbackPrompt
2390
+ history: HistoryItem[];
2401
2391
  context: TContext;
2402
2392
  session: SessionState<TData>;
2403
2393
  signal?: AbortSignal;
@@ -2405,7 +2395,6 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
2405
2395
  const { history, context, session, signal } = params;
2406
2396
 
2407
2397
  const fallbackPrompt = await this.responseEngine.buildFallbackPrompt({
2408
- history,
2409
2398
  agentOptions: this.agent.getAgentOptions(),
2410
2399
  terms: this.agent.getTerms(),
2411
2400
  guidelines: this.agent.getGuidelines(),
@@ -10,7 +10,7 @@ import type {
10
10
  Tool,
11
11
  RouteTransitionConfig,
12
12
  } from "../types";
13
- import { EventKind, MessageRole } from "../types/history";
13
+ import type { HistoryItem } from "../types/history";
14
14
  import {
15
15
  createSession,
16
16
  enterRoute,
@@ -18,6 +18,7 @@ import {
18
18
  mergeCollected,
19
19
  logger,
20
20
  render,
21
+ historyToEvents,
21
22
  } from "../utils";
22
23
  import { createTemplateContext } from "../utils/template";
23
24
  import { Route } from "../core/Route";
@@ -171,7 +172,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
171
172
  };
172
173
  }
173
174
  }
174
-
175
+
175
176
  // If no pending transition or transition handled, do normal routing
176
177
  if (routes.length > 0 && !selectedRoute) {
177
178
  const orchestration = await this.routingEngine.decideRouteAndStep({
@@ -242,7 +243,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
242
243
  currentStep, // Pass current step instead of undefined to maintain progression
243
244
  createTemplateContext({ data: session.data, session, context: contextToUse })
244
245
  );
245
-
246
+
246
247
  if (candidates.length > 0) {
247
248
  nextStep = candidates[0].step;
248
249
  logger.debug(
@@ -368,7 +369,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
368
369
  selectedRoute?: Route<TContext, TData>;
369
370
  nextStep: Step<TContext, TData>;
370
371
  responsePrompt: string;
371
- history: Event[];
372
+ history: HistoryItem[];
372
373
  context: TContext;
373
374
  session: SessionState<TData>;
374
375
  responseSchema: Record<string, unknown>;
@@ -400,31 +401,30 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
400
401
  );
401
402
 
402
403
  // Add tool execution results to history so AI knows what happened
403
- const toolResultsEvents: Event[] = [];
404
+ const toolResultItems: HistoryItem[] = [];
404
405
  for (const toolCall of currentToolCalls || []) {
405
406
  const tool = this.findAvailableTool(toolCall.toolName, selectedRoute);
406
407
  if (tool) {
407
- toolResultsEvents.push({
408
- kind: EventKind.TOOL,
409
- source: MessageRole.AGENT,
410
- timestamp: new Date().toISOString(),
411
- data: {
412
- tool_calls: [
413
- {
414
- tool_id: toolCall.toolName,
415
- arguments: toolCall.arguments,
416
- result: {
417
- data: "Tool executed successfully",
418
- },
419
- },
420
- ],
421
- },
408
+ toolResultItems.push({
409
+ role: "assistant" as const,
410
+ content: null,
411
+ tool_calls: [{
412
+ id: toolCall.toolName,
413
+ name: toolCall.toolName,
414
+ arguments: toolCall.arguments,
415
+ }],
416
+ });
417
+ toolResultItems.push({
418
+ role: "tool" as const,
419
+ tool_call_id: toolCall.toolName,
420
+ name: toolCall.toolName,
421
+ content: "Tool executed successfully",
422
422
  });
423
423
  }
424
424
  }
425
425
 
426
426
  // Create updated history with tool results
427
- const updatedHistory = [...history, ...toolResultsEvents];
427
+ const updatedHistory = [...history, ...toolResultItems];
428
428
 
429
429
  // Make follow-up AI call to see if more tools are needed
430
430
  const followUpResult = await this.options.provider.generateMessage({
@@ -454,7 +454,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
454
454
  selectedRoute,
455
455
  context,
456
456
  session: currentSession,
457
- history: updatedHistory,
457
+ history: historyToEvents(updatedHistory),
458
458
  isStreaming,
459
459
  });
460
460
 
@@ -12,7 +12,7 @@ import type { Step } from "./Step";
12
12
  import { PromptComposer } from "./PromptComposer";
13
13
  import { PromptSectionCache } from "./PromptSectionCache";
14
14
  import { END_ROUTE_ID } from "../constants";
15
- import { createTemplateContext, getLastMessageFromHistory, logger } from "../utils";
15
+ import { createTemplateContext, getLastMessageFromHistory, logger, eventsToHistory } from "../utils";
16
16
 
17
17
  export interface CandidateStep<TContext = unknown, TData = unknown> {
18
18
  step: Step<TContext, TData>;
@@ -246,7 +246,7 @@ export class RoutingEngine<TContext = unknown, TData = unknown> {
246
246
  }
247
247
  >({
248
248
  prompt: stepPrompt,
249
- history,
249
+ history: eventsToHistory(history),
250
250
  context,
251
251
  signal,
252
252
  parameters: {
@@ -659,7 +659,7 @@ export class RoutingEngine<TContext = unknown, TData = unknown> {
659
659
  RoutingDecisionOutput
660
660
  >({
661
661
  prompt: routingPrompt,
662
- history,
662
+ history: eventsToHistory(history),
663
663
  context,
664
664
  signal,
665
665
  parameters: {
@@ -15,6 +15,7 @@ import type {
15
15
  GenerateMessageStreamChunk,
16
16
  AgentStructuredResponse,
17
17
  } from "../types";
18
+ import type { HistoryItem } from "../types/history";
18
19
  import { withTimeoutAndRetry, logger } from "../utils";
19
20
 
20
21
  const DEFAULT_RETRY_CONFIG = {
@@ -152,6 +153,67 @@ export class AnthropicProvider implements AiProvider {
152
153
  };
153
154
  }
154
155
 
156
+ /**
157
+ * Build Anthropic-formatted messages from HistoryItem[] array.
158
+ * System messages are extracted separately (Anthropic uses a `system` param).
159
+ * Tool results are mapped to Anthropic's tool_result content blocks.
160
+ * Assistant tool_calls are mapped to tool_use content blocks.
161
+ */
162
+ private buildAnthropicMessages(history: HistoryItem[]): {
163
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
164
+ messages: any[];
165
+ systemMessages: string[];
166
+ } {
167
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
168
+ const messages: any[] = [];
169
+ const systemMessages: string[] = [];
170
+
171
+ for (const item of history) {
172
+ switch (item.role) {
173
+ case "system":
174
+ systemMessages.push(item.content);
175
+ break;
176
+ case "user":
177
+ messages.push({ role: "user", content: item.content });
178
+ break;
179
+ case "assistant":
180
+ if (item.tool_calls && item.tool_calls.length > 0) {
181
+ const content: Array<Record<string, unknown>> = [];
182
+ if (item.content) {
183
+ content.push({ type: "text", text: item.content });
184
+ }
185
+ for (const tc of item.tool_calls) {
186
+ content.push({
187
+ type: "tool_use",
188
+ id: tc.id,
189
+ name: tc.name,
190
+ input: tc.arguments,
191
+ });
192
+ }
193
+ messages.push({ role: "assistant", content });
194
+ } else {
195
+ messages.push({ role: "assistant", content: item.content || "" });
196
+ }
197
+ break;
198
+ case "tool":
199
+ // Anthropic tool results are sent as user messages with tool_result content blocks
200
+ messages.push({
201
+ role: "user",
202
+ content: [
203
+ {
204
+ type: "tool_result",
205
+ tool_use_id: item.tool_call_id,
206
+ content: typeof item.content === "string" ? item.content : JSON.stringify(item.content),
207
+ },
208
+ ],
209
+ });
210
+ break;
211
+ }
212
+ }
213
+
214
+ return { messages, systemMessages };
215
+ }
216
+
155
217
  async generateMessage<
156
218
  TContext = unknown,
157
219
  TStructured = AgentStructuredResponse
@@ -248,18 +310,36 @@ export class AnthropicProvider implements AiProvider {
248
310
  // Anthropic requires max_tokens to be specified
249
311
  const maxTokens = input.parameters?.maxOutputTokens || 4096;
250
312
 
313
+ // Build messages from history
314
+ const { messages: historyMessages, systemMessages } = this.buildAnthropicMessages(input.history);
315
+
316
+ // Append the current prompt as the final user message
317
+ historyMessages.push({
318
+ role: "user",
319
+ content: input.prompt,
320
+ });
321
+
251
322
  const params: MessageCreateParamsNonStreaming = {
252
323
  model,
253
324
  max_tokens: maxTokens,
254
- messages: [
255
- {
256
- role: "user",
257
- content: input.prompt,
258
- },
259
- ],
325
+ messages: historyMessages,
260
326
  ...this.config,
261
327
  };
262
328
 
329
+ // Set system messages from history if present
330
+ if (systemMessages.length > 0) {
331
+ if (typeof this.config?.system === "string") {
332
+ params.system = `${this.config.system}\n\n${systemMessages.join("\n\n")}`;
333
+ } else if (Array.isArray(this.config?.system)) {
334
+ params.system = [
335
+ ...this.config.system,
336
+ ...systemMessages.map(s => ({ type: "text" as const, text: s })),
337
+ ];
338
+ } else {
339
+ params.system = systemMessages.join("\n\n");
340
+ }
341
+ }
342
+
263
343
  // Add tools if provided
264
344
  if (input.tools && input.tools.length > 0) {
265
345
  params.tools = input.tools.map((tool) => ({
@@ -438,19 +518,37 @@ export class AnthropicProvider implements AiProvider {
438
518
  // Anthropic requires max_tokens to be specified
439
519
  const maxTokens = input.parameters?.maxOutputTokens || 4096;
440
520
 
521
+ // Build messages from history
522
+ const { messages: historyMessages, systemMessages } = this.buildAnthropicMessages(input.history);
523
+
524
+ // Append the current prompt as the final user message
525
+ historyMessages.push({
526
+ role: "user" as const,
527
+ content: input.prompt,
528
+ });
529
+
441
530
  const params = {
442
531
  model,
443
532
  max_tokens: maxTokens,
444
- messages: [
445
- {
446
- role: "user" as const,
447
- content: input.prompt,
448
- },
449
- ],
533
+ messages: historyMessages,
450
534
  stream: true,
451
535
  ...this.config,
452
536
  };
453
537
 
538
+ // Set system messages from history if present
539
+ if (systemMessages.length > 0) {
540
+ if (typeof this.config?.system === "string") {
541
+ params.system = `${this.config.system}\n\n${systemMessages.join("\n\n")}`;
542
+ } else if (Array.isArray(this.config?.system)) {
543
+ params.system = [
544
+ ...this.config.system,
545
+ ...systemMessages.map(s => ({ type: "text" as const, text: s })),
546
+ ];
547
+ } else {
548
+ params.system = systemMessages.join("\n\n");
549
+ }
550
+ }
551
+
454
552
  // Add tools if provided
455
553
  if (input.tools && input.tools.length > 0) {
456
554
  params.tools = input.tools.map((tool) => ({