@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
@@ -1,57 +1,340 @@
1
- # Context Management & Persistence
1
+ # Session State & Data Management
2
2
 
3
3
  ## Overview
4
4
 
5
- The `@falai/agent` framework provides flexible patterns for managing context in both **single-turn** and **multi-turn persistent** conversations. This guide explains how to handle stateful conversations that persist across requests.
5
+ The `@falai/agent` framework provides **session state management** for tracking conversation progress, extracted data, and user intent across multiple turns. This enables sophisticated data-driven conversations with intelligent state progression.
6
6
 
7
7
  ---
8
8
 
9
- ## 🤔 The Context Lifecycle Problem
9
+ ## 🎯 Session State: The Foundation
10
10
 
11
- ### Single-Turn Conversations (Simple)
11
+ Session state tracks three key aspects of a conversation:
12
+
13
+ 1. **Current Route** - Which conversation flow the user is in
14
+ 2. **Current State** - Where in the flow they currently are
15
+ 3. **Extracted Data** - Structured data collected so far
12
16
 
13
17
  ```typescript
14
- // Works great for single-turn conversations
15
- const agent = new Agent({
16
- name: "Bot",
17
- ai: provider,
18
- context: { userId: "123", preferences: {} },
18
+ import { createSession, SessionState } from "@falai/agent";
19
+
20
+ // Define your data extraction type
21
+ interface FlightData {
22
+ destination: string;
23
+ departureDate: string;
24
+ passengers: number;
25
+ cabinClass: "economy" | "business" | "first";
26
+ }
27
+
28
+ // Initialize session state
29
+ let session = createSession<FlightData>();
30
+
31
+ // Session starts empty
32
+ console.log(session.currentRoute); // undefined
33
+ console.log(session.currentState); // undefined
34
+ console.log(session.extracted); // {}
35
+
36
+ // Use in conversation
37
+ const response = await agent.respond({ history, session });
38
+
39
+ // Session updated with progress and extracted data
40
+ console.log(response.session?.currentRoute?.title); // "Book Flight"
41
+ console.log(response.session?.currentState?.id); // "ask_destination"
42
+ console.log(response.session?.extracted); // { destination: "Paris", ... }
43
+ ```
44
+
45
+ **Benefits of Session State:**
46
+
47
+ - **Always-On Routing** - Users can change their mind mid-conversation
48
+ - **Data Persistence** - Extracted data survives across turns
49
+ - **Context Awareness** - Router sees current progress and extracted data
50
+ - **State Recovery** - Resume conversations from any point
51
+
52
+ ---
53
+
54
+ ## 🔄 Session State Helpers
55
+
56
+ ### Creating and Managing Sessions
57
+
58
+ ```typescript
59
+ import {
60
+ createSession,
61
+ enterRoute,
62
+ enterState,
63
+ mergeExtracted,
64
+ type SessionState,
65
+ } from "@falai/agent";
66
+
67
+ // Create a new session
68
+ let session = createSession<FlightData>();
69
+
70
+ // Enter a route (when routing decides to switch)
71
+ session = enterRoute(session, "book_flight", "Book Flight");
72
+
73
+ // Enter a state (when progressing through the flow)
74
+ session = enterState(session, "ask_destination", "Ask where they want to fly");
75
+
76
+ // Merge extracted data (when AI extracts new information)
77
+ session = mergeExtracted(session, {
78
+ destination: "Paris",
79
+ departureDate: "2025-10-15",
80
+ passengers: 2,
19
81
  });
82
+ ```
83
+
84
+ ### Session State Structure
20
85
 
21
- const response = await agent.respond({ history });
86
+ ```typescript
87
+ interface SessionState<TExtracted = unknown> {
88
+ currentRoute?: {
89
+ id: string;
90
+ title: string;
91
+ enteredAt: Date;
92
+ };
93
+ currentState?: {
94
+ id: string;
95
+ description?: string;
96
+ enteredAt: Date;
97
+ };
98
+ extracted: Partial<TExtracted>; // Data collected so far
99
+ routeHistory: Array<{
100
+ routeId: string;
101
+ routeTitle: string;
102
+ enteredAt: Date;
103
+ exitedAt?: Date;
104
+ }>;
105
+ metadata?: Record<string, unknown>;
106
+ }
22
107
  ```
