@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.
Files changed (285) hide show
  1. package/README.md +21 -74
  2. package/dist/cjs/core/Agent.d.ts +22 -29
  3. package/dist/cjs/core/Agent.d.ts.map +1 -1
  4. package/dist/cjs/core/Agent.js +464 -291
  5. package/dist/cjs/core/Agent.js.map +1 -1
  6. package/dist/cjs/core/Events.d.ts +10 -1
  7. package/dist/cjs/core/Events.d.ts.map +1 -1
  8. package/dist/cjs/core/Events.js +3 -2
  9. package/dist/cjs/core/Events.js.map +1 -1
  10. package/dist/cjs/core/PersistenceManager.d.ts +19 -0
  11. package/dist/cjs/core/PersistenceManager.d.ts.map +1 -1
  12. package/dist/cjs/core/PersistenceManager.js +57 -0
  13. package/dist/cjs/core/PersistenceManager.js.map +1 -1
  14. package/dist/cjs/core/PromptComposer.d.ts +24 -0
  15. package/dist/cjs/core/PromptComposer.d.ts.map +1 -0
  16. package/dist/cjs/core/PromptComposer.js +127 -0
  17. package/dist/cjs/core/PromptComposer.js.map +1 -0
  18. package/dist/cjs/core/ResponseEngine.d.ts +19 -0
  19. package/dist/cjs/core/ResponseEngine.d.ts.map +1 -0
  20. package/dist/cjs/core/ResponseEngine.js +51 -0
  21. package/dist/cjs/core/ResponseEngine.js.map +1 -0
  22. package/dist/cjs/core/Route.d.ts +18 -12
  23. package/dist/cjs/core/Route.d.ts.map +1 -1
  24. package/dist/cjs/core/Route.js +15 -9
  25. package/dist/cjs/core/Route.js.map +1 -1
  26. package/dist/cjs/core/RoutingEngine.d.ts +38 -0
  27. package/dist/cjs/core/RoutingEngine.d.ts.map +1 -0
  28. package/dist/cjs/core/RoutingEngine.js +110 -0
  29. package/dist/cjs/core/RoutingEngine.js.map +1 -0
  30. package/dist/cjs/core/State.d.ts +15 -4
  31. package/dist/cjs/core/State.d.ts.map +1 -1
  32. package/dist/cjs/core/State.js +21 -2
  33. package/dist/cjs/core/State.js.map +1 -1
  34. package/dist/cjs/core/ToolExecutor.d.ts +29 -0
  35. package/dist/cjs/core/ToolExecutor.d.ts.map +1 -0
  36. package/dist/cjs/core/ToolExecutor.js +73 -0
  37. package/dist/cjs/core/ToolExecutor.js.map +1 -0
  38. package/dist/cjs/core/Transition.d.ts +5 -5
  39. package/dist/cjs/core/Transition.d.ts.map +1 -1
  40. package/dist/cjs/core/Transition.js.map +1 -1
  41. package/dist/cjs/index.d.ts +6 -8
  42. package/dist/cjs/index.d.ts.map +1 -1
  43. package/dist/cjs/index.js +8 -10
  44. package/dist/cjs/index.js.map +1 -1
  45. package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -1
  46. package/dist/cjs/providers/AnthropicProvider.js +10 -13
  47. package/dist/cjs/providers/AnthropicProvider.js.map +1 -1
  48. package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
  49. package/dist/cjs/providers/GeminiProvider.js +12 -8
  50. package/dist/cjs/providers/GeminiProvider.js.map +1 -1
  51. package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
  52. package/dist/cjs/providers/OpenAIProvider.js +10 -53
  53. package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
  54. package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
  55. package/dist/cjs/providers/OpenRouterProvider.js +10 -53
  56. package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
  57. package/dist/cjs/types/agent.d.ts +13 -12
  58. package/dist/cjs/types/agent.d.ts.map +1 -1
  59. package/dist/cjs/types/ai.d.ts +8 -2
  60. package/dist/cjs/types/ai.d.ts.map +1 -1
  61. package/dist/cjs/types/history.d.ts +8 -0
  62. package/dist/cjs/types/history.d.ts.map +1 -1
  63. package/dist/cjs/types/index.d.ts +0 -3
  64. package/dist/cjs/types/index.d.ts.map +1 -1
  65. package/dist/cjs/types/index.js +1 -3
  66. package/dist/cjs/types/index.js.map +1 -1
  67. package/dist/cjs/types/route.d.ts +39 -4
  68. package/dist/cjs/types/route.d.ts.map +1 -1
  69. package/dist/cjs/types/routing.d.ts +16 -0
  70. package/dist/cjs/types/routing.d.ts.map +1 -0
  71. package/dist/cjs/types/routing.js +3 -0
  72. package/dist/cjs/types/routing.js.map +1 -0
  73. package/dist/cjs/types/schema.d.ts +22 -0
  74. package/dist/cjs/types/schema.d.ts.map +1 -0
  75. package/dist/cjs/types/schema.js +3 -0
  76. package/dist/cjs/types/schema.js.map +1 -0
  77. package/dist/cjs/types/session.d.ts +72 -0
  78. package/dist/cjs/types/session.d.ts.map +1 -0
  79. package/dist/cjs/types/session.js +140 -0
  80. package/dist/cjs/types/session.js.map +1 -0
  81. package/dist/cjs/types/tool.d.ts +11 -5
  82. package/dist/cjs/types/tool.d.ts.map +1 -1
  83. package/dist/cjs/utils/id.d.ts +0 -5
  84. package/dist/cjs/utils/id.d.ts.map +1 -1
  85. package/dist/cjs/utils/id.js +0 -10
  86. package/dist/cjs/utils/id.js.map +1 -1
  87. package/dist/cjs/utils/schema.d.ts +17 -0
  88. package/dist/cjs/utils/schema.d.ts.map +1 -0
  89. package/dist/cjs/utils/schema.js +32 -0
  90. package/dist/cjs/utils/schema.js.map +1 -0
  91. package/dist/core/Agent.d.ts +22 -29
  92. package/dist/core/Agent.d.ts.map +1 -1
  93. package/dist/core/Agent.js +464 -291
  94. package/dist/core/Agent.js.map +1 -1
  95. package/dist/core/Events.d.ts +10 -1
  96. package/dist/core/Events.d.ts.map +1 -1
  97. package/dist/core/Events.js +3 -2
  98. package/dist/core/Events.js.map +1 -1
  99. package/dist/core/PersistenceManager.d.ts +19 -0
  100. package/dist/core/PersistenceManager.d.ts.map +1 -1
  101. package/dist/core/PersistenceManager.js +57 -0
  102. package/dist/core/PersistenceManager.js.map +1 -1
  103. package/dist/core/PromptComposer.d.ts +24 -0
  104. package/dist/core/PromptComposer.d.ts.map +1 -0
  105. package/dist/core/PromptComposer.js +123 -0
  106. package/dist/core/PromptComposer.js.map +1 -0
  107. package/dist/core/ResponseEngine.d.ts +19 -0
  108. package/dist/core/ResponseEngine.d.ts.map +1 -0
  109. package/dist/core/ResponseEngine.js +47 -0
  110. package/dist/core/ResponseEngine.js.map +1 -0
  111. package/dist/core/Route.d.ts +18 -12
  112. package/dist/core/Route.d.ts.map +1 -1
  113. package/dist/core/Route.js +15 -9
  114. package/dist/core/Route.js.map +1 -1
  115. package/dist/core/RoutingEngine.d.ts +38 -0
  116. package/dist/core/RoutingEngine.d.ts.map +1 -0
  117. package/dist/core/RoutingEngine.js +106 -0
  118. package/dist/core/RoutingEngine.js.map +1 -0
  119. package/dist/core/State.d.ts +15 -4
  120. package/dist/core/State.d.ts.map +1 -1
  121. package/dist/core/State.js +21 -2
  122. package/dist/core/State.js.map +1 -1
  123. package/dist/core/ToolExecutor.d.ts +29 -0
  124. package/dist/core/ToolExecutor.d.ts.map +1 -0
  125. package/dist/core/ToolExecutor.js +69 -0
  126. package/dist/core/ToolExecutor.js.map +1 -0
  127. package/dist/core/Transition.d.ts +5 -5
  128. package/dist/core/Transition.d.ts.map +1 -1
  129. package/dist/core/Transition.js.map +1 -1
  130. package/dist/index.d.ts +6 -8
  131. package/dist/index.d.ts.map +1 -1
  132. package/dist/index.js +3 -5
  133. package/dist/index.js.map +1 -1
  134. package/dist/providers/AnthropicProvider.d.ts.map +1 -1
  135. package/dist/providers/AnthropicProvider.js +10 -13
  136. package/dist/providers/AnthropicProvider.js.map +1 -1
  137. package/dist/providers/GeminiProvider.d.ts.map +1 -1
  138. package/dist/providers/GeminiProvider.js +12 -8
  139. package/dist/providers/GeminiProvider.js.map +1 -1
  140. package/dist/providers/OpenAIProvider.d.ts.map +1 -1
  141. package/dist/providers/OpenAIProvider.js +10 -53
  142. package/dist/providers/OpenAIProvider.js.map +1 -1
  143. package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
  144. package/dist/providers/OpenRouterProvider.js +10 -53
  145. package/dist/providers/OpenRouterProvider.js.map +1 -1
  146. package/dist/types/agent.d.ts +13 -12
  147. package/dist/types/agent.d.ts.map +1 -1
  148. package/dist/types/ai.d.ts +8 -2
  149. package/dist/types/ai.d.ts.map +1 -1
  150. package/dist/types/history.d.ts +8 -0
  151. package/dist/types/history.d.ts.map +1 -1
  152. package/dist/types/index.d.ts +0 -3
  153. package/dist/types/index.d.ts.map +1 -1
  154. package/dist/types/index.js +0 -1
  155. package/dist/types/index.js.map +1 -1
  156. package/dist/types/route.d.ts +39 -4
  157. package/dist/types/route.d.ts.map +1 -1
  158. package/dist/types/routing.d.ts +16 -0
  159. package/dist/types/routing.d.ts.map +1 -0
  160. package/dist/types/routing.js +2 -0
  161. package/dist/types/routing.js.map +1 -0
  162. package/dist/types/schema.d.ts +22 -0
  163. package/dist/types/schema.d.ts.map +1 -0
  164. package/dist/types/schema.js +2 -0
  165. package/dist/types/schema.js.map +1 -0
  166. package/dist/types/session.d.ts +72 -0
  167. package/dist/types/session.d.ts.map +1 -0
  168. package/dist/types/session.js +132 -0
  169. package/dist/types/session.js.map +1 -0
  170. package/dist/types/tool.d.ts +11 -5
  171. package/dist/types/tool.d.ts.map +1 -1
  172. package/dist/utils/id.d.ts +0 -5
  173. package/dist/utils/id.d.ts.map +1 -1
  174. package/dist/utils/id.js +0 -9
  175. package/dist/utils/id.js.map +1 -1
  176. package/dist/utils/schema.d.ts +17 -0
  177. package/dist/utils/schema.d.ts.map +1 -0
  178. package/dist/utils/schema.js +27 -0
  179. package/dist/utils/schema.js.map +1 -0
  180. package/docs/ADAPTERS.md +83 -3
  181. package/docs/API_REFERENCE.md +95 -104
  182. package/docs/ARCHITECTURE.md +284 -286
  183. package/docs/CONSTRUCTOR_OPTIONS.md +192 -135
  184. package/docs/CONTEXT_MANAGEMENT.md +311 -28
  185. package/docs/CONTRIBUTING.md +1 -1
  186. package/docs/DOMAINS.md +61 -0
  187. package/docs/GETTING_STARTED.md +177 -88
  188. package/docs/PERSISTENCE.md +170 -23
  189. package/docs/README.md +7 -10
  190. package/examples/business-onboarding.ts +21 -9
  191. package/examples/company-qna-agent.ts +508 -0
  192. package/examples/declarative-agent.ts +143 -26
  193. package/examples/domain-scoping.ts +31 -10
  194. package/examples/extracted-data-modification.ts +415 -0
  195. package/examples/healthcare-agent.ts +194 -90
  196. package/examples/openai-agent.ts +67 -25
  197. package/examples/opensearch-persistence.ts +455 -151
  198. package/examples/persistent-onboarding.ts +162 -96
  199. package/examples/prisma-persistence.ts +371 -125
  200. package/examples/redis-persistence.ts +393 -23
  201. package/examples/rules-prohibitions.ts +32 -11
  202. package/examples/streaming-agent.ts +61 -13
  203. package/examples/travel-agent.ts +266 -133
  204. package/package.json +1 -1
  205. package/src/core/Agent.ts +674 -356
  206. package/src/core/Events.ts +12 -2
  207. package/src/core/PersistenceManager.ts +83 -0
  208. package/src/core/PromptComposer.ts +143 -0
  209. package/src/core/ResponseEngine.ts +82 -0
  210. package/src/core/Route.ts +32 -17
  211. package/src/core/RoutingEngine.ts +165 -0
  212. package/src/core/State.ts +55 -15
  213. package/src/core/ToolExecutor.ts +117 -0
  214. package/src/core/Transition.ts +5 -5
  215. package/src/index.ts +12 -21
  216. package/src/providers/AnthropicProvider.ts +10 -13
  217. package/src/providers/GeminiProvider.ts +12 -8
  218. package/src/providers/OpenAIProvider.ts +10 -56
  219. package/src/providers/OpenRouterProvider.ts +10 -56
  220. package/src/types/agent.ts +16 -13
  221. package/src/types/ai.ts +6 -2
  222. package/src/types/history.ts +8 -0
  223. package/src/types/index.ts +0 -11
  224. package/src/types/route.ts +41 -5
  225. package/src/types/routing.ts +18 -0
  226. package/src/types/schema.ts +23 -0
  227. package/src/types/session.ts +207 -0
  228. package/src/types/tool.ts +29 -7
  229. package/src/utils/id.ts +0 -10
  230. package/src/utils/schema.ts +32 -0
  231. package/dist/cjs/core/ConditionEvaluator.d.ts +0 -72
  232. package/dist/cjs/core/ConditionEvaluator.d.ts.map +0 -1
  233. package/dist/cjs/core/ConditionEvaluator.js +0 -272
  234. package/dist/cjs/core/ConditionEvaluator.js.map +0 -1
  235. package/dist/cjs/core/Observation.d.ts +0 -24
  236. package/dist/cjs/core/Observation.d.ts.map +0 -1
  237. package/dist/cjs/core/Observation.js +0 -39
  238. package/dist/cjs/core/Observation.js.map +0 -1
  239. package/dist/cjs/core/PreparationEngine.d.ts +0 -116
  240. package/dist/cjs/core/PreparationEngine.d.ts.map +0 -1
  241. package/dist/cjs/core/PreparationEngine.js +0 -353
  242. package/dist/cjs/core/PreparationEngine.js.map +0 -1
  243. package/dist/cjs/core/PromptBuilder.d.ts +0 -136
  244. package/dist/cjs/core/PromptBuilder.d.ts.map +0 -1
  245. package/dist/cjs/core/PromptBuilder.js +0 -421
  246. package/dist/cjs/core/PromptBuilder.js.map +0 -1
  247. package/dist/cjs/types/observation.d.ts +0 -27
  248. package/dist/cjs/types/observation.d.ts.map +0 -1
  249. package/dist/cjs/types/observation.js +0 -6
  250. package/dist/cjs/types/observation.js.map +0 -1
  251. package/dist/cjs/types/prompt.d.ts +0 -46
  252. package/dist/cjs/types/prompt.d.ts.map +0 -1
  253. package/dist/cjs/types/prompt.js +0 -19
  254. package/dist/cjs/types/prompt.js.map +0 -1
  255. package/dist/core/ConditionEvaluator.d.ts +0 -72
  256. package/dist/core/ConditionEvaluator.d.ts.map +0 -1
  257. package/dist/core/ConditionEvaluator.js +0 -268
  258. package/dist/core/ConditionEvaluator.js.map +0 -1
  259. package/dist/core/Observation.d.ts +0 -24
  260. package/dist/core/Observation.d.ts.map +0 -1
  261. package/dist/core/Observation.js +0 -35
  262. package/dist/core/Observation.js.map +0 -1
  263. package/dist/core/PreparationEngine.d.ts +0 -116
  264. package/dist/core/PreparationEngine.d.ts.map +0 -1
  265. package/dist/core/PreparationEngine.js +0 -349
  266. package/dist/core/PreparationEngine.js.map +0 -1
  267. package/dist/core/PromptBuilder.d.ts +0 -136
  268. package/dist/core/PromptBuilder.d.ts.map +0 -1
  269. package/dist/core/PromptBuilder.js +0 -417
  270. package/dist/core/PromptBuilder.js.map +0 -1
  271. package/dist/types/observation.d.ts +0 -27
  272. package/dist/types/observation.d.ts.map +0 -1
  273. package/dist/types/observation.js +0 -5
  274. package/dist/types/observation.js.map +0 -1
  275. package/dist/types/prompt.d.ts +0 -46
  276. package/dist/types/prompt.d.ts.map +0 -1
  277. package/dist/types/prompt.js +0 -16
  278. package/dist/types/prompt.js.map +0 -1
  279. package/docs/STRUCTURE.md +0 -58
  280. package/src/core/ConditionEvaluator.ts +0 -381
  281. package/src/core/Observation.ts +0 -47
  282. package/src/core/PreparationEngine.ts +0 -561
  283. package/src/core/PromptBuilder.ts +0 -617
  284. package/src/types/observation.ts +0 -29
  285. package/src/types/prompt.ts +0 -49
