@falai/agent 0.4.0 → 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 +465 -275
  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 -9
  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 +465 -275
  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 -9
  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 +679 -332
  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 -10
  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 -105
  240. package/dist/cjs/core/PreparationEngine.d.ts.map +0 -1
  241. package/dist/cjs/core/PreparationEngine.js +0 -320
  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 -105
  264. package/dist/core/PreparationEngine.d.ts.map +0 -1
  265. package/dist/core/PreparationEngine.js +0 -316
  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 -500
  283. package/src/core/PromptBuilder.ts +0 -617
  284. package/src/types/observation.ts +0 -29
  285. package/src/types/prompt.ts +0 -49
@@ -0,0 +1,415 @@
1
+ /**
2
+ * Example: Modifying Extracted Data with Tools and Hooks
3
+ *
4
+ * This demonstrates:
5
+ * 1. Schema-first data extraction with JSON Schema
6
+ * 2. Tools that modify extracted data (validation, enrichment)
7
+ * 3. Lifecycle hooks for data validation and business logic
8
+ * 4. Data-driven state flows with code-based logic (skipIf, requiredData)
9
+ * 5. Three-phase pipeline: PREPARATION → ROUTING → RESPONSE
10
+ */
11
+
12
+ import { Agent, createSession, EventSource, createMessageEvent } from "../src";
13
+ import type { Event } from "../src/types";
14
+ import type { ToolRef } from "../src/types/tool";
15
+
16
+ // ==============================================================================
17
+ // CONTEXT & EXTRACTED DATA TYPES
18
+ // ==============================================================================
19
+
20
+ interface FlightBookingContext {
21
+ availableFlights?: Array<{
22
+ flightNumber: string;
23
+ price: number;
24
+ departure: string;
25
+ arrival: string;
26
+ }>;
27
+ airportCodes?: Record<string, string>;
28
+ }
29
+
30
+ interface FlightData {
31
+ destination: string;
32
+ destinationCode?: string; // Enriched by tool
33
+ departureDate: string;
34
+ departureDateParsed?: string; // Enriched by tool
35
+ passengers: number;
36
+ cabinClass: "economy" | "business" | "first";
37
+ shouldSearchFlights?: boolean; // Trigger flag
38
+ }
39
+
40
+ // ==============================================================================
41
+ // TOOLS: Data Enrichment (PREPARATION Phase)
42
+ // ==============================================================================
43
+
44
+ // Tool 1: Convert city names to airport codes
45
+ const enrichDestinationTool: ToolRef<
46
+ FlightBookingContext,
47
+ [],
48
+ void,
49
+ FlightData
50
+ > = {
51
+ id: "enrich_destination",
52
+ name: "Enrich Destination",
53
+ description: "Convert city names to IATA airport codes",
54
+ handler: async (context) => {
55
+ const { extracted } = context;
56
+ const destination = (extracted as Partial<FlightData>)?.destination;
57
+
58
+ if (!destination) {
59
+ return { data: undefined };
60
+ }
61
+
62
+ // Simulate airport code lookup
63
+ const airportCodes: Record<string, string> = {
64
+ Paris: "CDG",
65
+ London: "LHR",
66
+ "New York": "JFK",
67
+ Tokyo: "NRT",
68
+ "Los Angeles": "LAX",
69
+ };
70
+
71
+ const destinationCode = airportCodes[destination];
72
+
73
+ console.log(`[Tool] Enriched: ${destination} → ${destinationCode}`);
74
+
75
+ return {
76
+ data: undefined,
77
+ extractedUpdate: {
78
+ destinationCode,
79
+ } as Partial<FlightData>,
80
+ };
81
+ },
82
+ };
83
+
84
+ // Tool 2: Parse and validate dates
85
+ const validateDateTool: ToolRef<FlightBookingContext, [], void, FlightData> = {
86
+ id: "validate_date",
87
+ name: "Validate Date",
88
+ description:
89
+ "Parse relative dates (today, tomorrow) to ISO format and validate",
90
+ handler: async (context) => {
91
+ const { extracted } = context;
92
+ const departureDate = (extracted as Partial<FlightData>)?.departureDate;
93
+
94
+ if (!departureDate) {
95
+ return { data: undefined };
96
+ }
97
+
98
+ let parsedDate: string;
99
+ const today = new Date();
100
+
101
+ // Parse relative dates
102
+ if (departureDate.toLowerCase() === "today") {
103
+ parsedDate = today.toISOString().split("T")[0];
104
+ } else if (departureDate.toLowerCase() === "tomorrow") {
105
+ const tomorrow = new Date(today);
106
+ tomorrow.setDate(tomorrow.getDate() + 1);
107
+ parsedDate = tomorrow.toISOString().split("T")[0];
108
+ } else if (departureDate.toLowerCase().startsWith("next ")) {
109
+ // Handle "next week", "next month", etc.
110
+ const nextWeek = new Date(today);
111
+ nextWeek.setDate(nextWeek.getDate() + 7);
112
+ parsedDate = nextWeek.toISOString().split("T")[0];
113
+ } else {
114
+ // Assume it's already in a proper format
115
+ parsedDate = departureDate;
116
+ }
117
+
118
+ // Validate that date isn't in the past
119
+ const parsedDateObj = new Date(parsedDate);
120
+ if (parsedDateObj < today) {
121
+ console.warn(`[Tool] Warning: Date ${parsedDate} is in the past`);
122
+ // You could throw an error here or set a flag
123
+ }
124
+
125
+ console.log(`[Tool] Parsed date: ${departureDate} → ${parsedDate}`);
126
+
127
+ return {
128
+ data: undefined,
129
+ extractedUpdate: {
130
+ departureDateParsed: parsedDate,
131
+ } as Partial<FlightData>,
132
+ };
133
+ },
134
+ };
135
+
136
+ // Tool 3: Search for flights (triggered by flag)
137
+ const searchFlightsTool: ToolRef<FlightBookingContext, [], void, FlightData> = {
138
+ id: "search_flights",
139
+ name: "Search Flights",
140
+ description: "Search for available flights based on gathered data",
141
+ handler: async (context) => {
142
+ const { extracted } = context;
143
+ const flightData = extracted as Partial<FlightData>;
144
+
145
+ if (!flightData?.destinationCode || !flightData?.departureDateParsed) {
146
+ console.log("[Tool] Cannot search flights: missing required data");
147
+ return { data: undefined };
148
+ }
149
+
150
+ // Simulate flight search API call
151
+ const flights = [
152
+ {
153
+ flightNumber: "UA123",
154
+ price: 450,
155
+ departure: "08:00",
156
+ arrival: "14:30",
157
+ },
158
+ {
159
+ flightNumber: "DL456",
160
+ price: 520,
161
+ departure: "10:30",
162
+ arrival: "16:45",
163
+ },
164
+ ];
165
+
166
+ console.log(
167
+ `[Tool] Found ${flights.length} flights to ${flightData.destinationCode}`
168
+ );
169
+
170
+ return {
171
+ data: undefined,
172
+ contextUpdate: {
173
+ availableFlights: flights,
174
+ },
175
+ extractedUpdate: {
176
+ shouldSearchFlights: false, // Clear the flag to prevent re-execution
177
+ } as Partial<FlightData>,
178
+ };
179
+ },
180
+ };
181
+
182
+ // ==============================================================================
183
+ // LIFECYCLE HOOKS: Data Validation & Business Logic (RESPONSE Phase)
184
+ // ==============================================================================
185
+
186
+ async function onExtractedUpdate(
187
+ extracted: Record<string, unknown>,
188
+ previousExtracted: Record<string, unknown>
189
+ ): Promise<Record<string, unknown>> {
190
+ console.log("[Hook] onExtractedUpdate called");
191
+ console.log(" Previous:", previousExtracted);
192
+ console.log(" New:", extracted);
193
+
194
+ // Example: Validate passenger count
195
+ const passengers = extracted.passengers as number | undefined;
196
+ if (typeof passengers === "number") {
197
+ if (passengers < 1) {
198
+ console.warn("[Hook] Invalid passengers count, setting to 1");
199
+ extracted.passengers = 1;
200
+ }
201
+ if (passengers > 9) {
202
+ console.warn("[Hook] Too many passengers, capping at 9");
203
+ extracted.passengers = 9;
204
+ }
205
+ }
206
+
207
+ // Example: Normalize cabin class
208
+ const cabinClass = extracted.cabinClass as string | undefined;
209
+ if (typeof cabinClass === "string") {
210
+ extracted.cabinClass = cabinClass.toLowerCase();
211
+ }
212
+
213
+ // Example: Auto-trigger flight search when we have enough data
214
+ const hasDestination = extracted.destinationCode || extracted.destination;
215
+ const hasDate = extracted.departureDateParsed || extracted.departureDate;
216
+ const hasPassengers = typeof passengers === "number";
217
+
218
+ if (
219
+ hasDestination &&
220
+ hasDate &&
221
+ hasPassengers &&
222
+ !extracted.shouldSearchFlights
223
+ ) {
224
+ console.log("[Hook] All required data gathered, triggering flight search");
225
+ extracted.shouldSearchFlights = true;
226
+ }
227
+
228
+ return extracted;
229
+ }
230
+
231
+ // ==============================================================================
232
+ // AGENT SETUP
233
+ // ==============================================================================
234
+
235
+ const agent = new Agent<FlightBookingContext>({
236
+ name: "Flight Booking Agent",
237
+ goal: "Help users book flights efficiently",
238
+ description: "I help you find and book flights",
239
+ ai: null as any, // Replace with actual AI provider
240
+ context: {},
241
+ hooks: {
242
+ onExtractedUpdate, // Validation & enrichment hook
243
+ },
244
+ });
245
+
246
+ // Define route with data extraction
247
+ const bookingRoute = agent.createRoute<FlightData>({
248
+ title: "Book Flight",
249
+ description: "Help user book a flight",
250
+ conditions: [
251
+ "User wants to book a flight",
252
+ "User mentions flying, traveling, or booking",
253
+ ],
254
+ gatherSchema: {
255
+ type: "object",
256
+ properties: {
257
+ destination: {
258
+ type: "string",
259
+ description: "City or airport the user wants to fly to",
260
+ },
261
+ destinationCode: {
262
+ type: "string",
263
+ description: "IATA airport code (enriched by tool)",
264
+ },
265
+ departureDate: {
266
+ type: "string",
267
+ description: "When the user wants to depart",
268
+ },
269
+ departureDateParsed: {
270
+ type: "string",
271
+ description: "Parsed ISO date (enriched by tool)",
272
+ },
273
+ passengers: {
274
+ type: "number",
275
+ minimum: 1,
276
+ maximum: 9,
277
+ },
278
+ cabinClass: {
279
+ type: "string",
280
+ enum: ["economy", "business", "first"],
281
+ default: "economy",
282
+ },
283
+ shouldSearchFlights: {
284
+ type: "boolean",
285
+ description: "Flag to trigger flight search",
286
+ },
287
+ },
288
+ required: ["destination", "departureDate", "passengers"],
289
+ },
290
+ });
291
+
292
+ // State 1: Gather destination
293
+ const gatherDestination = bookingRoute.initialState.transitionTo({
294
+ chatState: "Ask where they want to fly",
295
+ gather: ["destination"],
296
+ skipIf: (data) => !!data.destination,
297
+ });
298
+
299
+ // State 2: Enrich destination (tool execution)
300
+ const enrichDestination = gatherDestination.transitionTo({
301
+ toolState: enrichDestinationTool,
302
+ requiredData: ["destination"],
303
+ });
304
+
305
+ // State 3: Gather date
306
+ const gatherDate = enrichDestination.transitionTo({
307
+ chatState: "Ask when they want to depart",
308
+ gather: ["departureDate"],
309
+ skipIf: (data) => !!data.departureDate,
310
+ });
311
+
312
+ // State 4: Validate/parse date (tool execution)
313
+ const validateDate = gatherDate.transitionTo({
314
+ toolState: validateDateTool,
315
+ requiredData: ["departureDate"],
316
+ });
317
+
318
+ // State 5: Gather passengers
319
+ const gatherPassengers = validateDate.transitionTo({
320
+ chatState: "Ask how many passengers",
321
+ gather: ["passengers"],
322
+ skipIf: (data) => !!data.passengers,
323
+ });
324
+
325
+ // State 6: Search flights (triggered by hook setting shouldSearchFlights)
326
+ const searchFlights = gatherPassengers.transitionTo({
327
+ toolState: searchFlightsTool,
328
+ // This state is entered when shouldSearchFlights is true
329
+ // The hook automatically sets this flag when all data is gathered
330
+ });
331
+
332
+ // State 7: Present results
333
+ const presentResults = searchFlights.transitionTo({
334
+ chatState: "Present available flights to the user",
335
+ });
336
+
337
+ // ==============================================================================
338
+ // USAGE EXAMPLE: Three-Phase Pipeline Demonstration
339
+ // ==============================================================================
340
+
341
+ async function main() {
342
+ let session = createSession<FlightData>();
343
+
344
+ // Turn 1: User provides everything at once
345
+ const history: Event[] = [
346
+ createMessageEvent(
347
+ EventSource.CUSTOMER,
348
+ "User",
349
+ "I want to fly to Paris tomorrow with 2 passengers"
350
+ ),
351
+ ];
352
+
353
+ const response = await agent.respond({ history, session });
354
+
355
+ console.log("\n=== RESPONSE ===");
356
+ console.log("Message:", response.message);
357
+ console.log("Extracted:", response.session?.extracted);
358
+ console.log("Context:", agent["context"]);
359
+
360
+ /*
361
+ * Expected flow:
362
+ * 1. AI extracts: { destination: "Paris", departureDate: "tomorrow", passengers: 2 }
363
+ * 2. Hook validates passengers ✓
364
+ * 3. Hook detects all data present, sets shouldSearchFlights: true
365
+ * 4. State machine:
366
+ * - Skips gatherDestination (has destination)
367
+ * - Runs enrichDestination tool → adds destinationCode: "CDG"
368
+ * - Skips gatherDate (has departureDate)
369
+ * - Runs validateDate tool → adds departureDateParsed: "2025-10-16"
370
+ * - Skips gatherPassengers (has passengers)
371
+ * - Runs searchFlights tool → updates context with flights, clears flag
372
+ * - Enters presentResults state
373
+ * 5. AI generates response with flight options
374
+ */
375
+ }
376
+
377
+ // ==============================================================================
378
+ // KEY TAKEAWAYS: Architecture Principles Demonstrated
379
+ // ==============================================================================
380
+
381
+ /*
382
+ * 1. SCHEMA-FIRST DATA EXTRACTION
383
+ * - Define data contracts upfront with JSON Schema
384
+ * - Type-safe extraction throughout the conversation
385
+ * - Predictable data structure every time
386
+ *
387
+ * 2. CODE-BASED STATE LOGIC
388
+ * - Use TypeScript functions (skipIf, requiredData) instead of LLM conditions
389
+ * - Deterministic flow based on extracted data
390
+ * - No fuzzy LLM interpretation of state logic
391
+ *
392
+ * 3. TOOLS ENRICH EXTRACTED DATA (PREPARATION Phase)
393
+ * - Tools modify extracted data via `extractedUpdate`
394
+ * - Great for: validation, enrichment, normalization, computed fields
395
+ * - Execute before AI sees the conversation
396
+ *
397
+ * 4. LIFECYCLE HOOKS (RESPONSE Phase)
398
+ * - onExtractedUpdate runs after each data extraction
399
+ * - Cross-cutting logic: validation, business rules, auto-triggering
400
+ * - Return modified extracted data
401
+ *
402
+ * 5. THREE-PHASE PIPELINE
403
+ * - PREPARATION: Tools execute and enrich context/data
404
+ * - ROUTING: AI scores routes based on current state
405
+ * - RESPONSE: AI generates message with schema-enforced extraction
406
+ *
407
+ * 6. SESSION STATE MANAGEMENT
408
+ * - Extracted data persists across conversation turns
409
+ * - Always-on routing respects user intent changes
410
+ * - State recovery enables conversation resumption
411
+ */
412
+
413
+ if (require.main === module) {
414
+ main().catch(console.error);
415
+ }