23
108
 
24
- **Works because:** Agent is used once and discarded.
109
+ ## 🔄 Enhanced Lifecycle Hooks
25
110
 
26
- ### Multi-Turn Conversations (Complex)
111
+ ### Context Hooks (Traditional Context Management)
27
112
 
28
113
  ```typescript
29
- // ❌ PROBLEM: Context updates don't persist
30
114
  const agent = new Agent({
31
- name: "Bot",
32
- ai: provider,
33
- context: { userId: "123", preferences: {} },
115
+ // ... other options
116
+ hooks: {
117
+ // Refresh context before each response
118
+ beforeRespond: async (currentContext) => {
119
+ const freshData = await loadUserData(currentContext.userId);
120
+ return { ...currentContext, ...freshData };
121
+ },
122
+
123
+ // Persist context updates
124
+ onContextUpdate: async (newContext, previousContext) => {
125
+ await saveUserData(newContext.userId, newContext);
126
+ },
127
+
128
+ // NEW: Validate and enrich extracted data
129
+ onExtractedUpdate: async (extracted, previousExtracted) => {
130
+ // Normalize passenger count
131
+ if (extracted.passengers < 1) extracted.passengers = 1;
132
+ if (extracted.passengers > 9) extracted.passengers = 9;
133
+
134
+ // Enrich with computed fields
135
+ if (extracted.destination) {
136
+ extracted.destinationCode = await lookupAirportCode(
137
+ extracted.destination
138
+ );
139
+ }
140
+
141
+ // Auto-trigger actions
142
+ if (hasAllRequiredData(extracted)) {
143
+ extracted.shouldSearchFlights = true;
144
+ }
145
+
146
+ return extracted;
147
+ },
148
+ },
34
149
  });
150
+ ```
151
+
152
+ ### Context Provider Pattern
35
153
 
36
- // Turn 1
37
- await agent.respond({ history: [message1] });
38
- // Tool modifies context in memory...
154
+ For always-fresh context from external sources:
39
155
 
40
- // Turn 2 (new request, agent recreated)
41
- const agent2 = new Agent({ context: { userId: "123", preferences: {} } });
42
- await agent2.respond({ history: [message2] });
43
- // Lost the context changes from Turn 1!
156
+ ```typescript
157
+ const agent = new Agent({
158
+ // ... other options
159
+ contextProvider: async () => {
160
+ // Load fresh context for each response
161
+ return await loadFullContextFromDatabase();
162
+ },
163
+ });
44
164
  ```
45
165
 
46
- **Problem:** When agents are recreated (e.g., across HTTP requests), context updates are lost.
166
+ ## 📊 Data Extraction Pipeline
47
167
 
48
- ---
168
+ Schema-first data extraction with intelligent state progression:
49
169
 