@@ -1,12 +1,27 @@
1
1
  /**
2
2
  * Core Agent implementation
3
3
  */
4
+ import { createSession, enterRoute, enterState, mergeExtracted, } from "../types/session";
5
+ import { EventKind } from "../types/history";
6
+ import { PromptComposer } from "./PromptComposer";
4
7
  import { Route } from "./Route";
5
8
  import { DomainRegistry } from "./DomainRegistry";
6
- import { PromptBuilder } from "./PromptBuilder";
7
- import { Observation } from "./Observation";
8
9
  import { PersistenceManager } from "./PersistenceManager";
9
- import { PreparationEngine } from "./PreparationEngine";
10
+ import { RoutingEngine } from "./RoutingEngine";
11
+ import { ResponseEngine } from "./ResponseEngine";
12
+ import { ToolExecutor } from "./ToolExecutor";
13
+ /**
14
+ * Helper to extract last message from history
15
+ */
16
+ function getLastMessageFromHistory(history) {
17
+ for (let i = history.length - 1; i >= 0; i--) {
18
+ const event = history[i];
19
+ if (event.kind === EventKind.MESSAGE) {
20
+ return event.data.message;
21
+ }
22
+ }
23
+ return "";
24
+ }
10
25
  /**
11
26
  * Main Agent class with generic context support
12
27
  */
