@falai/agent 1.2.0 → 1.2.2
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/dist/cjs/core/ResponseEngine.d.ts +0 -2
- package/dist/cjs/core/ResponseEngine.d.ts.map +1 -1
- package/dist/cjs/core/ResponseEngine.js +2 -6
- package/dist/cjs/core/ResponseEngine.js.map +1 -1
- package/dist/cjs/core/ResponseModal.d.ts.map +1 -1
- package/dist/cjs/core/ResponseModal.js +45 -54
- package/dist/cjs/core/ResponseModal.js.map +1 -1
- package/dist/cjs/core/ResponsePipeline.d.ts +2 -1
- package/dist/cjs/core/ResponsePipeline.d.ts.map +1 -1
- package/dist/cjs/core/ResponsePipeline.js +17 -19
- package/dist/cjs/core/ResponsePipeline.js.map +1 -1
- package/dist/cjs/core/RoutingEngine.d.ts +18 -0
- package/dist/cjs/core/RoutingEngine.d.ts.map +1 -1
- package/dist/cjs/core/RoutingEngine.js +108 -2
- package/dist/cjs/core/RoutingEngine.js.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.d.ts +7 -0
- package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.js +101 -12
- package/dist/cjs/providers/AnthropicProvider.js.map +1 -1
- package/dist/cjs/providers/GeminiProvider.d.ts +7 -0
- package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/cjs/providers/GeminiProvider.js +81 -2
- package/dist/cjs/providers/GeminiProvider.js.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.d.ts +5 -0
- package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.js +51 -12
- package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.d.ts +5 -0
- package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.js +50 -12
- package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
- package/dist/cjs/types/ai.d.ts +2 -2
- package/dist/cjs/types/ai.d.ts.map +1 -1
- package/dist/core/ResponseEngine.d.ts +0 -2
- package/dist/core/ResponseEngine.d.ts.map +1 -1
- package/dist/core/ResponseEngine.js +2 -6
- package/dist/core/ResponseEngine.js.map +1 -1
- package/dist/core/ResponseModal.d.ts.map +1 -1
- package/dist/core/ResponseModal.js +46 -55
- package/dist/core/ResponseModal.js.map +1 -1
- package/dist/core/ResponsePipeline.d.ts +2 -1
- package/dist/core/ResponsePipeline.d.ts.map +1 -1
- package/dist/core/ResponsePipeline.js +18 -20
- package/dist/core/ResponsePipeline.js.map +1 -1
- package/dist/core/RoutingEngine.d.ts +18 -0
- package/dist/core/RoutingEngine.d.ts.map +1 -1
- package/dist/core/RoutingEngine.js +109 -3
- package/dist/core/RoutingEngine.js.map +1 -1
- package/dist/providers/AnthropicProvider.d.ts +7 -0
- package/dist/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/providers/AnthropicProvider.js +101 -12
- package/dist/providers/AnthropicProvider.js.map +1 -1
- package/dist/providers/GeminiProvider.d.ts +7 -0
- package/dist/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/providers/GeminiProvider.js +81 -2
- package/dist/providers/GeminiProvider.js.map +1 -1
- package/dist/providers/OpenAIProvider.d.ts +5 -0
- package/dist/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/providers/OpenAIProvider.js +51 -12
- package/dist/providers/OpenAIProvider.js.map +1 -1
- package/dist/providers/OpenRouterProvider.d.ts +5 -0
- package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/providers/OpenRouterProvider.js +50 -12
- package/dist/providers/OpenRouterProvider.js.map +1 -1
- package/dist/types/ai.d.ts +2 -2
- package/dist/types/ai.d.ts.map +1 -1
- package/docs/core/agent/session-management.md +53 -0
- package/docs/core/conversation-flows/routes.md +2 -0
- package/package.json +1 -1
- package/src/core/ResponseEngine.ts +2 -9
- package/src/core/ResponseModal.ts +56 -67
- package/src/core/ResponsePipeline.ts +22 -22
- package/src/core/RoutingEngine.ts +159 -3
- package/src/providers/AnthropicProvider.ts +110 -12
- package/src/providers/GeminiProvider.ts +91 -2
- package/src/providers/OpenAIProvider.ts +56 -12
- package/src/providers/OpenRouterProvider.ts +55 -12
- 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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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[];
|
|
1380
|
+
history: HistoryItem[];
|
|
1384
1381
|
context: TContext;
|
|
1385
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
1867
|
-
const
|
|
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
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
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
|
|
1893
|
-
const
|
|
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:
|
|
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:
|
|
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
|
-
|
|
2125
|
-
historyEvents: Event[];
|
|
2117
|
+
history: HistoryItem[];
|
|
2118
|
+
historyEvents: Event[];
|
|
2126
2119
|
signal?: AbortSignal;
|
|
2127
2120
|
}): Promise<string> {
|
|
2128
|
-
const { selectedRoute, session, context,
|
|
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
|
|
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
|
-
|
|
2244
|
-
historyEvents: Event[];
|
|
2235
|
+
history: HistoryItem[];
|
|
2236
|
+
historyEvents: Event[];
|
|
2245
2237
|
signal?: AbortSignal;
|
|
2246
2238
|
}): AsyncGenerator<AgentResponseStreamChunk<TData>> {
|
|
2247
|
-
const { selectedRoute, context,
|
|
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,
|
|
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
|
|
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:
|
|
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:
|
|
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 {
|
|
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:
|
|
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
|
|
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
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
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, ...
|
|
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
|
|
|
@@ -6,13 +6,14 @@ import type {
|
|
|
6
6
|
AiProvider,
|
|
7
7
|
TemplateContext,
|
|
8
8
|
} from "../types";
|
|
9
|
+
import { MessageRole } from "../types";
|
|
9
10
|
import { enterRoute, mergeCollected } from "../utils";
|
|
10
11
|
import type { Route } from "./Route";
|
|
11
12
|
import type { Step } from "./Step";
|
|
12
13
|
import { PromptComposer } from "./PromptComposer";
|
|
13
14
|
import { PromptSectionCache } from "./PromptSectionCache";
|
|
14
15
|
import { END_ROUTE_ID } from "../constants";
|
|
15
|
-
import { createTemplateContext, getLastMessageFromHistory, logger } from "../utils";
|
|
16
|
+
import { createTemplateContext, getLastMessageFromHistory, logger, eventsToHistory } from "../utils";
|
|
16
17
|
|
|
17
18
|
export interface CandidateStep<TContext = unknown, TData = unknown> {
|
|
18
19
|
step: Step<TContext, TData>;
|
|
@@ -83,6 +84,148 @@ export interface BuildRoutingPromptParams<TContext = unknown, TData = unknown> {
|
|
|
83
84
|
export class RoutingEngine<TContext = unknown, TData = unknown> {
|
|
84
85
|
constructor(private readonly options?: RoutingEngineOptions) { }
|
|
85
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Check whether the history contains any user messages.
|
|
89
|
+
* Used to detect "session resume" scenarios where a route/step was
|
|
90
|
+
* pre-set programmatically and the conversation starts with only
|
|
91
|
+
* system messages (or no messages at all).
|
|
92
|
+
* @private
|
|
93
|
+
*/
|
|
94
|
+
private hasUserMessages(history: Event[]): boolean {
|
|
95
|
+
return history.some(
|
|
96
|
+
(event) => event.source === MessageRole.USER
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Handle the "session resume" fast-path: when a session already has a
|
|
102
|
+
* pre-set currentRoute (and optionally currentStep) and the conversation
|
|
103
|
+
* history contains no user messages, honor the pre-set position instead
|
|
104
|
+
* of running AI route/step selection.
|
|
105
|
+
*
|
|
106
|
+
* Returns `undefined` when the fast-path does not apply.
|
|
107
|
+
* @private
|
|
108
|
+
*/
|
|
109
|
+
private async handleSessionResume(params: {
|
|
110
|
+
routes: Route<TContext, TData>[];
|
|
111
|
+
session: SessionState<TData>;
|
|
112
|
+
history: Event[];
|
|
113
|
+
context: TContext;
|
|
114
|
+
}): Promise<{
|
|
115
|
+
selectedRoute?: Route<TContext, TData>;
|
|
116
|
+
selectedStep?: Step<TContext, TData>;
|
|
117
|
+
session: SessionState<TData>;
|
|
118
|
+
isRouteComplete?: boolean;
|
|
119
|
+
completedRoutes?: Route<TContext, TData>[];
|
|
120
|
+
} | undefined> {
|
|
121
|
+
const { routes, session, history, context } = params;
|
|
122
|
+
|
|
123
|
+
// Fast-path only applies when:
|
|
124
|
+
// 1. Session already has a currentRoute set
|
|
125
|
+
// 2. There are no user messages in the history (system-only or empty)
|
|
126
|
+
if (!session.currentRoute || this.hasUserMessages(history)) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Find the pre-set route among available routes
|
|
131
|
+
const presetRoute = routes.find(
|
|
132
|
+
(r) => r.id === session.currentRoute!.id
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
if (!presetRoute) {
|
|
136
|
+
logger.warn(
|
|
137
|
+
`[RoutingEngine] Session resume: pre-set route '${session.currentRoute.id}' not found among available routes, falling back to normal routing`
|
|
138
|
+
);
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
logger.debug(
|
|
143
|
+
`[RoutingEngine] Session resume: honoring pre-set route '${presetRoute.title}' (no user messages in history)`
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// Enter route if needed (merges initialData, no-op if already entered)
|
|
147
|
+
const updatedSession = this.enterRouteIfNeeded(session, presetRoute);
|
|
148
|
+
|
|
149
|
+
// Evaluate cross-route completions
|
|
150
|
+
const completedRoutes = this.evaluateRouteCompletions(routes, updatedSession.data || {});
|
|
151
|
+
|
|
152
|
+
// If a currentStep is also pre-set, honor it — stay on that step
|
|
153
|
+
if (session.currentStep) {
|
|
154
|
+
const presetStep = presetRoute.getStep(session.currentStep.id);
|
|
155
|
+
|
|
156
|
+
if (presetStep) {
|
|
157
|
+
logger.debug(
|
|
158
|
+
`[RoutingEngine] Session resume: honoring pre-set step '${presetStep.id}'`
|
|
159
|
+
);
|
|
160
|
+
return {
|
|
161
|
+
selectedRoute: presetRoute,
|
|
162
|
+
selectedStep: presetStep,
|
|
163
|
+
session: updatedSession,
|
|
164
|
+
isRouteComplete: false,
|
|
165
|
+
completedRoutes,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
logger.warn(
|
|
170
|
+
`[RoutingEngine] Session resume: pre-set step '${session.currentStep.id}' not found in route '${presetRoute.title}', resolving from initial step`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// No currentStep pre-set (or it wasn't found) — resolve from initialStep
|
|
175
|
+
// using the standard candidate logic (handles skipIf, etc.)
|
|
176
|
+
const templateContext = createTemplateContext({
|
|
177
|
+
context,
|
|
178
|
+
session: updatedSession,
|
|
179
|
+
history,
|
|
180
|
+
data: updatedSession.data,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const candidates = await this.getCandidateStepsWithConditions(
|
|
184
|
+
presetRoute,
|
|
185
|
+
undefined, // No current step — start from beginning
|
|
186
|
+
templateContext
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
if (candidates.length === 0) {
|
|
190
|
+
logger.warn(
|
|
191
|
+
`[RoutingEngine] Session resume: no valid steps found for route '${presetRoute.title}'`
|
|
192
|
+
);
|
|
193
|
+
return {
|
|
194
|
+
selectedRoute: presetRoute,
|
|
195
|
+
selectedStep: undefined,
|
|
196
|
+
session: updatedSession,
|
|
197
|
+
isRouteComplete: false,
|
|
198
|
+
completedRoutes,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const candidate = candidates[0];
|
|
203
|
+
|
|
204
|
+
if (candidate.isRouteComplete) {
|
|
205
|
+
logger.debug(
|
|
206
|
+
`[RoutingEngine] Session resume: route '${presetRoute.title}' is already complete`
|
|
207
|
+
);
|
|
208
|
+
return {
|
|
209
|
+
selectedRoute: presetRoute,
|
|
210
|
+
selectedStep: undefined,
|
|
211
|
+
session: updatedSession,
|
|
212
|
+
isRouteComplete: true,
|
|
213
|
+
completedRoutes,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
logger.debug(
|
|
218
|
+
`[RoutingEngine] Session resume: resolved initial step '${candidate.step.id}'`
|
|
219
|
+
);
|
|
220
|
+
return {
|
|
221
|
+
selectedRoute: presetRoute,
|
|
222
|
+
selectedStep: candidate.step,
|
|
223
|
+
session: updatedSession,
|
|
224
|
+
isRouteComplete: false,
|
|
225
|
+
completedRoutes,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
86
229
|
/**
|
|
87
230
|
* Enter a route if not already in it, merging initial data
|
|
88
231
|
* @private
|
|
@@ -246,7 +389,7 @@ export class RoutingEngine<TContext = unknown, TData = unknown> {
|
|
|
246
389
|
}
|
|
247
390
|
>({
|
|
248
391
|
prompt: stepPrompt,
|
|
249
|
-
history,
|
|
392
|
+
history: eventsToHistory(history),
|
|
250
393
|
context,
|
|
251
394
|
signal,
|
|
252
395
|
parameters: {
|
|
@@ -543,6 +686,19 @@ export class RoutingEngine<TContext = unknown, TData = unknown> {
|
|
|
543
686
|
return { session };
|
|
544
687
|
}
|
|
545
688
|
|
|
689
|
+
// SESSION RESUME: If the session has a pre-set route and there are no user
|
|
690
|
+
// messages in the history, honor the pre-set position without AI routing.
|
|
691
|
+
// This supports programmatic session setup and persistence-based resume.
|
|
692
|
+
const resumeResult = await this.handleSessionResume({
|
|
693
|
+
routes,
|
|
694
|
+
session,
|
|
695
|
+
history,
|
|
696
|
+
context,
|
|
697
|
+
});
|
|
698
|
+
if (resumeResult) {
|
|
699
|
+
return resumeResult;
|
|
700
|
+
}
|
|
701
|
+
|
|
546
702
|
// CROSS-ROUTE COMPLETION EVALUATION: Check all routes for completion
|
|
547
703
|
const completedRoutes = this.evaluateRouteCompletions(routes, session.data || {});
|
|
548
704
|
|
|
@@ -659,7 +815,7 @@ export class RoutingEngine<TContext = unknown, TData = unknown> {
|
|
|
659
815
|
RoutingDecisionOutput
|
|
660
816
|
>({
|
|
661
817
|
prompt: routingPrompt,
|
|
662
|
-
history,
|
|
818
|
+
history: eventsToHistory(history),
|
|
663
819
|
context,
|
|
664
820
|
signal,
|
|
665
821
|
parameters: {
|