@falai/agent 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -74
- package/dist/cjs/core/Agent.d.ts +22 -29
- package/dist/cjs/core/Agent.d.ts.map +1 -1
- package/dist/cjs/core/Agent.js +464 -291
- package/dist/cjs/core/Agent.js.map +1 -1
- package/dist/cjs/core/Events.d.ts +10 -1
- package/dist/cjs/core/Events.d.ts.map +1 -1
- package/dist/cjs/core/Events.js +3 -2
- package/dist/cjs/core/Events.js.map +1 -1
- package/dist/cjs/core/PersistenceManager.d.ts +19 -0
- package/dist/cjs/core/PersistenceManager.d.ts.map +1 -1
- package/dist/cjs/core/PersistenceManager.js +57 -0
- package/dist/cjs/core/PersistenceManager.js.map +1 -1
- package/dist/cjs/core/PromptComposer.d.ts +24 -0
- package/dist/cjs/core/PromptComposer.d.ts.map +1 -0
- package/dist/cjs/core/PromptComposer.js +127 -0
- package/dist/cjs/core/PromptComposer.js.map +1 -0
- package/dist/cjs/core/ResponseEngine.d.ts +19 -0
- package/dist/cjs/core/ResponseEngine.d.ts.map +1 -0
- package/dist/cjs/core/ResponseEngine.js +51 -0
- package/dist/cjs/core/ResponseEngine.js.map +1 -0
- package/dist/cjs/core/Route.d.ts +18 -12
- package/dist/cjs/core/Route.d.ts.map +1 -1
- package/dist/cjs/core/Route.js +15 -9
- package/dist/cjs/core/Route.js.map +1 -1
- package/dist/cjs/core/RoutingEngine.d.ts +38 -0
- package/dist/cjs/core/RoutingEngine.d.ts.map +1 -0
- package/dist/cjs/core/RoutingEngine.js +110 -0
- package/dist/cjs/core/RoutingEngine.js.map +1 -0
- package/dist/cjs/core/State.d.ts +15 -4
- package/dist/cjs/core/State.d.ts.map +1 -1
- package/dist/cjs/core/State.js +21 -2
- package/dist/cjs/core/State.js.map +1 -1
- package/dist/cjs/core/ToolExecutor.d.ts +29 -0
- package/dist/cjs/core/ToolExecutor.d.ts.map +1 -0
- package/dist/cjs/core/ToolExecutor.js +73 -0
- package/dist/cjs/core/ToolExecutor.js.map +1 -0
- package/dist/cjs/core/Transition.d.ts +5 -5
- package/dist/cjs/core/Transition.d.ts.map +1 -1
- package/dist/cjs/core/Transition.js.map +1 -1
- package/dist/cjs/index.d.ts +6 -8
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +8 -10
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.js +10 -13
- package/dist/cjs/providers/AnthropicProvider.js.map +1 -1
- package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/cjs/providers/GeminiProvider.js +12 -8
- package/dist/cjs/providers/GeminiProvider.js.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.js +10 -53
- package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.js +10 -53
- package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +13 -12
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/cjs/types/ai.d.ts +8 -2
- package/dist/cjs/types/ai.d.ts.map +1 -1
- package/dist/cjs/types/history.d.ts +8 -0
- package/dist/cjs/types/history.d.ts.map +1 -1
- package/dist/cjs/types/index.d.ts +0 -3
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/cjs/types/index.js +1 -3
- package/dist/cjs/types/index.js.map +1 -1
- package/dist/cjs/types/route.d.ts +39 -4
- package/dist/cjs/types/route.d.ts.map +1 -1
- package/dist/cjs/types/routing.d.ts +16 -0
- package/dist/cjs/types/routing.d.ts.map +1 -0
- package/dist/cjs/types/routing.js +3 -0
- package/dist/cjs/types/routing.js.map +1 -0
- package/dist/cjs/types/schema.d.ts +22 -0
- package/dist/cjs/types/schema.d.ts.map +1 -0
- package/dist/cjs/types/schema.js +3 -0
- package/dist/cjs/types/schema.js.map +1 -0
- package/dist/cjs/types/session.d.ts +72 -0
- package/dist/cjs/types/session.d.ts.map +1 -0
- package/dist/cjs/types/session.js +140 -0
- package/dist/cjs/types/session.js.map +1 -0
- package/dist/cjs/types/tool.d.ts +11 -5
- package/dist/cjs/types/tool.d.ts.map +1 -1
- package/dist/cjs/utils/id.d.ts +0 -5
- package/dist/cjs/utils/id.d.ts.map +1 -1
- package/dist/cjs/utils/id.js +0 -10
- package/dist/cjs/utils/id.js.map +1 -1
- package/dist/cjs/utils/schema.d.ts +17 -0
- package/dist/cjs/utils/schema.d.ts.map +1 -0
- package/dist/cjs/utils/schema.js +32 -0
- package/dist/cjs/utils/schema.js.map +1 -0
- package/dist/core/Agent.d.ts +22 -29
- package/dist/core/Agent.d.ts.map +1 -1
- package/dist/core/Agent.js +464 -291
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/Events.d.ts +10 -1
- package/dist/core/Events.d.ts.map +1 -1
- package/dist/core/Events.js +3 -2
- package/dist/core/Events.js.map +1 -1
- package/dist/core/PersistenceManager.d.ts +19 -0
- package/dist/core/PersistenceManager.d.ts.map +1 -1
- package/dist/core/PersistenceManager.js +57 -0
- package/dist/core/PersistenceManager.js.map +1 -1
- package/dist/core/PromptComposer.d.ts +24 -0
- package/dist/core/PromptComposer.d.ts.map +1 -0
- package/dist/core/PromptComposer.js +123 -0
- package/dist/core/PromptComposer.js.map +1 -0
- package/dist/core/ResponseEngine.d.ts +19 -0
- package/dist/core/ResponseEngine.d.ts.map +1 -0
- package/dist/core/ResponseEngine.js +47 -0
- package/dist/core/ResponseEngine.js.map +1 -0
- package/dist/core/Route.d.ts +18 -12
- package/dist/core/Route.d.ts.map +1 -1
- package/dist/core/Route.js +15 -9
- package/dist/core/Route.js.map +1 -1
- package/dist/core/RoutingEngine.d.ts +38 -0
- package/dist/core/RoutingEngine.d.ts.map +1 -0
- package/dist/core/RoutingEngine.js +106 -0
- package/dist/core/RoutingEngine.js.map +1 -0
- package/dist/core/State.d.ts +15 -4
- package/dist/core/State.d.ts.map +1 -1
- package/dist/core/State.js +21 -2
- package/dist/core/State.js.map +1 -1
- package/dist/core/ToolExecutor.d.ts +29 -0
- package/dist/core/ToolExecutor.d.ts.map +1 -0
- package/dist/core/ToolExecutor.js +69 -0
- package/dist/core/ToolExecutor.js.map +1 -0
- package/dist/core/Transition.d.ts +5 -5
- package/dist/core/Transition.d.ts.map +1 -1
- package/dist/core/Transition.js.map +1 -1
- package/dist/index.d.ts +6 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/dist/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/providers/AnthropicProvider.js +10 -13
- package/dist/providers/AnthropicProvider.js.map +1 -1
- package/dist/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/providers/GeminiProvider.js +12 -8
- package/dist/providers/GeminiProvider.js.map +1 -1
- package/dist/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/providers/OpenAIProvider.js +10 -53
- package/dist/providers/OpenAIProvider.js.map +1 -1
- package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/providers/OpenRouterProvider.js +10 -53
- package/dist/providers/OpenRouterProvider.js.map +1 -1
- package/dist/types/agent.d.ts +13 -12
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/ai.d.ts +8 -2
- package/dist/types/ai.d.ts.map +1 -1
- package/dist/types/history.d.ts +8 -0
- package/dist/types/history.d.ts.map +1 -1
- package/dist/types/index.d.ts +0 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/route.d.ts +39 -4
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/routing.d.ts +16 -0
- package/dist/types/routing.d.ts.map +1 -0
- package/dist/types/routing.js +2 -0
- package/dist/types/routing.js.map +1 -0
- package/dist/types/schema.d.ts +22 -0
- package/dist/types/schema.d.ts.map +1 -0
- package/dist/types/schema.js +2 -0
- package/dist/types/schema.js.map +1 -0
- package/dist/types/session.d.ts +72 -0
- package/dist/types/session.d.ts.map +1 -0
- package/dist/types/session.js +132 -0
- package/dist/types/session.js.map +1 -0
- package/dist/types/tool.d.ts +11 -5
- package/dist/types/tool.d.ts.map +1 -1
- package/dist/utils/id.d.ts +0 -5
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +0 -9
- package/dist/utils/id.js.map +1 -1
- package/dist/utils/schema.d.ts +17 -0
- package/dist/utils/schema.d.ts.map +1 -0
- package/dist/utils/schema.js +27 -0
- package/dist/utils/schema.js.map +1 -0
- package/docs/ADAPTERS.md +83 -3
- package/docs/API_REFERENCE.md +95 -104
- package/docs/ARCHITECTURE.md +284 -286
- package/docs/CONSTRUCTOR_OPTIONS.md +192 -135
- package/docs/CONTEXT_MANAGEMENT.md +311 -28
- package/docs/CONTRIBUTING.md +1 -1
- package/docs/DOMAINS.md +61 -0
- package/docs/GETTING_STARTED.md +177 -88
- package/docs/PERSISTENCE.md +170 -23
- package/docs/README.md +7 -10
- package/examples/business-onboarding.ts +21 -9
- package/examples/company-qna-agent.ts +508 -0
- package/examples/declarative-agent.ts +143 -26
- package/examples/domain-scoping.ts +31 -10
- package/examples/extracted-data-modification.ts +415 -0
- package/examples/healthcare-agent.ts +194 -90
- package/examples/openai-agent.ts +67 -25
- package/examples/opensearch-persistence.ts +455 -151
- package/examples/persistent-onboarding.ts +162 -96
- package/examples/prisma-persistence.ts +371 -125
- package/examples/redis-persistence.ts +393 -23
- package/examples/rules-prohibitions.ts +32 -11
- package/examples/streaming-agent.ts +61 -13
- package/examples/travel-agent.ts +266 -133
- package/package.json +1 -1
- package/src/core/Agent.ts +674 -356
- package/src/core/Events.ts +12 -2
- package/src/core/PersistenceManager.ts +83 -0
- package/src/core/PromptComposer.ts +143 -0
- package/src/core/ResponseEngine.ts +82 -0
- package/src/core/Route.ts +32 -17
- package/src/core/RoutingEngine.ts +165 -0
- package/src/core/State.ts +55 -15
- package/src/core/ToolExecutor.ts +117 -0
- package/src/core/Transition.ts +5 -5
- package/src/index.ts +12 -21
- package/src/providers/AnthropicProvider.ts +10 -13
- package/src/providers/GeminiProvider.ts +12 -8
- package/src/providers/OpenAIProvider.ts +10 -56
- package/src/providers/OpenRouterProvider.ts +10 -56
- package/src/types/agent.ts +16 -13
- package/src/types/ai.ts +6 -2
- package/src/types/history.ts +8 -0
- package/src/types/index.ts +0 -11
- package/src/types/route.ts +41 -5
- package/src/types/routing.ts +18 -0
- package/src/types/schema.ts +23 -0
- package/src/types/session.ts +207 -0
- package/src/types/tool.ts +29 -7
- package/src/utils/id.ts +0 -10
- package/src/utils/schema.ts +32 -0
- package/dist/cjs/core/ConditionEvaluator.d.ts +0 -72
- package/dist/cjs/core/ConditionEvaluator.d.ts.map +0 -1
- package/dist/cjs/core/ConditionEvaluator.js +0 -272
- package/dist/cjs/core/ConditionEvaluator.js.map +0 -1
- package/dist/cjs/core/Observation.d.ts +0 -24
- package/dist/cjs/core/Observation.d.ts.map +0 -1
- package/dist/cjs/core/Observation.js +0 -39
- package/dist/cjs/core/Observation.js.map +0 -1
- package/dist/cjs/core/PreparationEngine.d.ts +0 -116
- package/dist/cjs/core/PreparationEngine.d.ts.map +0 -1
- package/dist/cjs/core/PreparationEngine.js +0 -353
- package/dist/cjs/core/PreparationEngine.js.map +0 -1
- package/dist/cjs/core/PromptBuilder.d.ts +0 -136
- package/dist/cjs/core/PromptBuilder.d.ts.map +0 -1
- package/dist/cjs/core/PromptBuilder.js +0 -421
- package/dist/cjs/core/PromptBuilder.js.map +0 -1
- package/dist/cjs/types/observation.d.ts +0 -27
- package/dist/cjs/types/observation.d.ts.map +0 -1
- package/dist/cjs/types/observation.js +0 -6
- package/dist/cjs/types/observation.js.map +0 -1
- package/dist/cjs/types/prompt.d.ts +0 -46
- package/dist/cjs/types/prompt.d.ts.map +0 -1
- package/dist/cjs/types/prompt.js +0 -19
- package/dist/cjs/types/prompt.js.map +0 -1
- package/dist/core/ConditionEvaluator.d.ts +0 -72
- package/dist/core/ConditionEvaluator.d.ts.map +0 -1
- package/dist/core/ConditionEvaluator.js +0 -268
- package/dist/core/ConditionEvaluator.js.map +0 -1
- package/dist/core/Observation.d.ts +0 -24
- package/dist/core/Observation.d.ts.map +0 -1
- package/dist/core/Observation.js +0 -35
- package/dist/core/Observation.js.map +0 -1
- package/dist/core/PreparationEngine.d.ts +0 -116
- package/dist/core/PreparationEngine.d.ts.map +0 -1
- package/dist/core/PreparationEngine.js +0 -349
- package/dist/core/PreparationEngine.js.map +0 -1
- package/dist/core/PromptBuilder.d.ts +0 -136
- package/dist/core/PromptBuilder.d.ts.map +0 -1
- package/dist/core/PromptBuilder.js +0 -417
- package/dist/core/PromptBuilder.js.map +0 -1
- package/dist/types/observation.d.ts +0 -27
- package/dist/types/observation.d.ts.map +0 -1
- package/dist/types/observation.js +0 -5
- package/dist/types/observation.js.map +0 -1
- package/dist/types/prompt.d.ts +0 -46
- package/dist/types/prompt.d.ts.map +0 -1
- package/dist/types/prompt.js +0 -16
- package/dist/types/prompt.js.map +0 -1
- package/docs/STRUCTURE.md +0 -58
- package/src/core/ConditionEvaluator.ts +0 -381
- package/src/core/Observation.ts +0 -47
- package/src/core/PreparationEngine.ts +0 -561
- package/src/core/PromptBuilder.ts +0 -617
- package/src/types/observation.ts +0 -29
- package/src/types/prompt.ts +0 -49
package/src/core/Agent.ts
CHANGED
|
@@ -2,22 +2,41 @@
|
|
|
2
2
|
* Core Agent implementation
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type {
|
|
6
|
-
|
|
7
|
-
Term,
|
|
8
|
-
Guideline,
|
|
9
|
-
GuidelineMatch,
|
|
10
|
-
Capability,
|
|
11
|
-
} from "../types/agent";
|
|
12
|
-
import type { Event, StateRef } from "../types/index";
|
|
5
|
+
import type { AgentOptions, Term, Guideline, Capability } from "../types/agent";
|
|
6
|
+
import type { Event, StateRef, MessageEventData } from "../types/index";
|
|
13
7
|
import type { RouteOptions } from "../types/route";
|
|
8
|
+
import type { RoutingDecisionOutput } from "./RoutingEngine";
|
|
9
|
+
import type { SessionState } from "../types/session";
|
|
10
|
+
import type { AgentStructuredResponse } from "../types/ai";
|
|
11
|
+
import {
|
|
12
|
+
createSession,
|
|
13
|
+
enterRoute,
|
|
14
|
+
enterState,
|
|
15
|
+
mergeExtracted,
|
|
16
|
+
} from "../types/session";
|
|
17
|
+
import { EventKind } from "../types/history";
|
|
18
|
+
import { PromptComposer } from "./PromptComposer";
|
|
14
19
|
|
|
15
20
|
import { Route } from "./Route";
|
|
21
|
+
import { State } from "./State";
|
|
16
22
|
import { DomainRegistry } from "./DomainRegistry";
|
|
17
|
-
import { PromptBuilder } from "./PromptBuilder";
|
|
18
|
-
import { Observation } from "./Observation";
|
|
19
23
|
import { PersistenceManager } from "./PersistenceManager";
|
|
20
|
-
import {
|
|
24
|
+
import { RoutingEngine } from "./RoutingEngine";
|
|
25
|
+
import { ResponseEngine } from "./ResponseEngine";
|
|
26
|
+
import { ToolExecutor } from "./ToolExecutor";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Helper to extract last message from history
|
|
30
|
+
*/
|
|
31
|
+
function getLastMessageFromHistory(history: Event[]): string {
|
|
32
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
33
|
+
const event = history[i];
|
|
34
|
+
if (event.kind === EventKind.MESSAGE) {
|
|
35
|
+
return (event.data as MessageEventData).message;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return "";
|
|
39
|
+
}
|
|
21
40
|
|
|
22
41
|
/**
|
|
23
42
|
* Main Agent class with generic context support
|
|
@@ -26,12 +45,13 @@ export class Agent<TContext = unknown> {
|
|
|
26
45
|
private terms: Term[] = [];
|
|
27
46
|
private guidelines: Guideline[] = [];
|
|
28
47
|
private capabilities: Capability[] = [];
|
|
29
|
-
|
|
30
|
-
private
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
49
|
+
private routes: Route<TContext, any>[] = [];
|
|
31
50
|
private domainRegistry = new DomainRegistry();
|
|
32
51
|
private context: TContext | undefined;
|
|
33
52
|
private persistenceManager: PersistenceManager | undefined;
|
|
34
|
-
private
|
|
53
|
+
private routingEngine: RoutingEngine<TContext>;
|
|
54
|
+
private responseEngine: ResponseEngine<TContext>;
|
|
35
55
|
|
|
36
56
|
/**
|
|
37
57
|
* Dynamic domain property - populated via addDomain
|
|
@@ -39,11 +59,6 @@ export class Agent<TContext = unknown> {
|
|
|
39
59
|
public readonly domain: Record<string, Record<string, unknown>> = {};
|
|
40
60
|
|
|
41
61
|
constructor(private readonly options: AgentOptions<TContext>) {
|
|
42
|
-
// Initialize with default values
|
|
43
|
-
if (!this.options.maxEngineIterations) {
|
|
44
|
-
this.options.maxEngineIterations = 1;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
62
|
// Validate context configuration
|
|
48
63
|
if (options.context !== undefined && options.contextProvider) {
|
|
49
64
|
throw new Error(
|
|
@@ -54,11 +69,13 @@ export class Agent<TContext = unknown> {
|
|
|
54
69
|
// Initialize context if provided
|
|
55
70
|
this.context = options.context;
|
|
56
71
|
|
|
57
|
-
// Initialize
|
|
58
|
-
this.
|
|
59
|
-
|
|
60
|
-
|
|
72
|
+
// Initialize routing and response engines
|
|
73
|
+
this.routingEngine = new RoutingEngine<TContext>({
|
|
74
|
+
maxCandidates: 5,
|
|
75
|
+
allowRouteSwitch: true,
|
|
76
|
+
switchThreshold: 70,
|
|
61
77
|
});
|
|
78
|
+
this.responseEngine = new ResponseEngine<TContext>();
|
|
62
79
|
|
|
63
80
|
// Initialize persistence if configured
|
|
64
81
|
if (options.persistence) {
|
|
@@ -94,27 +111,7 @@ export class Agent<TContext = unknown> {
|
|
|
94
111
|
|
|
95
112
|
if (options.routes) {
|
|
96
113
|
options.routes.forEach((routeOptions) => {
|
|
97
|
-
this.createRoute(routeOptions);
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (options.observations) {
|
|
102
|
-
options.observations.forEach((obsOptions) => {
|
|
103
|
-
const obs = this.createObservation(obsOptions.description);
|
|
104
|
-
|
|
105
|
-
// If route refs were provided, resolve and disambiguate
|
|
106
|
-
if (obsOptions.routeRefs && obsOptions.routeRefs.length > 0) {
|
|
107
|
-
const resolvedRoutes = obsOptions.routeRefs
|
|
108
|
-
.map((ref) => {
|
|
109
|
-
// Try to find route by ID or title
|
|
110
|
-
return this.routes.find((r) => r.id === ref || r.title === ref);
|
|
111
|
-
})
|
|
112
|
-
.filter((r): r is Route<TContext> => r !== undefined);
|
|
113
|
-
|
|
114
|
-
if (resolvedRoutes.length > 0) {
|
|
115
|
-
obs.disambiguate(resolvedRoutes);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
114
|
+
this.createRoute<unknown>(routeOptions);
|
|
118
115
|
});
|
|
119
116
|
}
|
|
120
117
|
}
|
|
@@ -142,9 +139,12 @@ export class Agent<TContext = unknown> {
|
|
|
142
139
|
|
|
143
140
|
/**
|
|
144
141
|
* Create a new route (journey)
|
|
142
|
+
* @template TExtracted - Type of data extracted throughout the route
|
|
145
143
|
*/
|
|
146
|
-
createRoute
|
|
147
|
-
|
|
144
|
+
createRoute<TExtracted = unknown>(
|
|
145
|
+
options: RouteOptions<TExtracted>
|
|
146
|
+
): Route<TContext, TExtracted> {
|
|
147
|
+
const route = new Route<TContext, TExtracted>(options);
|
|
148
148
|
this.routes.push(route);
|
|
149
149
|
return route;
|
|
150
150
|
}
|
|
@@ -182,25 +182,34 @@ export class Agent<TContext = unknown> {
|
|
|
182
182
|
return this;
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
/**
|
|
186
|
-
* Create an observation for disambiguation
|
|
187
|
-
*/
|
|
188
|
-
createObservation(description: string): Observation {
|
|
189
|
-
const observation = new Observation({ description });
|
|
190
|
-
this.observations.push(observation);
|
|
191
|
-
return observation;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
185
|
/**
|
|
195
186
|
* Add a domain with its tools/methods
|
|
187
|
+
* Automatically tags all ToolRef objects with their domain name for security enforcement
|
|
196
188
|
*/
|
|
197
189
|
addDomain<TName extends string, TDomain extends Record<string, unknown>>(
|
|
198
190
|
name: TName,
|
|
199
191
|
domainObject: TDomain
|
|
200
192
|
): void {
|
|
201
|
-
this
|
|
193
|
+
// Tag all tools in this domain with the domain name for security enforcement
|
|
194
|
+
const taggedDomain = { ...domainObject };
|
|
195
|
+
for (const key in taggedDomain) {
|
|
196
|
+
const value = taggedDomain[key];
|
|
197
|
+
// Check if value is a ToolRef (has handler, id, name properties)
|
|
198
|
+
if (
|
|
199
|
+
value &&
|
|
200
|
+
typeof value === "object" &&
|
|
201
|
+
"handler" in value &&
|
|
202
|
+
"id" in value &&
|
|
203
|
+
"name" in value
|
|
204
|
+
) {
|
|
205
|
+
// Tag the tool with its domain name
|
|
206
|
+
(value as Record<string, unknown>).domainName = name;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
this.domainRegistry.register(name, taggedDomain);
|
|
202
211
|
// Attach to the domain property for easy access
|
|
203
|
-
this.domain[name] =
|
|
212
|
+
this.domain[name] = taggedDomain;
|
|
204
213
|
}
|
|
205
214
|
|
|
206
215
|
/**
|
|
@@ -222,6 +231,36 @@ export class Agent<TContext = unknown> {
|
|
|
222
231
|
}
|
|
223
232
|
}
|
|
224
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Update extracted data in session with lifecycle hook support
|
|
236
|
+
* Triggers the onExtractedUpdate lifecycle hook if configured
|
|
237
|
+
* @internal
|
|
238
|
+
*/
|
|
239
|
+
private async updateExtracted<TExtracted = unknown>(
|
|
240
|
+
session: SessionState<TExtracted>,
|
|
241
|
+
extractedUpdate: Partial<TExtracted>
|
|
242
|
+
): Promise<SessionState<TExtracted>> {
|
|
243
|
+
const previousExtracted = { ...session.extracted };
|
|
244
|
+
|
|
245
|
+
// Merge new extracted data
|
|
246
|
+
let newExtracted = {
|
|
247
|
+
...session.extracted,
|
|
248
|
+
...extractedUpdate,
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// Trigger lifecycle hook if configured
|
|
252
|
+
if (this.options.hooks?.onExtractedUpdate) {
|
|
253
|
+
const updatedExtracted = (await this.options.hooks.onExtractedUpdate(
|
|
254
|
+
newExtracted,
|
|
255
|
+
previousExtracted
|
|
256
|
+
)) as Partial<TExtracted>;
|
|
257
|
+
newExtracted = updatedExtracted;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Return updated session
|
|
261
|
+
return mergeExtracted(session, newExtracted);
|
|
262
|
+
}
|
|
263
|
+
|
|
225
264
|
/**
|
|
226
265
|
* Get current context (fetches from provider if configured)
|
|
227
266
|
* @internal
|
|
@@ -236,20 +275,71 @@ export class Agent<TContext = unknown> {
|
|
|
236
275
|
return this.context;
|
|
237
276
|
}
|
|
238
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Determine the next state in a route based on extracted data
|
|
280
|
+
* @internal
|
|
281
|
+
*/
|
|
282
|
+
private getNextState<TExtracted = unknown>(
|
|
283
|
+
route: Route<TContext, TExtracted>,
|
|
284
|
+
currentState: State<TContext, TExtracted> | undefined,
|
|
285
|
+
extracted: Partial<TExtracted>
|
|
286
|
+
): State<TContext, TExtracted> {
|
|
287
|
+
// If no current state, start from initial state
|
|
288
|
+
if (!currentState) {
|
|
289
|
+
// Check if initial state should be skipped
|
|
290
|
+
if (route.initialState.shouldSkip(extracted)) {
|
|
291
|
+
return this.getNextState(route, route.initialState, extracted);
|
|
292
|
+
}
|
|
293
|
+
return route.initialState;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Get transitions from current state
|
|
297
|
+
const transitions = currentState.getTransitions();
|
|
298
|
+
|
|
299
|
+
// If no transitions, stay in current state
|
|
300
|
+
if (transitions.length === 0) {
|
|
301
|
+
return currentState;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Try to find the next state to transition to
|
|
305
|
+
for (const transition of transitions) {
|
|
306
|
+
const target = transition.getTarget();
|
|
307
|
+
if (!target) continue;
|
|
308
|
+
|
|
309
|
+
// Check if target state should be skipped
|
|
310
|
+
if (target.shouldSkip(extracted)) {
|
|
311
|
+
// Recursively find next non-skipped state
|
|
312
|
+
return this.getNextState(route, target, extracted);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Check if target state has required data
|
|
316
|
+
if (!target.hasRequiredData(extracted)) {
|
|
317
|
+
// Cannot enter this state yet, stay in current state
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Found valid next state
|
|
322
|
+
return target;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// No valid transition found, stay in current state
|
|
326
|
+
return currentState;
|
|
327
|
+
}
|
|
328
|
+
|
|
239
329
|
/**
|
|
240
330
|
* Generate a response based on history and context as a stream
|
|
241
331
|
*/
|
|
242
332
|
async *respondStream(params: {
|
|
243
333
|
history: Event[];
|
|
244
334
|
state?: StateRef;
|
|
335
|
+
session?: SessionState;
|
|
245
336
|
contextOverride?: Partial<TContext>;
|
|
246
337
|
signal?: AbortSignal;
|
|
247
338
|
}): AsyncGenerator<{
|
|
248
339
|
delta: string;
|
|
249
340
|
accumulated: string;
|
|
250
341
|
done: boolean;
|
|
251
|
-
|
|
252
|
-
state?: { id: string; description?: string } | null;
|
|
342
|
+
session?: SessionState;
|
|
253
343
|
toolCalls?: Array<{ toolName: string; arguments: Record<string, unknown> }>;
|
|
254
344
|
}> {
|
|
255
345
|
const { history, contextOverride, signal } = params;
|
|
@@ -265,184 +355,307 @@ export class Agent<TContext = unknown> {
|
|
|
265
355
|
}
|
|
266
356
|
|
|
267
357
|
// Merge context with override
|
|
268
|
-
|
|
358
|
+
const effectiveContext = {
|
|
269
359
|
...(currentContext as Record<string, unknown>),
|
|
270
360
|
...(contextOverride as Record<string, unknown>),
|
|
271
361
|
} as TContext;
|
|
272
362
|
|
|
273
|
-
//
|
|
274
|
-
|
|
275
|
-
// 1. Matched guidelines with associated tools
|
|
276
|
-
// 2. State machine transitions with toolState
|
|
277
|
-
//
|
|
278
|
-
// The AI will NEVER see these tools - they execute before message generation
|
|
279
|
-
const preparationResult = await this.preparationEngine.prepare({
|
|
280
|
-
history,
|
|
281
|
-
currentState: params.state,
|
|
282
|
-
context: effectiveContext,
|
|
283
|
-
routes: this.routes,
|
|
284
|
-
guidelines: this.guidelines,
|
|
285
|
-
maxIterations: this.options.maxEngineIterations || 1,
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
// Update context with results from tool executions
|
|
289
|
-
effectiveContext = preparationResult.finalContext;
|
|
363
|
+
// Initialize or get session
|
|
364
|
+
let session = params.session || createSession();
|
|
290
365
|
|
|
291
|
-
//
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
this.options.hooks?.onContextUpdate &&
|
|
296
|
-
previousStoredContext !== undefined
|
|
297
|
-
) {
|
|
298
|
-
await this.options.hooks.onContextUpdate(
|
|
299
|
-
this.context,
|
|
300
|
-
previousStoredContext
|
|
366
|
+
// PHASE 1: TOOL EXECUTION - Execute tools if current state has toolState
|
|
367
|
+
if (session.currentRoute && session.currentState) {
|
|
368
|
+
const currentRoute = this.routes.find(
|
|
369
|
+
(r) => r.id === session.currentRoute?.id
|
|
301
370
|
);
|
|
371
|
+
if (currentRoute) {
|
|
372
|
+
const currentState = currentRoute.getState(session.currentState.id);
|
|
373
|
+
if (currentState) {
|
|
374
|
+
const transitions = currentState.getTransitions();
|
|
375
|
+
const toolTransition = transitions.find((t) => t.spec.toolState);
|
|
376
|
+
|
|
377
|
+
if (toolTransition?.spec.toolState) {
|
|
378
|
+
const toolExecutor = new ToolExecutor<TContext, unknown>();
|
|
379
|
+
// Get allowed domains from current route for security enforcement
|
|
380
|
+
const allowedDomains = currentRoute.getDomains();
|
|
381
|
+
const result = await toolExecutor.executeTool(
|
|
382
|
+
toolTransition.spec.toolState,
|
|
383
|
+
effectiveContext,
|
|
384
|
+
this.updateContext.bind(this),
|
|
385
|
+
history,
|
|
386
|
+
session.extracted,
|
|
387
|
+
allowedDomains
|
|
388
|
+
);
|
|
389
|
+
|
|
390
|
+
// Update context with tool results
|
|
391
|
+
if (result.contextUpdate) {
|
|
392
|
+
await this.updateContext(
|
|
393
|
+
result.contextUpdate as Partial<TContext>
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Update extracted data with tool results
|
|
398
|
+
if (result.extractedUpdate) {
|
|
399
|
+
session = await this.updateExtracted(
|
|
400
|
+
session,
|
|
401
|
+
result.extractedUpdate
|
|
402
|
+
);
|
|
403
|
+
console.log(
|
|
404
|
+
`[Agent] Tool updated extracted data:`,
|
|
405
|
+
result.extractedUpdate
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
console.log(
|
|
410
|
+
`[Agent] Executed tool: ${result.toolName} (success: ${result.success})`
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
302
415
|
}
|
|
303
416
|
|
|
304
|
-
//
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
417
|
+
// PHASE 2: ROUTING - Determine which route to use
|
|
418
|
+
let selectedRoute: Route<TContext> | undefined;
|
|
419
|
+
let responseDirectives: string[] | undefined;
|
|
420
|
+
|
|
421
|
+
if (this.routes.length > 0) {
|
|
422
|
+
// Get last user message
|
|
423
|
+
const lastUserMessage = getLastMessageFromHistory(history);
|
|
424
|
+
|
|
425
|
+
// Build routing schema
|
|
426
|
+
const routingSchema = this.routingEngine.buildDynamicRoutingSchema(
|
|
427
|
+
this.routes
|
|
308
428
|
);
|
|
309
|
-
}
|
|
310
429
|
|
|
311
|
-
|
|
312
|
-
|
|
430
|
+
// Build routing prompt with session context
|
|
431
|
+
const routingPrompt = this.routingEngine.buildRoutingPrompt(
|
|
432
|
+
history,
|
|
433
|
+
this.routes,
|
|
434
|
+
lastUserMessage,
|
|
435
|
+
{
|
|
436
|
+
name: this.options.name,
|
|
437
|
+
goal: this.options.goal,
|
|
438
|
+
description: this.options.description,
|
|
439
|
+
personality: this.options.personality,
|
|
440
|
+
},
|
|
441
|
+
session // Pass session for context-aware routing
|
|
442
|
+
);
|
|
313
443
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
444
|
+
// Call AI to score routes (non-streaming for routing decision)
|
|
445
|
+
const routingResult = await this.options.ai.generateMessage<
|
|
446
|
+
TContext,
|
|
447
|
+
RoutingDecisionOutput
|
|
448
|
+
>({
|
|
449
|
+
prompt: routingPrompt,
|
|
450
|
+
history,
|
|
451
|
+
context: effectiveContext,
|
|
452
|
+
signal,
|
|
453
|
+
parameters: {
|
|
454
|
+
jsonSchema: routingSchema,
|
|
455
|
+
schemaName: "routing_output",
|
|
456
|
+
},
|
|
319
457
|
});
|
|
320
|
-
}
|
|
321
458
|
|
|
322
|
-
|
|
323
|
-
|
|
459
|
+
// Select best route from scores
|
|
460
|
+
if (routingResult.structured?.routes) {
|
|
461
|
+
const decision = this.routingEngine.decideRouteFromScores({
|
|
462
|
+
context: routingResult.structured.context,
|
|
463
|
+
routes: routingResult.structured.routes,
|
|
464
|
+
responseDirectives: routingResult.structured.responseDirectives,
|
|
465
|
+
});
|
|
466
|
+
selectedRoute = this.routes.find((r) => r.id === decision.routeId);
|
|
467
|
+
responseDirectives = routingResult.structured.responseDirectives;
|
|
324
468
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
469
|
+
if (selectedRoute) {
|
|
470
|
+
console.log(
|
|
471
|
+
`[Agent] Selected route: ${selectedRoute.title} (score: ${decision.maxScore})`
|
|
472
|
+
);
|
|
329
473
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
474
|
+
// Update session with selected route (if changed)
|
|
475
|
+
if (
|
|
476
|
+
!session.currentRoute ||
|
|
477
|
+
session.currentRoute.id !== selectedRoute.id
|
|
478
|
+
) {
|
|
479
|
+
session = enterRoute(
|
|
480
|
+
session,
|
|
481
|
+
selectedRoute.id,
|
|
482
|
+
selectedRoute.title
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
// Merge initial data if provided by the route
|
|
486
|
+
if (selectedRoute.initialData) {
|
|
487
|
+
session = mergeExtracted(session, selectedRoute.initialData);
|
|
488
|
+
console.log(
|
|
489
|
+
`[Agent] Merged initial data:`,
|
|
490
|
+
selectedRoute.initialData
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
console.log(`[Agent] Entered route: ${selectedRoute.title}`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
339
498
|
}
|
|
340
499
|
|
|
341
|
-
//
|
|
342
|
-
if (
|
|
343
|
-
|
|
344
|
-
|
|
500
|
+
// PHASE 3: RESPONSE - Stream message using selected route
|
|
501
|
+
if (selectedRoute) {
|
|
502
|
+
// Determine next state based on current extracted data
|
|
503
|
+
const currentStateRef = session.currentState;
|
|
504
|
+
const currentState = currentStateRef
|
|
505
|
+
? selectedRoute.getState(currentStateRef.id)
|
|
506
|
+
: undefined;
|
|
507
|
+
const nextState = this.getNextState(
|
|
508
|
+
selectedRoute,
|
|
509
|
+
currentState,
|
|
510
|
+
session.extracted
|
|
511
|
+
);
|
|
345
512
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
.map((obs) => ({
|
|
350
|
-
description: obs.description,
|
|
351
|
-
routes: obs.getRoutes().map((routeRef) => {
|
|
352
|
-
const route = this.routes.find((r) => r.id === routeRef.id);
|
|
353
|
-
return { title: route?.title || routeRef.id };
|
|
354
|
-
}),
|
|
355
|
-
}))
|
|
356
|
-
.filter((obs) => obs.routes.length > 0);
|
|
357
|
-
|
|
358
|
-
if (observationsWithRoutes.length > 0) {
|
|
359
|
-
promptBuilder.addObservations(observationsWithRoutes);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
513
|
+
// Update session with next state
|
|
514
|
+
session = enterState(session, nextState.id, nextState.description);
|
|
515
|
+
console.log(`[Agent] Entered state: ${nextState.id}`);
|
|
362
516
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
domains: r.getDomains(),
|
|
371
|
-
rules: r.getRules(),
|
|
372
|
-
prohibitions: r.getProhibitions(),
|
|
373
|
-
}))
|
|
517
|
+
// Get last user message
|
|
518
|
+
const lastUserMessage = getLastMessageFromHistory(history);
|
|
519
|
+
|
|
520
|
+
// Build response schema for this route (with gather fields from state)
|
|
521
|
+
const responseSchema = this.responseEngine.responseSchemaForRoute(
|
|
522
|
+
selectedRoute,
|
|
523
|
+
nextState
|
|
374
524
|
);
|
|
375
|
-
}
|
|
376
525
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
signal,
|
|
393
|
-
parameters: {
|
|
394
|
-
jsonMode: true,
|
|
395
|
-
},
|
|
396
|
-
});
|
|
526
|
+
// Build response prompt
|
|
527
|
+
const responsePrompt = this.responseEngine.buildResponsePrompt(
|
|
528
|
+
selectedRoute,
|
|
529
|
+
selectedRoute.getRules(),
|
|
530
|
+
selectedRoute.getProhibitions(),
|
|
531
|
+
responseDirectives,
|
|
532
|
+
history,
|
|
533
|
+
lastUserMessage,
|
|
534
|
+
{
|
|
535
|
+
name: this.options.name,
|
|
536
|
+
goal: this.options.goal,
|
|
537
|
+
description: this.options.description,
|
|
538
|
+
personality: this.options.personality,
|
|
539
|
+
}
|
|
540
|
+
);
|
|
397
541
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
542
|
+
// Generate message stream using AI provider
|
|
543
|
+
const stream = this.options.ai.generateMessageStream({
|
|
544
|
+
prompt: responsePrompt,
|
|
545
|
+
history,
|
|
546
|
+
context: effectiveContext,
|
|
547
|
+
signal,
|
|
548
|
+
parameters: {
|
|
549
|
+
jsonSchema: responseSchema,
|
|
550
|
+
schemaName: "response_stream_output",
|
|
551
|
+
},
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
// Stream chunks to caller
|
|
555
|
+
for await (const chunk of stream) {
|
|
556
|
+
const toolCalls:
|
|
557
|
+
| Array<{ toolName: string; arguments: Record<string, unknown> }>
|
|
558
|
+
| undefined = undefined;
|
|
559
|
+
|
|
560
|
+
// Extract gathered data on final chunk
|
|
561
|
+
if (chunk.done && chunk.structured && nextState.gatherFields) {
|
|
562
|
+
const gatheredData: Record<string, unknown> = {};
|
|
563
|
+
// The structured response includes both base fields and gathered extraction fields
|
|
564
|
+
const structuredData = chunk.structured as AgentStructuredResponse &
|
|
565
|
+
Record<string, unknown>;
|
|
566
|
+
|
|
567
|
+
for (const field of nextState.gatherFields) {
|
|
568
|
+
if (field in structuredData) {
|
|
569
|
+
gatheredData[field] = structuredData[field];
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Merge gathered data into session
|
|
574
|
+
if (Object.keys(gatheredData).length > 0) {
|
|
575
|
+
session = await this.updateExtracted(session, gatheredData);
|
|
576
|
+
console.log(`[Agent] Extracted data:`, gatheredData);
|
|
418
577
|
}
|
|
419
578
|
}
|
|
420
579
|
|
|
421
|
-
//
|
|
422
|
-
if (
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
580
|
+
// Extract any additional data from structured response on final chunk
|
|
581
|
+
if (
|
|
582
|
+
chunk.done &&
|
|
583
|
+
chunk.structured &&
|
|
584
|
+
typeof chunk.structured === "object" &&
|
|
585
|
+
"contextUpdate" in chunk.structured
|
|
586
|
+
) {
|
|
587
|
+
await this.updateContext(
|
|
588
|
+
(chunk.structured as { contextUpdate?: Partial<TContext> })
|
|
589
|
+
.contextUpdate as Partial<TContext>
|
|
590
|
+
);
|
|
427
591
|
}
|
|
428
592
|
|
|
429
|
-
//
|
|
593
|
+
// Auto-save session state on final chunk
|
|
430
594
|
if (
|
|
431
|
-
chunk.
|
|
432
|
-
|
|
595
|
+
chunk.done &&
|
|
596
|
+
this.persistenceManager &&
|
|
597
|
+
session.metadata?.sessionId &&
|
|
598
|
+
this.options.persistence?.autoSave !== false
|
|
433
599
|
) {
|
|
434
|
-
|
|
600
|
+
await this.persistenceManager.saveSessionState(
|
|
601
|
+
session.metadata.sessionId,
|
|
602
|
+
session
|
|
603
|
+
);
|
|
604
|
+
console.log(
|
|
605
|
+
`[Agent] Auto-saved session state to persistence: ${session.metadata.sessionId}`
|
|
606
|
+
);
|
|
435
607
|
}
|
|
608
|
+
|
|
609
|
+
yield {
|
|
610
|
+
delta: chunk.delta,
|
|
611
|
+
accumulated: chunk.accumulated,
|
|
612
|
+
done: chunk.done,
|
|
613
|
+
session, // Return updated session
|
|
614
|
+
toolCalls,
|
|
615
|
+
};
|
|
436
616
|
}
|
|
617
|
+
} else {
|
|
618
|
+
// Fallback: No routes defined, stream a simple response
|
|
619
|
+
const fallbackPrompt = new PromptComposer<TContext>()
|
|
620
|
+
.addAgentMeta({
|
|
621
|
+
name: this.options.name,
|
|
622
|
+
goal: this.options.goal,
|
|
623
|
+
description: this.options.description,
|
|
624
|
+
})
|
|
625
|
+
.addPersonality(this.options.personality)
|
|
626
|
+
.addInteractionHistory(history)
|
|
627
|
+
.addGlossary(this.terms)
|
|
628
|
+
.addGuidelines(this.guidelines)
|
|
629
|
+
.addCapabilities(this.capabilities)
|
|
630
|
+
.build();
|
|
631
|
+
|
|
632
|
+
const stream = this.options.ai.generateMessageStream({
|
|
633
|
+
prompt: fallbackPrompt,
|
|
634
|
+
history,
|
|
635
|
+
context: effectiveContext,
|
|
636
|
+
signal,
|
|
637
|
+
parameters: {
|
|
638
|
+
jsonSchema: {
|
|
639
|
+
type: "object",
|
|
640
|
+
properties: {
|
|
641
|
+
message: { type: "string" },
|
|
642
|
+
},
|
|
643
|
+
required: ["message"],
|
|
644
|
+
additionalProperties: false,
|
|
645
|
+
},
|
|
646
|
+
schemaName: "fallback_stream_response",
|
|
647
|
+
},
|
|
648
|
+
});
|
|
437
649
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
650
|
+
for await (const chunk of stream) {
|
|
651
|
+
yield {
|
|
652
|
+
delta: chunk.delta,
|
|
653
|
+
accumulated: chunk.accumulated,
|
|
654
|
+
done: chunk.done,
|
|
655
|
+
session, // Return updated session
|
|
656
|
+
toolCalls: undefined,
|
|
657
|
+
};
|
|
658
|
+
}
|
|
446
659
|
}
|
|
447
660
|
}
|
|
448
661
|
|
|
@@ -452,12 +665,12 @@ export class Agent<TContext = unknown> {
|
|
|
452
665
|
async respond(params: {
|
|
453
666
|
history: Event[];
|
|
454
667
|
state?: StateRef;
|
|
668
|
+
session?: SessionState;
|
|
455
669
|
contextOverride?: Partial<TContext>;
|
|
456
670
|
signal?: AbortSignal;
|
|
457
671
|
}): Promise<{
|
|
458
672
|
message: string;
|
|
459
|
-
|
|
460
|
-
state?: { id: string; description?: string } | null;
|
|
673
|
+
session?: SessionState;
|
|
461
674
|
toolCalls?: Array<{ toolName: string; arguments: Record<string, unknown> }>;
|
|
462
675
|
}> {
|
|
463
676
|
const { history, contextOverride, signal } = params;
|
|
@@ -473,182 +686,294 @@ export class Agent<TContext = unknown> {
|
|
|
473
686
|
}
|
|
474
687
|
|
|
475
688
|
// Merge context with override
|
|
476
|
-
|
|
689
|
+
const effectiveContext = {
|
|
477
690
|
...(currentContext as Record<string, unknown>),
|
|
478
691
|
...(contextOverride as Record<string, unknown>),
|
|
479
692
|
} as TContext;
|
|
480
693
|
|
|
481
|
-
//
|
|
482
|
-
|
|
483
|
-
// 1. Matched guidelines with associated tools
|
|
484
|
-
// 2. State machine transitions with toolState
|
|
485
|
-
//
|
|
486
|
-
// The AI will NEVER see these tools - they execute before message generation
|
|
487
|
-
const preparationResult = await this.preparationEngine.prepare({
|
|
488
|
-
history,
|
|
489
|
-
currentState: params.state,
|
|
490
|
-
context: effectiveContext,
|
|
491
|
-
routes: this.routes,
|
|
492
|
-
guidelines: this.guidelines,
|
|
493
|
-
maxIterations: this.options.maxEngineIterations || 1,
|
|
494
|
-
});
|
|
694
|
+
// Initialize or get session
|
|
695
|
+
let session = params.session || createSession();
|
|
495
696
|
|
|
496
|
-
//
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
const previousStoredContext = this.context;
|
|
501
|
-
this.context = effectiveContext;
|
|
502
|
-
if (
|
|
503
|
-
this.options.hooks?.onContextUpdate &&
|
|
504
|
-
previousStoredContext !== undefined
|
|
505
|
-
) {
|
|
506
|
-
await this.options.hooks.onContextUpdate(
|
|
507
|
-
this.context,
|
|
508
|
-
previousStoredContext
|
|
697
|
+
// PHASE 1: TOOL EXECUTION - Execute tools if current state has toolState
|
|
698
|
+
if (session.currentRoute && session.currentState) {
|
|
699
|
+
const currentRoute = this.routes.find(
|
|
700
|
+
(r) => r.id === session.currentRoute?.id
|
|
509
701
|
);
|
|
702
|
+
if (currentRoute) {
|
|
703
|
+
const currentState = currentRoute.getState(session.currentState.id);
|
|
704
|
+
if (currentState) {
|
|
705
|
+
const transitions = currentState.getTransitions();
|
|
706
|
+
const toolTransition = transitions.find((t) => t.spec.toolState);
|
|
707
|
+
|
|
708
|
+
if (toolTransition?.spec.toolState) {
|
|
709
|
+
const toolExecutor = new ToolExecutor<TContext, unknown>();
|
|
710
|
+
// Get allowed domains from current route for security enforcement
|
|
711
|
+
const allowedDomains = currentRoute.getDomains();
|
|
712
|
+
const result = await toolExecutor.executeTool(
|
|
713
|
+
toolTransition.spec.toolState,
|
|
714
|
+
effectiveContext,
|
|
715
|
+
this.updateContext.bind(this),
|
|
716
|
+
history,
|
|
717
|
+
session.extracted,
|
|
718
|
+
allowedDomains
|
|
719
|
+
);
|
|
720
|
+
|
|
721
|
+
// Update context with tool results
|
|
722
|
+
if (result.contextUpdate) {
|
|
723
|
+
await this.updateContext(
|
|
724
|
+
result.contextUpdate as Partial<TContext>
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// Update extracted data with tool results
|
|
729
|
+
if (result.extractedUpdate) {
|
|
730
|
+
session = await this.updateExtracted(
|
|
731
|
+
session,
|
|
732
|
+
result.extractedUpdate
|
|
733
|
+
);
|
|
734
|
+
console.log(
|
|
735
|
+
`[Agent] Tool updated extracted data:`,
|
|
736
|
+
result.extractedUpdate
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
console.log(
|
|
741
|
+
`[Agent] Executed tool: ${result.toolName} (success: ${result.success})`
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
510
746
|
}
|
|
511
747
|
|
|
512
|
-
//
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
748
|
+
// PHASE 2: ROUTING - Determine which route to use
|
|
749
|
+
let selectedRoute: Route<TContext> | undefined;
|
|
750
|
+
let responseDirectives: string[] | undefined;
|
|
751
|
+
|
|
752
|
+
if (this.routes.length > 0) {
|
|
753
|
+
// Get last user message
|
|
754
|
+
const lastUserMessage = getLastMessageFromHistory(history);
|
|
755
|
+
|
|
756
|
+
// Build routing schema
|
|
757
|
+
const routingSchema = this.routingEngine.buildDynamicRoutingSchema(
|
|
758
|
+
this.routes
|
|
516
759
|
);
|
|
517
|
-
}
|
|
518
760
|
|
|
519
|
-
|
|
520
|
-
|
|
761
|
+
// Build routing prompt with session context
|
|
762
|
+
const routingPrompt = this.routingEngine.buildRoutingPrompt(
|
|
763
|
+
history,
|
|
764
|
+
this.routes,
|
|
765
|
+
lastUserMessage,
|
|
766
|
+
{
|
|
767
|
+
name: this.options.name,
|
|
768
|
+
goal: this.options.goal,
|
|
769
|
+
description: this.options.description,
|
|
770
|
+
personality: this.options.personality,
|
|
771
|
+
},
|
|
772
|
+
session // Pass session for context-aware routing
|
|
773
|
+
);
|
|
521
774
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
775
|
+
// Call AI to score routes
|
|
776
|
+
const routingResult = await this.options.ai.generateMessage<
|
|
777
|
+
TContext,
|
|
778
|
+
RoutingDecisionOutput
|
|
779
|
+
>({
|
|
780
|
+
prompt: routingPrompt,
|
|
781
|
+
history,
|
|
782
|
+
context: effectiveContext,
|
|
783
|
+
signal,
|
|
784
|
+
parameters: {
|
|
785
|
+
jsonSchema: routingSchema,
|
|
786
|
+
schemaName: "routing_output",
|
|
787
|
+
},
|
|
527
788
|
});
|
|
528
|
-
}
|
|
529
789
|
|
|
530
|
-
|
|
531
|
-
|
|
790
|
+
// Select best route from scores
|
|
791
|
+
if (routingResult.structured?.routes) {
|
|
792
|
+
const decision = this.routingEngine.decideRouteFromScores({
|
|
793
|
+
context: routingResult.structured.context,
|
|
794
|
+
routes: routingResult.structured.routes,
|
|
795
|
+
responseDirectives: routingResult.structured.responseDirectives,
|
|
796
|
+
});
|
|
797
|
+
selectedRoute = this.routes.find((r) => r.id === decision.routeId);
|
|
798
|
+
responseDirectives = routingResult.structured.responseDirectives;
|
|
532
799
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
800
|
+
if (selectedRoute) {
|
|
801
|
+
console.log(
|
|
802
|
+
`[Agent] Selected route: ${selectedRoute.title} (score: ${decision.maxScore})`
|
|
803
|
+
);
|
|
537
804
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
805
|
+
// Update session with selected route (if changed)
|
|
806
|
+
if (
|
|
807
|
+
!session.currentRoute ||
|
|
808
|
+
session.currentRoute.id !== selectedRoute.id
|
|
809
|
+
) {
|
|
810
|
+
session = enterRoute(
|
|
811
|
+
session,
|
|
812
|
+
selectedRoute.id,
|
|
813
|
+
selectedRoute.title
|
|
814
|
+
);
|
|
815
|
+
|
|
816
|
+
// Merge initial data if provided by the route
|
|
817
|
+
if (selectedRoute.initialData) {
|
|
818
|
+
session = mergeExtracted(session, selectedRoute.initialData);
|
|
819
|
+
console.log(
|
|
820
|
+
`[Agent] Merged initial data:`,
|
|
821
|
+
selectedRoute.initialData
|
|
822
|
+
);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
console.log(`[Agent] Entered route: ${selectedRoute.title}`);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
547
829
|
}
|
|
548
830
|
|
|
549
|
-
//
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
831
|
+
// PHASE 3: RESPONSE - Generate message using selected route
|
|
832
|
+
let message: string;
|
|
833
|
+
const toolCalls:
|
|
834
|
+
| Array<{ toolName: string; arguments: Record<string, unknown> }>
|
|
835
|
+
| undefined = undefined;
|
|
836
|
+
|
|
837
|
+
if (selectedRoute) {
|
|
838
|
+
// Determine next state based on current extracted data
|
|
839
|
+
const currentStateRef = session.currentState;
|
|
840
|
+
const currentState = currentStateRef
|
|
841
|
+
? selectedRoute.getState(currentStateRef.id)
|
|
842
|
+
: undefined;
|
|
843
|
+
const nextState = this.getNextState(
|
|
844
|
+
selectedRoute,
|
|
845
|
+
currentState,
|
|
846
|
+
session.extracted
|
|
847
|
+
);
|
|
553
848
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
.map((obs) => ({
|
|
558
|
-
description: obs.description,
|
|
559
|
-
routes: obs.getRoutes().map((routeRef) => {
|
|
560
|
-
const route = this.routes.find((r) => r.id === routeRef.id);
|
|
561
|
-
return { title: route?.title || routeRef.id };
|
|
562
|
-
}),
|
|
563
|
-
}))
|
|
564
|
-
.filter((obs) => obs.routes.length > 0);
|
|
565
|
-
|
|
566
|
-
if (observationsWithRoutes.length > 0) {
|
|
567
|
-
promptBuilder.addObservations(observationsWithRoutes);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
849
|
+
// Update session with next state
|
|
850
|
+
session = enterState(session, nextState.id, nextState.description);
|
|
851
|
+
console.log(`[Agent] Entered state: ${nextState.id}`);
|
|
570
852
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
domains: r.getDomains(),
|
|
579
|
-
rules: r.getRules(),
|
|
580
|
-
prohibitions: r.getProhibitions(),
|
|
581
|
-
}))
|
|
853
|
+
// Get last user message
|
|
854
|
+
const lastUserMessage = getLastMessageFromHistory(history);
|
|
855
|
+
|
|
856
|
+
// Build response schema for this route (with gather fields from state)
|
|
857
|
+
const responseSchema = this.responseEngine.responseSchemaForRoute(
|
|
858
|
+
selectedRoute,
|
|
859
|
+
nextState
|
|
582
860
|
);
|
|
583
|
-
}
|
|
584
861
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
signal,
|
|
601
|
-
parameters: {
|
|
602
|
-
jsonMode: true,
|
|
603
|
-
},
|
|
604
|
-
});
|
|
862
|
+
// Build response prompt
|
|
863
|
+
const responsePrompt = this.responseEngine.buildResponsePrompt(
|
|
864
|
+
selectedRoute,
|
|
865
|
+
selectedRoute.getRules(),
|
|
866
|
+
selectedRoute.getProhibitions(),
|
|
867
|
+
responseDirectives,
|
|
868
|
+
history,
|
|
869
|
+
lastUserMessage,
|
|
870
|
+
{
|
|
871
|
+
name: this.options.name,
|
|
872
|
+
goal: this.options.goal,
|
|
873
|
+
description: this.options.description,
|
|
874
|
+
personality: this.options.personality,
|
|
875
|
+
}
|
|
876
|
+
);
|
|
605
877
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
878
|
+
// Generate message using AI provider
|
|
879
|
+
const result = await this.options.ai.generateMessage({
|
|
880
|
+
prompt: responsePrompt,
|
|
881
|
+
history,
|
|
882
|
+
context: effectiveContext,
|
|
883
|
+
signal,
|
|
884
|
+
parameters: {
|
|
885
|
+
jsonSchema: responseSchema,
|
|
886
|
+
schemaName: "response_output",
|
|
887
|
+
},
|
|
888
|
+
});
|
|
613
889
|
|
|
614
|
-
|
|
615
|
-
// Extract data from structured response
|
|
616
|
-
message = result.structured.message || message;
|
|
890
|
+
message = result.structured?.message || result.message;
|
|
617
891
|
|
|
618
|
-
//
|
|
619
|
-
if (result.structured.
|
|
620
|
-
const
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
892
|
+
// Extract gathered data from response
|
|
893
|
+
if (result.structured && nextState.gatherFields) {
|
|
894
|
+
const gatheredData: Record<string, unknown> = {};
|
|
895
|
+
// The structured response includes both base fields and gathered extraction fields
|
|
896
|
+
const structuredData = result.structured as AgentStructuredResponse &
|
|
897
|
+
Record<string, unknown>;
|
|
898
|
+
|
|
899
|
+
for (const field of nextState.gatherFields) {
|
|
900
|
+
if (field in structuredData) {
|
|
901
|
+
gatheredData[field] = structuredData[field];
|
|
902
|
+
}
|
|
628
903
|
}
|
|
629
|
-
}
|
|
630
904
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
};
|
|
905
|
+
// Merge gathered data into session
|
|
906
|
+
if (Object.keys(gatheredData).length > 0) {
|
|
907
|
+
session = mergeExtracted(session, gatheredData);
|
|
908
|
+
console.log(`[Agent] Extracted data:`, gatheredData);
|
|
909
|
+
}
|
|
637
910
|
}
|
|
638
911
|
|
|
639
|
-
// Extract
|
|
912
|
+
// Extract any additional data from structured response
|
|
640
913
|
if (
|
|
641
|
-
result.structured
|
|
642
|
-
result.structured
|
|
914
|
+
result.structured &&
|
|
915
|
+
typeof result.structured === "object" &&
|
|
916
|
+
"contextUpdate" in result.structured
|
|
643
917
|
) {
|
|
644
|
-
|
|
918
|
+
await this.updateContext(
|
|
919
|
+
(result.structured as { contextUpdate?: Partial<TContext> })
|
|
920
|
+
.contextUpdate as Partial<TContext>
|
|
921
|
+
);
|
|
645
922
|
}
|
|
923
|
+
} else {
|
|
924
|
+
// Fallback: No routes defined, generate a simple response
|
|
925
|
+
const fallbackPrompt = new PromptComposer<TContext>()
|
|
926
|
+
.addAgentMeta({
|
|
927
|
+
name: this.options.name,
|
|
928
|
+
goal: this.options.goal,
|
|
929
|
+
description: this.options.description,
|
|
930
|
+
})
|
|
931
|
+
.addPersonality(this.options.personality)
|
|
932
|
+
.addInteractionHistory(history)
|
|
933
|
+
.addGlossary(this.terms)
|
|
934
|
+
.addGuidelines(this.guidelines)
|
|
935
|
+
.addCapabilities(this.capabilities)
|
|
936
|
+
.build();
|
|
937
|
+
|
|
938
|
+
const result = await this.options.ai.generateMessage({
|
|
939
|
+
prompt: fallbackPrompt,
|
|
940
|
+
history,
|
|
941
|
+
context: effectiveContext,
|
|
942
|
+
signal,
|
|
943
|
+
parameters: {
|
|
944
|
+
jsonSchema: {
|
|
945
|
+
type: "object",
|
|
946
|
+
properties: {
|
|
947
|
+
message: { type: "string" },
|
|
948
|
+
},
|
|
949
|
+
required: ["message"],
|
|
950
|
+
additionalProperties: false,
|
|
951
|
+
},
|
|
952
|
+
schemaName: "fallback_response",
|
|
953
|
+
},
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
message = result.structured?.message || result.message;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// Auto-save session state to persistence if configured
|
|
960
|
+
if (
|
|
961
|
+
this.persistenceManager &&
|
|
962
|
+
session.metadata?.sessionId &&
|
|
963
|
+
this.options.persistence?.autoSave !== false
|
|
964
|
+
) {
|
|
965
|
+
await this.persistenceManager.saveSessionState(
|
|
966
|
+
session.metadata.sessionId,
|
|
967
|
+
session
|
|
968
|
+
);
|
|
969
|
+
console.log(
|
|
970
|
+
`[Agent] Auto-saved session state to persistence: ${session.metadata.sessionId}`
|
|
971
|
+
);
|
|
646
972
|
}
|
|
647
973
|
|
|
648
974
|
return {
|
|
649
975
|
message,
|
|
650
|
-
|
|
651
|
-
state: state || undefined,
|
|
976
|
+
session, // Return updated session with route/state info
|
|
652
977
|
toolCalls,
|
|
653
978
|
};
|
|
654
979
|
}
|
|
@@ -656,7 +981,7 @@ export class Agent<TContext = unknown> {
|
|
|
656
981
|
/**
|
|
657
982
|
* Get all routes
|
|
658
983
|
*/
|
|
659
|
-
getRoutes(): Route<TContext>[] {
|
|
984
|
+
getRoutes(): Route<TContext, unknown>[] {
|
|
660
985
|
return [...this.routes];
|
|
661
986
|
}
|
|
662
987
|
|
|
@@ -681,13 +1006,6 @@ export class Agent<TContext = unknown> {
|
|
|
681
1006
|
return [...this.capabilities];
|
|
682
1007
|
}
|
|
683
1008
|
|
|
684
|
-
/**
|
|
685
|
-
* Get all observations
|
|
686
|
-
*/
|
|
687
|
-
getObservations(): Observation[] {
|
|
688
|
-
return [...this.observations];
|
|
689
|
-
}
|
|
690
|
-
|
|
691
1009
|
/**
|
|
692
1010
|
* Get the domain registry
|
|
693
1011
|
*/
|