@@ -16,28 +31,26 @@ export class Agent {
16
31
  this.terms = [];
17
32
  this.guidelines = [];
18
33
  this.capabilities = [];
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
35
  this.routes = [];
20
- this.observations = [];
21
36
  this.domainRegistry = new DomainRegistry();
22
37
  /**
23
38
  * Dynamic domain property - populated via addDomain
24
39
  */
25
40
  this.domain = {};
26
- // Initialize with default values
27
- if (!this.options.maxEngineIterations) {
28
- this.options.maxEngineIterations = 1;
29
- }
30
41
  // Validate context configuration
31
42
  if (options.context !== undefined && options.contextProvider) {
32
43
  throw new Error("Cannot provide both 'context' and 'contextProvider'. Choose one.");
33
44
  }
34
45
  // Initialize context if provided
35
46
  this.context = options.context;
36
- // Initialize preparation engine with AI provider and optional parallelism controls
37
- this.preparationEngine = new PreparationEngine(options.ai, {
38
- maxParallelLlmCalls: options.preparation?.maxParallelLlmCalls,
39
- maxParallelTools: options.preparation?.maxParallelTools,
47
+ // Initialize routing and response engines
48
+ this.routingEngine = new RoutingEngine({
49
+ maxCandidates: 5,
50
+ allowRouteSwitch: true,
51
+ switchThreshold: 70,
40
52
  });
53
+ this.responseEngine = new ResponseEngine();
41
54
  // Initialize persistence if configured
42
55
  if (options.persistence) {
43
56
  this.persistenceManager = new PersistenceManager(options.persistence);
@@ -67,23 +80,6 @@ export class Agent {
67
80
  this.createRoute(routeOptions);
68
81
  });
69
82
  }
70
- if (options.observations) {
71
- options.observations.forEach((obsOptions) => {
72
- const obs = this.createObservation(obsOptions.description);
73
- // If route refs were provided, resolve and disambiguate
74
- if (obsOptions.routeRefs && obsOptions.routeRefs.length > 0) {
75
- const resolvedRoutes = obsOptions.routeRefs
76
- .map((ref) => {
77
- // Try to find route by ID or title
78
- return this.routes.find((r) => r.id === ref || r.title === ref);
79
- })
80
- .filter((r) => r !== undefined);
81
- if (resolvedRoutes.length > 0) {
82
- obs.disambiguate(resolvedRoutes);
83
- }
84
- }
85
- });
86
- }
87
83
  }
88
84
  /**
89
85
  * Get agent name
@@ -105,6 +101,7 @@ export class Agent {
105
101
  }
106
102
  /**
107
103
  * Create a new route (journey)
104
+ * @template TExtracted - Type of data extracted throughout the route
108
105
  */
109
106
  createRoute(options) {
110
107
  const route = new Route(options);
@@ -141,21 +138,28 @@ export class Agent {
141
138
  this.capabilities.push(capabilityWithId);
142
139
  return this;
143
140
  }
144
- /**
145
- * Create an observation for disambiguation
146
- */
147
- createObservation(description) {
148
- const observation = new Observation({ description });
149
- this.observations.push(observation);
150
- return observation;
151
- }
152
141
  /**
153
142
  * Add a domain with its tools/methods
143
+ * Automatically tags all ToolRef objects with their domain name for security enforcement
154
144
  */
155
145
  addDomain(name, domainObject) {
156
- this.domainRegistry.register(name, domainObject);
146
+ // Tag all tools in this domain with the domain name for security enforcement
147
+ const taggedDomain = { ...domainObject };
148
+ for (const key in taggedDomain) {
149
+ const value = taggedDomain[key];
150
+ // Check if value is a ToolRef (has handler, id, name properties)
151
+ if (value &&
152
+ typeof value === "object" &&
153
+ "handler" in value &&
154
+ "id" in value &&
155
+ "name" in value) {
156
+ // Tag the tool with its domain name
157
+ value.domainName = name;
158
+ }
159
+ }
160
+ this.domainRegistry.register(name, taggedDomain);
157
161
  // Attach to the domain property for easy access
158
- this.domain[name] = domainObject;
162
+ this.domain[name] = taggedDomain;
159
163
  }
160
164
  /**
161
165
  * Update the agent's context
@@ -173,6 +177,26 @@ export class Agent {
173
177
  await this.options.hooks.onContextUpdate(this.context, previousContext);
174
178
  }
175
179
  }
180
+ /**
181
+ * Update extracted data in session with lifecycle hook support
182
+ * Triggers the onExtractedUpdate lifecycle hook if configured
183
+ * @internal
184
+ */
185
+ async updateExtracted(session, extractedUpdate) {
186
+ const previousExtracted = { ...session.extracted };
187
+ // Merge new extracted data
188
+ let newExtracted = {
189
+ ...session.extracted,
190
+ ...extractedUpdate,
191
+ };
192
+ // Trigger lifecycle hook if configured
193
+ if (this.options.hooks?.onExtractedUpdate) {
194
+ const updatedExtracted = (await this.options.hooks.onExtractedUpdate(newExtracted, previousExtracted));
195
+ newExtracted = updatedExtracted;
196
+ }
197
+ // Return updated session
198
+ return mergeExtracted(session, newExtracted);
199
+ }
176
200
  /**
177
201
  * Get current context (fetches from provider if configured)
178
202
  * @internal
@@ -185,6 +209,46 @@ export class Agent {
185
209
  // Otherwise return the stored context
186
210
  return this.context;
187
211
  }
212
+ /**
213
+ * Determine the next state in a route based on extracted data
214
+ * @internal
215
+ */
216
+ getNextState(route, currentState, extracted) {
217
+ // If no current state, start from initial state
218
+ if (!currentState) {
219
+ // Check if initial state should be skipped
220
+ if (route.initialState.shouldSkip(extracted)) {
221
+ return this.getNextState(route, route.initialState, extracted);
222
+ }
223
+ return route.initialState;
224
+ }
225
+ // Get transitions from current state
226
+ const transitions = currentState.getTransitions();
227
+ // If no transitions, stay in current state
228
+ if (transitions.length === 0) {
229
+ return currentState;
230
+ }
231
+ // Try to find the next state to transition to
232
+ for (const transition of transitions) {
233
+ const target = transition.getTarget();
234
+ if (!target)
235
+ continue;
236
+ // Check if target state should be skipped
237
+ if (target.shouldSkip(extracted)) {
238
+ // Recursively find next non-skipped state
239
+ return this.getNextState(route, target, extracted);
240
+ }
241
+ // Check if target state has required data
242
+ if (!target.hasRequiredData(extracted)) {
243
+ // Cannot enter this state yet, stay in current state
244
+ continue;
245
+ }
246
+ // Found valid next state
247
+ return target;
248
+ }
249
+ // No valid transition found, stay in current state
250
+ return currentState;
251
+ }
188
252
  /**
189
253
  * Generate a response based on history and context as a stream
190
254
  */
@@ -199,145 +263,208 @@ export class Agent {
199
263
  this.context = currentContext;
200
264
  }
201
265
  // Merge context with override
202
- let effectiveContext = {
266
+ const effectiveContext = {
203
267
  ...currentContext,
204
268
  ...contextOverride,
205
269
  };
206
- // RUN PREPARATION ITERATIONS
207
- // This is where tools execute automatically based on:
208
- // 1. Matched guidelines with associated tools
209
- // 2. State machine transitions with toolState
210
- //
211
- // The AI will NEVER see these tools - they execute before message generation
212
- const preparationResult = await this.preparationEngine.prepare({
213
- history,
214
- currentState: params.state,
215
- context: effectiveContext,
216
- routes: this.routes,
217
- guidelines: this.guidelines,
218
- maxIterations: this.options.maxEngineIterations || 1,
219
- });
220
- // Update context with results from tool executions
221
- effectiveContext = preparationResult.finalContext;
222
- // Persist updated context and trigger lifecycle hook
223
- const previousStoredContext = this.context;
224
- this.context = effectiveContext;
225
- if (this.options.hooks?.onContextUpdate &&
226
- previousStoredContext !== undefined) {
227
- await this.options.hooks.onContextUpdate(this.context, previousStoredContext);
228
- }
229
- // Log tool executions for debugging
230
- if (preparationResult.toolExecutions.length > 0) {
231
- console.log(`[Agent] Preparation complete: ${preparationResult.toolExecutions.length} tools executed in ${preparationResult.iterations.length} iterations`);
270
+ // Initialize or get session
271
+ let session = params.session || createSession();
272
+ // PHASE 1: TOOL EXECUTION - Execute tools if current state has toolState
273
+ if (session.currentRoute && session.currentState) {
274
+ const currentRoute = this.routes.find((r) => r.id === session.currentRoute?.id);
275
+ if (currentRoute) {
276
+ const currentState = currentRoute.getState(session.currentState.id);
277
+ if (currentState) {
278
+ const transitions = currentState.getTransitions();
279
+ const toolTransition = transitions.find((t) => t.spec.toolState);
280
+ if (toolTransition?.spec.toolState) {
281
+ const toolExecutor = new ToolExecutor();
282
+ // Get allowed domains from current route for security enforcement
283
+ const allowedDomains = currentRoute.getDomains();
284
+ const result = await toolExecutor.executeTool(toolTransition.spec.toolState, effectiveContext, this.updateContext.bind(this), history, session.extracted, allowedDomains);
285
+ // Update context with tool results
286
+ if (result.contextUpdate) {
287
+ await this.updateContext(result.contextUpdate);
288
+ }
289
+ // Update extracted data with tool results
290
+ if (result.extractedUpdate) {
291
+ session = await this.updateExtracted(session, result.extractedUpdate);
292
+ console.log(`[Agent] Tool updated extracted data:`, result.extractedUpdate);
293
+ }
294
+ console.log(`[Agent] Executed tool: ${result.toolName} (success: ${result.success})`);
295
+ }
296
+ }
297
+ }
232
298
  }
233
- // Build prompt (same as respond method)
234
- const promptBuilder = new PromptBuilder();
235
- // Add agent identity
236
- if (this.options.description) {
237
- promptBuilder.addAgentIdentity({
299
+ // PHASE 2: ROUTING - Determine which route to use
300
+ let selectedRoute;
301
+ let responseDirectives;
302
+ if (this.routes.length > 0) {
303
+ // Get last user message
304
+ const lastUserMessage = getLastMessageFromHistory(history);
305
+ // Build routing schema
306
+ const routingSchema = this.routingEngine.buildDynamicRoutingSchema(this.routes);
307
+ // Build routing prompt with session context
308
+ const routingPrompt = this.routingEngine.buildRoutingPrompt(history, this.routes, lastUserMessage, {
238
309
  name: this.options.name,
310
+ goal: this.options.goal,
239
311
  description: this.options.description,
312
+ personality: this.options.personality,
313
+ }, session // Pass session for context-aware routing
314
+ );
315
+ // Call AI to score routes (non-streaming for routing decision)
316
+ const routingResult = await this.options.ai.generateMessage({
317
+ prompt: routingPrompt,
318
+ history,
319
+ context: effectiveContext,
320
+ signal,
321
+ parameters: {
322
+ jsonSchema: routingSchema,
323
+ schemaName: "routing_output",
324
+ },
240
325
  });
241
- }
242
- // Add interaction history
243
- promptBuilder.addInteractionHistoryForMessageGeneration(history);
244
- // Add glossary
245
- if (this.terms.length > 0) {
246
- promptBuilder.addGlossary(this.terms);
247
- }
248
- // Add guidelines (convert to GuidelineMatch format, filter enabled only)
249
- const enabledGuidelines = this.guidelines.filter((g) => g.enabled !== false);
250
- if (enabledGuidelines.length > 0) {
251
- const guidelineMatches = enabledGuidelines.map((g) => ({
252
- guideline: g,
253
- }));
254
- promptBuilder.addGuidelinesForMessageGeneration(guidelineMatches);
255
- }
256
- // Add capabilities
257
- if (this.capabilities.length > 0) {
258
- promptBuilder.addCapabilitiesForMessageGeneration(this.capabilities);
259
- }
260
- // Add observations
261
- if (this.observations.length > 0) {
262
- const observationsWithRoutes = this.observations
263
- .map((obs) => ({
264
- description: obs.description,
265
- routes: obs.getRoutes().map((routeRef) => {
266
- const route = this.routes.find((r) => r.id === routeRef.id);
267
- return { title: route?.title || routeRef.id };
268
- }),
269
- }))
270
- .filter((obs) => obs.routes.length > 0);
271
- if (observationsWithRoutes.length > 0) {
272
- promptBuilder.addObservations(observationsWithRoutes);
326
+ // Select best route from scores
327
+ if (routingResult.structured?.routes) {
328
+ const decision = this.routingEngine.decideRouteFromScores({
329
+ context: routingResult.structured.context,
330
+ routes: routingResult.structured.routes,
331
+ responseDirectives: routingResult.structured.responseDirectives,
332
+ });
333
+ selectedRoute = this.routes.find((r) => r.id === decision.routeId);
334
+ responseDirectives = routingResult.structured.responseDirectives;
335
+ if (selectedRoute) {
336
+ console.log(`[Agent] Selected route: ${selectedRoute.title} (score: ${decision.maxScore})`);
337
+ // Update session with selected route (if changed)
338
+ if (!session.currentRoute ||
339
+ session.currentRoute.id !== selectedRoute.id) {
340
+ session = enterRoute(session, selectedRoute.id, selectedRoute.title);
341
+ // Merge initial data if provided by the route
342
+ if (selectedRoute.initialData) {
343
+ session = mergeExtracted(session, selectedRoute.initialData);
344
+ console.log(`[Agent] Merged initial data:`, selectedRoute.initialData);
345
+ }
346
+ console.log(`[Agent] Entered route: ${selectedRoute.title}`);
347
+ }
348
+ }
273
349
  }
274
350
  }
275
- // Add active routes with their rules and prohibitions
276
- if (this.routes.length > 0) {
277
- promptBuilder.addActiveRoutes(this.routes.map((r) => ({
278
- title: r.title,
279
- description: r.description,
280
- conditions: r.conditions,
281
- domains: r.getDomains(),
282
- rules: r.getRules(),
283
- prohibitions: r.getProhibitions(),
284
- })));
285
- }
286
- // NOTE: Domains/tools are NOT added to the prompt.
287
- // Tools execute automatically based on state transitions and guideline matching,
288
- // NOT based on AI decisions. The AI should never see available tools.
289
- // Add JSON response schema instructions
290
- promptBuilder.addJsonResponseSchema();
291
- // Build final prompt
292
- const prompt = promptBuilder.build();
293
- // Generate message stream using AI provider with JSON mode enabled
294
- const stream = this.options.ai.generateMessageStream({
295
- prompt,
296
- history,
297
- context: effectiveContext,
298
- signal,
299
- parameters: {
300
- jsonMode: true,
301
- },
302
- });
303
- // Stream chunks to caller
304
- for await (const chunk of stream) {
305
- // Extract route and state from structured response on final chunk
306
- let route = null;
307
- let state = null;
308
- let toolCalls;
309
- if (chunk.done && chunk.structured) {
310
- // Find route by title
311
- if (chunk.structured.route) {
312
- const foundRoute = this.routes.find((r) => r.title === chunk.structured?.route);
313
- if (foundRoute) {
314
- route = {
315
- id: foundRoute.id,
316
- title: foundRoute.title,
317
- };
351
+ // PHASE 3: RESPONSE - Stream message using selected route
352
+ if (selectedRoute) {
353
+ // Determine next state based on current extracted data
354
+ const currentStateRef = session.currentState;
355
+ const currentState = currentStateRef
356
+ ? selectedRoute.getState(currentStateRef.id)
357
+ : undefined;
358
+ const nextState = this.getNextState(selectedRoute, currentState, session.extracted);
359
+ // Update session with next state
360
+ session = enterState(session, nextState.id, nextState.description);
361
+ console.log(`[Agent] Entered state: ${nextState.id}`);
362
+ // Get last user message
363
+ const lastUserMessage = getLastMessageFromHistory(history);
364
+ // Build response schema for this route (with gather fields from state)
365
+ const responseSchema = this.responseEngine.responseSchemaForRoute(selectedRoute, nextState);
366
+ // Build response prompt
367
+ const responsePrompt = this.responseEngine.buildResponsePrompt(selectedRoute, selectedRoute.getRules(), selectedRoute.getProhibitions(), responseDirectives, history, lastUserMessage, {
368
+ name: this.options.name,
369
+ goal: this.options.goal,
370
+ description: this.options.description,
371
+ personality: this.options.personality,
372
+ });
373
+ // Generate message stream using AI provider
374
+ const stream = this.options.ai.generateMessageStream({
375
+ prompt: responsePrompt,
376
+ history,
377
+ context: effectiveContext,
378
+ signal,
379
+ parameters: {
380
+ jsonSchema: responseSchema,
381
+ schemaName: "response_stream_output",
382
+ },
383
+ });
384
+ // Stream chunks to caller
385
+ for await (const chunk of stream) {
386
+ const toolCalls = undefined;
387
+ // Extract gathered data on final chunk
388
+ if (chunk.done && chunk.structured && nextState.gatherFields) {
389
+ const gatheredData = {};
390
+ // The structured response includes both base fields and gathered extraction fields
391
+ const structuredData = chunk.structured;
392
+ for (const field of nextState.gatherFields) {
393
+ if (field in structuredData) {
394
+ gatheredData[field] = structuredData[field];
395
+ }
396
+ }
397
+ // Merge gathered data into session
398
+ if (Object.keys(gatheredData).length > 0) {
399
+ session = await this.updateExtracted(session, gatheredData);
400
+ console.log(`[Agent] Extracted data:`, gatheredData);
318
401
  }
319
402
  }
320
- // Create state reference if provided
321
- if (chunk.structured.state) {
322
- state = {
323
- id: "dynamic_state",
324
- description: chunk.structured.state,
325
- };
403
+ // Extract any additional data from structured response on final chunk
404
+ if (chunk.done &&
405
+ chunk.structured &&
406
+ typeof chunk.structured === "object" &&
407
+ "contextUpdate" in chunk.structured) {
408
+ await this.updateContext(chunk.structured
409
+ .contextUpdate);
326
410
  }
327
- // Extract tool calls
328
- if (chunk.structured.toolCalls &&
329
- chunk.structured.toolCalls.length > 0) {
330
- toolCalls = chunk.structured.toolCalls;
411
+ // Auto-save session state on final chunk
412
+ if (chunk.done &&
413
+ this.persistenceManager &&
414
+ session.metadata?.sessionId &&
415
+ this.options.persistence?.autoSave !== false) {
416
+ await this.persistenceManager.saveSessionState(session.metadata.sessionId, session);
417
+ console.log(`[Agent] Auto-saved session state to persistence: ${session.metadata.sessionId}`);
331
418
  }
419
+ yield {
420
+ delta: chunk.delta,
421
+ accumulated: chunk.accumulated,
422
+ done: chunk.done,
423
+ session, // Return updated session
424
+ toolCalls,
425
+ };
426
+ }
427
+ }
428
+ else {
429
+ // Fallback: No routes defined, stream a simple response
430
+ const fallbackPrompt = new PromptComposer()
431
+ .addAgentMeta({
432
+ name: this.options.name,
433
+ goal: this.options.goal,
434
+ description: this.options.description,
435
+ })
436
+ .addPersonality(this.options.personality)
437
+ .addInteractionHistory(history)
438
+ .addGlossary(this.terms)
439
+ .addGuidelines(this.guidelines)
440
+ .addCapabilities(this.capabilities)
441
+ .build();
442
+ const stream = this.options.ai.generateMessageStream({
443
+ prompt: fallbackPrompt,
444
+ history,
445
+ context: effectiveContext,
446
+ signal,
447
+ parameters: {
448
+ jsonSchema: {
449
+ type: "object",
450
+ properties: {
451
+ message: { type: "string" },
452
+ },
453
+ required: ["message"],
454
+ additionalProperties: false,
455
+ },
456
+ schemaName: "fallback_stream_response",
457
+ },
458
+ });
459
+ for await (const chunk of stream) {
460
+ yield {
461
+ delta: chunk.delta,
462
+ accumulated: chunk.accumulated,
463
+ done: chunk.done,
464
+ session, // Return updated session
465
+ toolCalls: undefined,
466
+ };
332
467
  }
333
- yield {
334
- delta: chunk.delta,
335
- accumulated: chunk.accumulated,
336
- done: chunk.done,
337
- route: route || undefined,
338
- state: state || undefined,
339
- toolCalls,
340
- };
341
468
  }
342
469
  }
343
470
  /**
@@ -354,142 +481,194 @@ export class Agent {
354
481
  this.context = currentContext;
355
482
  }
356
483
  // Merge context with override
357
- let effectiveContext = {
484
+ const effectiveContext = {
358
485
  ...currentContext,
359
486
  ...contextOverride,
360
487
  };
361
- // RUN PREPARATION ITERATIONS
362
- // This is where tools execute automatically based on:
363
- // 1. Matched guidelines with associated tools
364
- // 2. State machine transitions with toolState
365
- //
366
- // The AI will NEVER see these tools - they execute before message generation
367
- const preparationResult = await this.preparationEngine.prepare({
368
- history,
369
- currentState: params.state,
370
- context: effectiveContext,
371
- routes: this.routes,
372
- guidelines: this.guidelines,
373
- maxIterations: this.options.maxEngineIterations || 1,
374
- });
375
- // Update context with results from tool executions
376
- effectiveContext = preparationResult.finalContext;
377
- // Persist updated context and trigger lifecycle hook
378
- const previousStoredContext = this.context;
379
- this.context = effectiveContext;
380
- if (this.options.hooks?.onContextUpdate &&
381
- previousStoredContext !== undefined) {
382
- await this.options.hooks.onContextUpdate(this.context, previousStoredContext);
383
- }
384
- // Log tool executions for debugging
385
- if (preparationResult.toolExecutions.length > 0) {
386
- console.log(`[Agent] Preparation complete: ${preparationResult.toolExecutions.length} tools executed in ${preparationResult.iterations.length} iterations`);
488
+ // Initialize or get session
489
+ let session = params.session || createSession();
490
+ // PHASE 1: TOOL EXECUTION - Execute tools if current state has toolState
491
+ if (session.currentRoute && session.currentState) {
492
+ const currentRoute = this.routes.find((r) => r.id === session.currentRoute?.id);
493
+ if (currentRoute) {
494
+ const currentState = currentRoute.getState(session.currentState.id);
495
+ if (currentState) {
496
+ const transitions = currentState.getTransitions();
497
+ const toolTransition = transitions.find((t) => t.spec.toolState);
498
+ if (toolTransition?.spec.toolState) {
499
+ const toolExecutor = new ToolExecutor();
500
+ // Get allowed domains from current route for security enforcement
501
+ const allowedDomains = currentRoute.getDomains();
502
+ const result = await toolExecutor.executeTool(toolTransition.spec.toolState, effectiveContext, this.updateContext.bind(this), history, session.extracted, allowedDomains);
503
+ // Update context with tool results
504
+ if (result.contextUpdate) {
505
+ await this.updateContext(result.contextUpdate);
506
+ }
507
+ // Update extracted data with tool results
508
+ if (result.extractedUpdate) {
509
+ session = await this.updateExtracted(session, result.extractedUpdate);
510
+ console.log(`[Agent] Tool updated extracted data:`, result.extractedUpdate);
511
+ }
512
+ console.log(`[Agent] Executed tool: ${result.toolName} (success: ${result.success})`);
513
+ }
514
+ }
515
+ }
387
516
  }
388
- // Build prompt
389
- const promptBuilder = new PromptBuilder();
390
- // Add agent identity
391
- if (this.options.description) {
392
- promptBuilder.addAgentIdentity({
517
+ // PHASE 2: ROUTING - Determine which route to use
518
+ let selectedRoute;
519
+ let responseDirectives;
520
+ if (this.routes.length > 0) {
521
+ // Get last user message
522
+ const lastUserMessage = getLastMessageFromHistory(history);
523
+ // Build routing schema
524
+ const routingSchema = this.routingEngine.buildDynamicRoutingSchema(this.routes);
525
+ // Build routing prompt with session context
526
+ const routingPrompt = this.routingEngine.buildRoutingPrompt(history, this.routes, lastUserMessage, {
393
527
  name: this.options.name,
528
+ goal: this.options.goal,
394
529
  description: this.options.description,
530
+ personality: this.options.personality,
531
+ }, session // Pass session for context-aware routing
532
+ );
533
+ // Call AI to score routes
534
+ const routingResult = await this.options.ai.generateMessage({
535
+ prompt: routingPrompt,
536
+ history,
537
+ context: effectiveContext,
538
+ signal,
539
+ parameters: {
540
+ jsonSchema: routingSchema,
541
+ schemaName: "routing_output",
542
+ },
395
543
  });
396
- }
397
- // Add interaction history
398
- promptBuilder.addInteractionHistoryForMessageGeneration(history);
399
- // Add glossary
400
- if (this.terms.length > 0) {
401
- promptBuilder.addGlossary(this.terms);
402
- }
403
- // Add guidelines (convert to GuidelineMatch format, filter enabled only)
404
- const enabledGuidelines = this.guidelines.filter((g) => g.enabled !== false);
405
- if (enabledGuidelines.length > 0) {
406
- const guidelineMatches = enabledGuidelines.map((g) => ({
407
- guideline: g,
408
- }));
409
- promptBuilder.addGuidelinesForMessageGeneration(guidelineMatches);
410
- }
411
- // Add capabilities
412
- if (this.capabilities.length > 0) {
413
- promptBuilder.addCapabilitiesForMessageGeneration(this.capabilities);
414
- }
415
- // Add observations
416
- if (this.observations.length > 0) {
417
- const observationsWithRoutes = this.observations
418
- .map((obs) => ({
419
- description: obs.description,
420
- routes: obs.getRoutes().map((routeRef) => {
421
- const route = this.routes.find((r) => r.id === routeRef.id);
422
- return { title: route?.title || routeRef.id };
423
- }),
424
- }))
425
- .filter((obs) => obs.routes.length > 0);
426
- if (observationsWithRoutes.length > 0) {
427
- promptBuilder.addObservations(observationsWithRoutes);
544
+ // Select best route from scores
545
+ if (routingResult.structured?.routes) {
546
+ const decision = this.routingEngine.decideRouteFromScores({
547
+ context: routingResult.structured.context,
548
+ routes: routingResult.structured.routes,
549
+ responseDirectives: routingResult.structured.responseDirectives,
550
+ });
551
+ selectedRoute = this.routes.find((r) => r.id === decision.routeId);
552
+ responseDirectives = routingResult.structured.responseDirectives;
553
+ if (selectedRoute) {
554
+ console.log(`[Agent] Selected route: ${selectedRoute.title} (score: ${decision.maxScore})`);
555
+ // Update session with selected route (if changed)
556
+ if (!session.currentRoute ||
557
+ session.currentRoute.id !== selectedRoute.id) {
558
+ session = enterRoute(session, selectedRoute.id, selectedRoute.title);
559
+ // Merge initial data if provided by the route
560
+ if (selectedRoute.initialData) {
561
+ session = mergeExtracted(session, selectedRoute.initialData);
562
+ console.log(`[Agent] Merged initial data:`, selectedRoute.initialData);
563
+ }
564
+ console.log(`[Agent] Entered route: ${selectedRoute.title}`);
565
+ }
566
+ }
428
567
  }
429
568
  }
430
- // Add active routes with their rules and prohibitions
431
- if (this.routes.length > 0) {
432
- promptBuilder.addActiveRoutes(this.routes.map((r) => ({
433
- title: r.title,
434
- description: r.description,
435
- conditions: r.conditions,
436
- domains: r.getDomains(),
437
- rules: r.getRules(),
438
- prohibitions: r.getProhibitions(),
439
- })));
440
- }
441
- // NOTE: Domains/tools are NOT added to the prompt.
442
- // Tools execute automatically based on state transitions and guideline matching,
443
- // NOT based on AI decisions. The AI should never see available tools.
444
- // Add JSON response schema instructions
445
- promptBuilder.addJsonResponseSchema();
446
- // Build final prompt
447
- const prompt = promptBuilder.build();
448
- // Generate message using AI provider with JSON mode enabled
449
- const result = await this.options.ai.generateMessage({
450
- prompt,
451
- history,
452
- context: effectiveContext,
453
- signal,
454
- parameters: {
455
- jsonMode: true,
456
- },
457
- });
458
- // Parse structured response
459
- let message = result.message;
460
- let route = null;
461
- let state = null;
462
- let toolCalls;
463
- if (result.structured) {
464
- // Extract data from structured response
465
- message = result.structured.message || message;
466
- // Find route by title
467
- if (result.structured.route) {
468
- const foundRoute = this.routes.find((r) => r.title === result.structured?.route);
469
- if (foundRoute) {
470
- route = {
471
- id: foundRoute.id,
472
- title: foundRoute.title,
473
- };
569
+ // PHASE 3: RESPONSE - Generate message using selected route
570
+ let message;
571
+ const toolCalls = undefined;
572
+ if (selectedRoute) {
573
+ // Determine next state based on current extracted data
574
+ const currentStateRef = session.currentState;
575
+ const currentState = currentStateRef
576
+ ? selectedRoute.getState(currentStateRef.id)
577
+ : undefined;
578
+ const nextState = this.getNextState(selectedRoute, currentState, session.extracted);
579
+ // Update session with next state
580
+ session = enterState(session, nextState.id, nextState.description);
581
+ console.log(`[Agent] Entered state: ${nextState.id}`);
582
+ // Get last user message
583
+ const lastUserMessage = getLastMessageFromHistory(history);
584
+ // Build response schema for this route (with gather fields from state)
585
+ const responseSchema = this.responseEngine.responseSchemaForRoute(selectedRoute, nextState);
586
+ // Build response prompt
587
+ const responsePrompt = this.responseEngine.buildResponsePrompt(selectedRoute, selectedRoute.getRules(), selectedRoute.getProhibitions(), responseDirectives, history, lastUserMessage, {
588
+ name: this.options.name,
589
+ goal: this.options.goal,
590
+ description: this.options.description,
591
+ personality: this.options.personality,
592
+ });
593
+ // Generate message using AI provider
594
+ const result = await this.options.ai.generateMessage({
595
+ prompt: responsePrompt,
596
+ history,
597
+ context: effectiveContext,
598
+ signal,
599
+ parameters: {
600
+ jsonSchema: responseSchema,
601
+ schemaName: "response_output",
602
+ },
603
+ });
604
+ message = result.structured?.message || result.message;
605
+ // Extract gathered data from response
606
+ if (result.structured && nextState.gatherFields) {
607
+ const gatheredData = {};
608
+ // The structured response includes both base fields and gathered extraction fields
609
+ const structuredData = result.structured;
610
+ for (const field of nextState.gatherFields) {
611
+ if (field in structuredData) {
612
+ gatheredData[field] = structuredData[field];
613
+ }
614
+ }
615
+ // Merge gathered data into session
616
+ if (Object.keys(gatheredData).length > 0) {
617
+ session = mergeExtracted(session, gatheredData);
618
+ console.log(`[Agent] Extracted data:`, gatheredData);
474
619
  }
475
620
  }
476
- // Create state reference if provided
477
- if (result.structured.state) {
478
- state = {
479
- id: "dynamic_state",
480
- description: result.structured.state,
481
- };
482
- }
483
- // Extract tool calls
484
- if (result.structured.toolCalls &&
485
- result.structured.toolCalls.length > 0) {
486
- toolCalls = result.structured.toolCalls;
621
+ // Extract any additional data from structured response
622
+ if (result.structured &&
623
+ typeof result.structured === "object" &&
624
+ "contextUpdate" in result.structured) {
625
+ await this.updateContext(result.structured
626
+ .contextUpdate);
487
627
  }
488
628
  }
629
+ else {
630
+ // Fallback: No routes defined, generate a simple response
631
+ const fallbackPrompt = new PromptComposer()
632
+ .addAgentMeta({
633
+ name: this.options.name,
634
+ goal: this.options.goal,
635
+ description: this.options.description,
636
+ })
637
+ .addPersonality(this.options.personality)
638
+ .addInteractionHistory(history)
639
+ .addGlossary(this.terms)
640
+ .addGuidelines(this.guidelines)
641
+ .addCapabilities(this.capabilities)
642
+ .build();
643
+ const result = await this.options.ai.generateMessage({
644
+ prompt: fallbackPrompt,
645
+ history,
646
+ context: effectiveContext,
647
+ signal,
648
+ parameters: {
649
+ jsonSchema: {
650
+ type: "object",
651
+ properties: {
652
+ message: { type: "string" },
653
+ },
654
+ required: ["message"],
655
+ additionalProperties: false,
656
+ },
657
+ schemaName: "fallback_response",
658
+ },
659
+ });
660
+ message = result.structured?.message || result.message;
661
+ }
662
+ // Auto-save session state to persistence if configured
663
+ if (this.persistenceManager &&
664
+ session.metadata?.sessionId &&
665
+ this.options.persistence?.autoSave !== false) {
666
+ await this.persistenceManager.saveSessionState(session.metadata.sessionId, session);
667
+ console.log(`[Agent] Auto-saved session state to persistence: ${session.metadata.sessionId}`);
668
+ }
489
669
  return {
490
670
  message,
491
- route: route || undefined,
492
- state: state || undefined,
671
+ session, // Return updated session with route/state info
493
672
  toolCalls,
494
673
  };
495
674
  }
@@ -517,12 +696,6 @@ export class Agent {
517
696
  getCapabilities() {
518
697
  return [...this.capabilities];
519
698
  }
520
- /**
521
- * Get all observations
522
- */
523
- getObservations() {
524
- return [...this.observations];
525
- }
526
699
  /**
527
700
  * Get the domain registry
528
701
  */