50
- ## 💡 Solution: Context Lifecycle Hooks
170
+ ### 1. Define Your Data Schema
171
+
172
+ ```typescript
173
+ interface FlightData {
174
+ destination: string;
175
+ destinationCode?: string; // Enriched by tools
176
+ departureDate: string;
177
+ departureDateParsed?: string; // Enriched by tools
178
+ passengers: number;
179
+ cabinClass: "economy" | "business" | "first";
180
+ shouldSearchFlights?: boolean; // Action flag
181
+ }
51
182
 
52
- The framework provides **lifecycle hooks** to integrate with your persistence layer (database, cache, etc.).
183
+ const route = agent.createRoute<FlightData>({
184
+ title: "Book Flight",
185
+ gatherSchema: {
186
+ type: "object",
187
+ properties: {
188
+ destination: { type: "string" },
189
+ departureDate: { type: "string" },
190
+ passengers: { type: "number", minimum: 1, maximum: 9 },
191
+ cabinClass: {
192
+ type: "string",
193
+ enum: ["economy", "business", "first"],
194
+ default: "economy",
195
+ },
196
+ },
197
+ required: ["destination", "departureDate", "passengers"],
198
+ },
199
+ initialData: {
200
+ cabinClass: "economy", // Pre-populate defaults
201
+ },
202
+ });
203
+ ```
204
+
205
+ ### 2. Create Smart State Machines
206
+
207
+ ```typescript
208
+ // State with code-based logic (no fuzzy LLM conditions!)
209
+ const askDestination = route.initialState.transitionTo({
210
+ chatState: "Ask where they want to fly",
211
+ gather: ["destination"],
212
+ skipIf: (extracted) => !!extracted.destination, // Skip if already have destination
213
+ });
214
+
215
+ const enrichDestination = askDestination.transitionTo({
216
+ toolState: lookupAirportCode, // Tool executes automatically
217
+ requiredData: ["destination"], // Prerequisites
218
+ });
219
+
220
+ const askDates = enrichDestination.transitionTo({
221
+ chatState: "Ask about travel dates",
222
+ gather: ["departureDate"],
223
+ skipIf: (extracted) => !!extracted.departureDate,
224
+ requiredData: ["destination"], // Must have destination first
225
+ });
226
+
227
+ const validateDate = askDates.transitionTo({
228
+ toolState: parseAndValidateDate,
229
+ requiredData: ["departureDate"],
230
+ });
231
+
232
+ const askPassengers = validateDate.transitionTo({
233
+ chatState: "How many passengers?",
234
+ gather: ["passengers"],
235
+ skipIf: (extracted) => !!extracted.passengers,
236
+ });
237
+
238
+ const searchFlights = askPassengers.transitionTo({
239
+ toolState: searchFlightAPI,
240
+ // Triggered when shouldSearchFlights flag is set by hook
241
+ });
242
+ ```
243
+
244
+ ### 3. Tools Access Extracted Data
245
+
246
+ ```typescript
247
+ const searchFlights = defineTool<Context, [], void, FlightData>(
248
+ "search_flights",
249
+ async ({ context, extracted }) => {
250
+ // Access extracted data directly (no LLM extraction needed!)
251
+ if (!extracted.destination || !extracted.departureDate) {
252
+ return { data: undefined };
253
+ }
254
+
255
+ const flights = await searchFlightAPI(
256
+ extracted.destination,
257
+ extracted.departureDate
258
+ );
259
+
260
+ return {
261
+ data: undefined,
262
+ contextUpdate: { availableFlights: flights },
263
+ extractedUpdate: {
264
+ shouldSearchFlights: false, // Clear the flag
265
+ },
266
+ };
267
+ }
268
+ );
269
+ ```
270
+
271
+ ### 4. Lifecycle Hooks for Validation & Enrichment
272
+
273
+ ```typescript
274
+ const agent = new Agent({
275
+ // ... other options
276
+ hooks: {
277
+ onExtractedUpdate: async (extracted, previous) => {
278
+ // Normalize data
279
+ if (extracted.passengers < 1) extracted.passengers = 1;
280
+ if (extracted.passengers > 9) extracted.passengers = 9;
281
+
282
+ // Enrich data
283
+ if (extracted.destination && !extracted.destinationCode) {
284
+ extracted.destinationCode = await lookupAirportCode(
285
+ extracted.destination
286
+ );
287
+ }
288
+
289
+ // Auto-trigger actions
290
+ if (hasAllRequiredData(extracted) && !extracted.shouldSearchFlights) {
291
+ extracted.shouldSearchFlights = true;
292
+ }
293
+
294
+ return extracted;
295
+ },
296
+ },
297
+ });
298
+ ```
299
+
300
+ ## 🎯 Always-On Routing with Context
301
+
302
+ Routing happens every turn with full session context:
303
+
304
+ ```typescript
305
+ // User starts booking a flight
306
+ const response1 = await agent.respond({
307
+ history: [
308
+ createMessageEvent(
309
+ EventSource.CUSTOMER,
310
+ "User",
311
+ "I want to fly to Paris tomorrow with 2 people"
312
+ ),
313
+ ],
314
+ session,
315
+ });
316
+
317
+ // User changes mind mid-conversation
318
+ const response2 = await agent.respond({
319
+ history: [
320
+ ...previousHistory,
321
+ createMessageEvent(
322
+ EventSource.CUSTOMER,
323
+ "User",
324
+ "Actually, make that Tokyo instead"
325
+ ),
326
+ ],
327
+ session: response1.session, // Router sees context and switches appropriately
328
+ });
329
+
330
+ // Router understands:
331
+ // - Current route: "Book Flight"
332
+ // - Current state: "ask_passengers"
333
+ // - Extracted data: { destination: "Paris", departureDate: "tomorrow", passengers: 2 }
334
+ // - User intent: "Tokyo instead" → switches to new destination
335
+ ```
53
336
 
54
- ### Pattern 1: `beforeRespond` + `onContextUpdate` (Recommended)
337
+ ## 📋 Multi-Turn Conversation Patterns
55
338
 
56
339
  ```typescript
57
340
  import { Agent, type ContextLifecycleHooks } from "@falai/agent";
@@ -244,7 +244,7 @@ Follow [Conventional Commits](https://www.conventionalcommits.org/):
244
244
 
245
245
  ```bash
246
246
  git add .
247
- git commit -m "feat: add disambiguation support for routes"
247
+ git commit -m "feat: add support for routes"
248
248
  ```
249
249
 
250
250
  **Commit types:**
package/docs/DOMAINS.md CHANGED
@@ -662,6 +662,67 @@ route.initialState.transitionTo({
662
662
  });
663
663
  ```
664
664
 
665
+ ## How Enforcement Works (Under the Hood)
666
+
667
+ For developers who want to understand the implementation:
668
+
669
+ ### Automatic Tool Tagging
670
+
671
+ When you register tools via `agent.addDomain()`, each tool is automatically tagged with its domain name:
672
+
673
+ ```typescript
674
+ // When you do this:
675
+ agent.addDomain("payment", {
676
+ processPayment: defineTool(/* ... */),
677
+ });
678
+
679
+ // The framework automatically adds:
680
+ // processPayment.domainName = "payment"
681
+ ```
682
+
683
+ ### Runtime Enforcement
684
+
685
+ When a tool tries to execute, `ToolExecutor` checks:
686
+
687
+ 1. **Get allowed domains** from the current route
688
+ 2. **Check tool's domain** against the allowed list
689
+ 3. **Block execution** if domain not allowed
690
+ 4. **Throw security error** with clear message
691
+
692
+ ```typescript
693
+ // ToolExecutor enforcement code:
694
+ if (allowedDomains !== undefined && tool.domainName) {
695
+ if (!allowedDomains.includes(tool.domainName)) {
696
+ throw new Error(
697
+ `Domain security violation: Tool "${tool.name}" belongs to domain "${tool.domainName}" ` +
698
+ `which is not allowed in this route. Allowed domains: [${allowedDomains.join(
699
+ ", "
700
+ )}]`
701
+ );
702
+ }
703
+ }
704
+ ```
705
+
706
+ ### What Gets Enforced
707
+
708
+ ✅ **Tools in state machine transitions** (`toolState`)
709
+ ✅ **Multiple domain access** (route can allow several domains)
710
+ ✅ **Empty array enforcement** (`domains: []` blocks all tools)
711
+ ✅ **Undefined = all allowed** (backward compatible)
712
+
713
+ ### Type Safety
714
+
715
+ The domain system is fully type-safe:
716
+
717
+ ```typescript
718
+ interface ToolRef {
719
+ id: string;
720
+ name: string;
721
+ handler: Function;
722
+ domainName?: string; // Added by addDomain()
723
+ }
724
+ ```
725
+
665
726
  ## See Also
666
727
 
667
728
  - [Architecture Guide](./ARCHITECTURE.md) - Core design principles