@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
@@ -2,384 +2,382 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- `@falai/agent` is built on a **state machine-driven architecture** inspired by [Parlant/Emcie](https://github.com/emcie-co/parlant), where tools execute automatically based on conversation flow rather than AI decision-making. This creates deterministic, controllable, and predictable agent behavior.
5
+ `@falai/agent` is built on a **schema-first, data-driven architecture** that prioritizes type safety, structured data extraction, and code-based state management over fuzzy LLM conditions. This creates predictable, maintainable, and efficient conversational AI agents.
6
6
 
7
7
  ## Core Design Principles
8
8
 
9
- ### 1. 🎯 State Machine First
9
+ ### 1. 🎯 Schema-First Data Extraction
10
10
 
11
- Conversations are modeled as **explicit state machines** using the Route DSL:
11
+ Define what data to collect upfront using JSON Schema, then extract it reliably:
12
12
 
13
13
  ```typescript
14
- const route = agent.createRoute({
15
- title: "Onboarding Flow",
16
- description: "Collect user information step by step",
17
- conditions: ["User wants to sign up"],
18
- });
14
+ // Define your data contract
15
+ interface FlightData {
16
+ destination: string;
17
+ departureDate: string;
18
+ passengers: number;
19
+ cabinClass: "economy" | "business" | "first";
20
+ }
19
21
 
20
- // Define states and transitions explicitly
21
- route.initialState
22
- .transitionTo({ chatState: "Ask for name" })
23
- .transitionTo({ toolState: saveName }, "User provided name")
24
- .transitionTo({ chatState: "Ask for email" })
25
- .transitionTo({ toolState: saveEmail }, "User provided email")
26
- .transitionTo({ state: END_ROUTE });
22
+ const route = agent.createRoute<FlightData>({
23
+ title: "Book Flight",
24
+ description: "Help user book a flight",
25
+ gatherSchema: {
26
+ type: "object",
27
+ properties: {
28
+ destination: { type: "string" },
29
+ departureDate: { type: "string" },
30
+ passengers: { type: "number", minimum: 1, maximum: 9 },
31
+ cabinClass: {
32
+ type: "string",
33
+ enum: ["economy", "business", "first"],
34
+ default: "economy",
35
+ },
36
+ },
37
+ required: ["destination", "departureDate", "passengers"],
38
+ },
39
+ });
27
40
  ```
28
41
 
29
- **Why?** Explicit state machines make conversation flows:
42
+ **Why?** Schema-first extraction provides:
30
43
 
31
- - **Predictable** - You know exactly what happens when
32
- - **Testable** - Each state transition can be unit tested
33
- - **Debuggable** - Clear state progression makes issues obvious
34
- - **Maintainable** - Easy to modify flow without breaking things
44
+ - **Type Safety** - Full TypeScript types from definition to extraction
45
+ - **Reliability** - Provider-enforced schemas, not prompt-based parsing
46
+ - **Predictability** - Same data structure every time
47
+ - **Efficiency** - Extract multiple fields in one LLM call
35
48
 
36
- ### 2. 🔧 Automatic Tool Execution
49
+ ### 2. 📊 Session State Management
37
50
 
38
- Tools **execute automatically** when triggered by the state machine or guideline matching - the AI never decides when to call tools.
51
+ Track conversation progress and extracted data across turns:
39
52
 
40
53
  ```typescript
41
- // ✅ CORRECT: Tool executes automatically when state is reached
42
- askName.transitionTo({ toolState: saveName }, "User provided their name");
43
-
44
- // ✅ CORRECT: Tool executes when guideline matches
45
- agent.createGuideline({
46
- condition: "User asks about pricing",
47
- action: "Explain our pricing structure",
48
- tools: [fetchCurrentPricing], // Auto-executes when guideline matches
49
- });
50
-
51
- // WRONG: Don't expect AI to call tools
52
- agent.createCapability({
53
- title: "Save Data",
54
- tools: [saveName], // AI sees this but can't call it
55
- });
54
+ import {
55
+ createSession,
56
+ enterRoute,
57
+ enterState,
58
+ mergeExtracted,
59
+ } from "@falai/agent";
60
+
61
+ let session = createSession<FlightData>();
62
+
63
+ // Turn 1 - Extract data
64
+ const response1 = await agent.respond({ history, session });
65
+ session = response1.session!; // Updated with extracted data
66
+
67
+ // Turn 2 - User changes mind (always-on routing)
68
+ const response2 = await agent.respond({ history, session: response1.session });
69
+ session = response2.session!; // Route/state updated if user changed direction
56
70
  ```
57
71
 
58
- **Why?** Automatic execution ensures:
59
-
60
- - **Reliability** - Tools always execute when they should
61
- - **Security** - You control tool execution, not the AI
62
- - **Determinism** - Same conversation always triggers same tools
63
- - **Separation of Concerns** - AI generates messages, engine executes tools
72
+ **Why?** Session state enables:
64
73
 
65
- ### 3. 🧠 AI as Message Generator Only
74
+ - **Always-on Routing** - Users can change their mind mid-conversation
75
+ - **Context Awareness** - Router sees current progress and extracted data
76
+ - **Data Persistence** - Extracted data survives across turns
77
+ - **State Recovery** - Resume conversations from any point
66
78
 
67
- The AI's **only job** is to generate natural, conversational messages to users. It never:
79
+ ### 3. 🔧 Code-Based State Logic
68
80
 
69
- - Decides which tools to call
70
- - Controls conversation flow
71
- - Makes routing decisions
72
- - Manages state transitions
81
+ Use TypeScript functions instead of LLM conditions for deterministic flow:
73
82
 
74
83
  ```typescript
75
- // The AI only sees:
76
- // 1. Agent identity & description
77
- // 2. Conversation history
78
- // 3. Context variables (updated by tools)
79
- // 4. Guidelines to follow
80
- // 5. Available routes
81
- // 6. Glossary terms
82
-
83
- // The AI NEVER sees:
84
- // ❌ Available tools
85
- // Tool signatures
86
- // Domain definitions
87
- // ❌ Tool execution results (sees context updates instead)
84
+ // State with smart bypassing based on extracted data
85
+ const askDestination = route.initialState.transitionTo({
86
+ chatState: "Ask where they want to fly",
87
+ gather: ["destination"],
88
+ skipIf: (extracted) => !!extracted.destination, // Code-based condition!
89
+ });
90
+
91
+ const askDate = askDestination.transitionTo({
92
+ chatState: "Ask about travel dates",
93
+ gather: ["departureDate"],
94
+ skipIf: (extracted) => !!extracted.departureDate,
95
+ requiredData: ["destination"], // Prerequisites
96
+ });
88
97
  ```
89
98
 
90
- **Why?** This creates:
99
+ **Why?** Code-based logic provides:
91
100
 
92
- - **Predictable behavior** - AI can't go "off script"
93
- - **Cost efficiency** - Smaller prompts = lower API costs
94
- - **Faster responses** - Less for AI to process
95
- - **Better quality** - AI focuses on one thing: good messages
101
+ - **Predictability** - No fuzzy LLM interpretation of conditions
102
+ - **Performance** - No extra LLM calls for condition checking
103
+ - **Debugging** - Clear logic flow you can trace
104
+ - **Type Safety** - Full TypeScript support for data validation
96
105
 
97
- ### 4. 🔄 Preparation Iteration Loop
106
+ ### 4. 🛠️ Tools with Data Access
98
107
 
99
- Before generating a message, the agent runs **preparation iterations** to gather all needed data:
108
+ Tools execute with full context including extracted data:
100
109
 
101
- ```
102
- ┌─────────────────────────────────────────────────┐
103
- │ 1. New message arrives │
104
- └─────────────────┬───────────────────────────────┘
105
-
106
-
107
- ┌─────────────────────────────────────────────────┐
108
- 2. Match guidelines against context │
109
- │ - Which guidelines apply now? │
110
- - Do they have tools attached? │
111
- └─────────────────┬───────────────────────────────┘
112
-
113
-
114
- ┌─────────────────────────────────────────────────┐
115
- 3. Execute tools for matched guidelines │
116
- │ - Run tools automatically │
117
- │ - Collect results & context updates │
118
- └─────────────────┬───────────────────────────────┘
119
-
120
-
121
- ┌─────────────────────────────────────────────────┐
122
- │ 4. Execute state machine transitions │
123
- │ - If state has toolState → execute it │
124
- │ - Update context with results │
125
- └─────────────────┬───────────────────────────────┘
126
-
127
-
128
- ┌─────────┴─────────┐
129
- │ Tools executed? │
130
- └─────────┬─────────┘
131
-
132
- ┌─────────┴─────────┐
133
- │ Yes │ No │
134
- ▼ ▼
135
- ┌─────────┐ ┌────────────┐
136
- │ Loop │ │ Generate │
137
- │ again │ │ message │
138
- └─────────┘ └────────────┘
110
+ ```typescript
111
+ const searchFlights = defineTool<Context, [], void, FlightData>(
112
+ "search_flights",
113
+ async (toolContext) => {
114
+ const { extracted, context, history } = toolContext;
115
+
116
+ // Access extracted data directly
117
+ if (!extracted.destination || !extracted.departureDate) {
118
+ return { data: undefined };
119
+ }
120
+
121
+ // Enrich extracted data
122
+ return {
123
+ data: undefined,
124
+ extractedUpdate: {
125
+ destinationCode: await lookupAirportCode(extracted.destination),
126
+ departureDateParsed: parseDate(extracted.departureDate),
127
+ },
128
+ };
129
+ }
130
+ );
139
131
  ```
140
132
 
141
- **Why?** This enables:
133
+ **Why?** Tools with data access enable:
142
134
 
143
- - **Multi-step data gathering** - Tools can trigger more tools
144
- - **Context enrichment** - Each iteration adds more context
145
- - **Dynamic flows** - Tool results can change which guidelines match
146
- - **Efficiency** - One message generation with complete context
135
+ - **Data Enrichment** - Tools can validate and enhance extracted data
136
+ - **Computed Fields** - Calculate derived values from extracted data
137
+ - **Conditional Execution** - Tools decide what to do based on data state
138
+ - **Action Flags** - Tools can set flags for subsequent operations
147
139
 
148
- ### 5. 📦 Domain-Based Tool Organization
140
+ ### 5. 🎭 Always-On Routing
149
141
 
150
- Tools are organized into **domains** for better structure and access control:
142
+ Routing happens every turn, allowing users to change direction:
151
143
 
152
144
  ```typescript
153
- // Register tools by domain
154
- agent.addDomain("user", {
155
- saveProfile: async (data) => {
156
- /* ... */
157
- },
158
- updatePreferences: async (prefs) => {
159
- /* ... */
160
- },
145
+ // User starts booking a flight
146
+ const response1 = await agent.respond({
147
+ history: [
148
+ createMessageEvent(EventSource.CUSTOMER, "User", "I want to fly to Paris"),
149
+ ],
150
+ session,
161
151
  });
162
152
 
163
- agent.addDomain("payment", {
164
- processPayment: async (amount) => {
165
- /* ... */
166
- },
167
- refund: async (id) => {
168
- /* ... */
169
- },
153
+ // User changes mind mid-conversation
154
+ const response2 = await agent.respond({
155
+ history: [
156
+ ...previousHistory,
157
+ createMessageEvent(
158
+ EventSource.CUSTOMER,
159
+ "User",
160
+ "Actually, make that Tokyo instead"
161
+ ),
162
+ ],
163
+ session: response1.session, // Router sees context and switches appropriately
170
164
  });
165
+ ```
171
166
 
172
- // Route-level access control
173
- const checkoutRoute = agent.createRoute({
174
- title: "Checkout",
175
- domains: ["payment"], // SECURITY: Only payment tools can execute in this route
176
- });
167
+ **Why?** Always-on routing provides:
177
168
 
178
- const profileRoute = agent.createRoute({
179
- title: "Profile Setup",
180
- domains: ["user"], // SECURITY: Only user tools can execute
181
- });
169
+ - **User Control** - Users can change their mind naturally
170
+ - **Context Awareness** - Router considers current progress
171
+ - **Graceful Transitions** - Smooth handling of intent changes
172
+ - **No Dead Ends** - Always a valid next step
173
+
174
+ ### 6. 🔄 Three-Phase Pipeline
175
+
176
+ Clean separation of concerns in every response:
182
177
 
183
- const generalRoute = agent.createRoute({
184
- title: "General Chat",
185
- // No domains = ALL tools available (default behavior)
186
- });
178
+ ```
179
+ 1. PREPARATION → 2. ROUTING → 3. RESPONSE
187
180
  ```
188
181
 
189
- **Important**: Domains are for **internal tool filtering**, not AI prompts:
182
+ **Phase 1: Preparation**
190
183
 
191
- - Engine uses domains to filter which tools can execute
192
- - Prevents accidental/malicious tool calls outside intended routes
193
- - AI never sees domains (they're not in prompts)
194
- - ❌ Domains don't affect what AI says, only what tools can run
184
+ - Execute tools if current state has `toolState`
185
+ - Update context with tool results
186
+ - Enrich extracted data if tools return `extractedUpdate`
195
187
 
196
- **Why?** Domains provide:
188
+ **Phase 2: Routing**
197
189
 
198
- - **Security** - Prevent prompt injection attacks from calling sensitive tools
199
- - **Route Isolation** - Checkout route can't accidentally trigger user profile tools
200
- - **Organization** - Logical grouping of related tools
201
- - **Optional** - Routes without domains have access to all tools (simple default)
202
- - **Scalability** - Add new domains without affecting existing ones
190
+ - Build dynamic routing schema (scores for all routes 0-100)
191
+ - Include session context in routing prompt
192
+ - AI selects best route based on current state
193
+ - Update session if route changed
203
194
 
204
- **Example Security Scenario**:
195
+ **Phase 3: Response**
205
196
 
206
- ```typescript
207
- // Without domains: Malicious user in chat could trigger payment processing
208
- // With domains: Payment tools only available in checkout route
209
- const chatRoute = agent.createRoute({
210
- title: "General Chat",
211
- domains: ["chat", "user"], // Can't execute payment tools
212
- });
197
+ - Determine next state using `skipIf` and `requiredData`
198
+ - Build response schema including current state's `gather` fields
199
+ - Generate message with schema-enforced data extraction
200
+ - Update session with newly extracted data
213
201
 
214
- const checkoutRoute = agent.createRoute({
215
- title: "Checkout",
216
- domains: ["payment"], // Can execute payment, but not unrelated tools
217
- });
218
- ```
202
+ **Why?** This architecture enables:
219
203
 
220
- ### 6. 🎭 Context-Driven Behavior
204
+ - **Efficiency** - 1-2 LLM calls per turn vs 3-5 in condition-heavy approaches
205
+ - **Reliability** - Tools execute before AI sees the conversation
206
+ - **Flexibility** - Always-on routing handles any user direction
207
+ - **Type Safety** - Schemas ensure consistent data structures
221
208
 
222
- Agent behavior adapts based on **context** - not hardcoded logic:
209
+ ### 7. 📦 Enhanced Lifecycle Hooks
223
210
 
224
- ```typescript
225
- interface MyContext {
226
- userId: string;
227
- plan: "free" | "pro" | "enterprise";
228
- remainingCredits: number;
229
- }
211
+ Hooks for validation, enrichment, and persistence:
230
212
 
231
- const agent = new Agent<MyContext>({
232
- name: "Assistant",
233
- ai: provider,
234
- context: {
235
- userId: "user123",
236
- plan: "pro",
237
- remainingCredits: 100,
238
- },
213
+ ```typescript
214
+ const agent = new Agent({
215
+ // ... other options
239
216
  hooks: {
240
217
  // Refresh context before each response
241
218
  beforeRespond: async (ctx) => {
242
- const freshData = await db.getUser(ctx.userId);
243
- return { ...ctx, remainingCredits: freshData.credits };
219
+ return await loadFreshContext(ctx.userId);
244
220
  },
245
221
 
246
222
  // Persist context updates
247
223
  onContextUpdate: async (newCtx, oldCtx) => {
248
- await db.updateUser(newCtx.userId, {
249
- credits: newCtx.remainingCredits,
250
- });
224
+ await saveContext(newCtx);
225
+ },
226
+
227
+ // Validate and enrich extracted data
228
+ onExtractedUpdate: async (extracted, previous) => {
229
+ // Normalize data
230
+ if (extracted.passengers < 1) extracted.passengers = 1;
231
+
232
+ // Auto-trigger actions
233
+ if (hasAllRequiredData(extracted)) {
234
+ extracted.shouldSearchFlights = true;
235
+ }
236
+
237
+ return extracted;
251
238
  },
252
239
  },
253
240
  });
241
+ ```
254
242
 
255
- // Guidelines can reference context
256
- agent.createGuideline({
257
- condition: "User requests a feature",
258
- action:
259
- "If context.plan is 'free', explain upgrade benefits. Otherwise provide the feature.",
260
- });
243
+ **Why?** Lifecycle hooks provide:
261
244
 
262
- // Tools receive and update context
263
- const useFeature = defineTool<MyContext, [feature: string], boolean>(
264
- "use_feature",
265
- async (toolContext, feature) => {
266
- return {
267
- data: true,
268
- contextUpdate: {
269
- remainingCredits: toolContext.context.remainingCredits - 1,
270
- },
271
- };
272
- }
273
- );
274
- ```
245
+ - **Data Validation** - Ensure extracted data meets business rules
246
+ - **Enrichment** - Add computed fields or normalize values
247
+ - **Persistence** - Save/restore conversation state
248
+ - **Integration** - Connect with external systems
275
249
 
276
- **Why?** Context-driven design enables:
250
+ ## Comparison with Other Approaches
277
251
 
278
- - **Personalization** - Behavior adapts to each user
279
- - **State persistence** - Context survives across conversations
280
- - **Dynamic guidelines** - Rules that depend on user state
281
- - **Tool integration** - Tools update context for next iteration
252
+ ### @falai/agent vs Traditional State Machines
282
253
 
283
- ### 7. 📝 Declarative Over Imperative
254
+ | Aspect | @falai/agent (Data-Driven) | Traditional State Machines |
255
+ | ------------------- | -------------------------- | ------------------------------------- |
256
+ | **State Logic** | Code-based (`skipIf`) | Manual condition checking |
257
+ | **Data Collection** | Schema-driven extraction | Manual parsing |
258
+ | **Type Safety** | Full TypeScript | Often runtime validation |
259
+ | **LLM Calls/Turn** | 1-2 (routing + response) | 3-5 (conditions + routing + response) |
260
+ | **Validation** | Code + schemas | Manual implementation |
261
+ | **Routing** | Always-on, context-aware | Route-only, rigid |
284
262
 
285
- Configuration is **declarative** - you describe what you want, not how to do it:
263
+ ### @falai/agent vs OpenAI Function Calling
286
264
 
287
- ```typescript
288
- // DECLARATIVE: Describe the desired behavior
289
- const agent = new Agent({
290
- name: "Support Agent",
291
- description: "Helpful and empathetic customer support",
292
- goal: "Resolve customer issues efficiently",
293
- ai: provider,
294
- terms: [
295
- {
296
- name: "SLA",
297
- description: "Service Level Agreement - 24hr response time",
298
- },
299
- ],
300
- guidelines: [
301
- {
302
- condition: "Customer is frustrated",
303
- action: "Apologize sincerely and escalate to human if needed",
304
- },
305
- ],
306
- routes: [
307
- {
308
- title: "Refund Request",
309
- conditions: ["Customer wants a refund"],
310
- rules: ["Always check order date before processing"],
311
- prohibitions: ["Never process refunds over $1000 without approval"],
312
- },
313
- ],
314
- });
265
+ | Aspect | @falai/agent | OpenAI Functions |
266
+ | ------------------ | ------------------------ | ----------------------------- |
267
+ | **Tool Execution** | Automatic (state-driven) | AI-decided |
268
+ | **Flow Control** | Code-based state logic | AI inference |
269
+ | **Determinism** | High - code-driven flow | Low - AI may vary |
270
+ | **Data Handling** | Structured schemas | Unstructured function results |
271
+ | **Type Safety** | Full TypeScript | Runtime type checking |
272
+ | **Use Case** | Structured conversations | Flexible, open-ended tasks |
315
273
 
316
- // IMPERATIVE: Manual control flow
317
- if (userMessage.includes("refund")) {
318
- if (orderDate < thirtyDaysAgo) {
319
- if (amount <= 1000) {
320
- processRefund();
321
- } else {
322
- requestApproval();
323
- }
324
- }
274
+ ## When to Use @falai/agent
275
+
276
+ **Great for:**
277
+
278
+ - **Data Collection Flows** - Booking, onboarding, forms, surveys
279
+ - **Multi-Step Processes** - Complex workflows with clear progression
280
+ - **Type-Safe Applications** - When data structure matters
281
+ - **Predictable Conversations** - Structured, goal-oriented interactions
282
+ - **Session-Based Apps** - Conversations that span multiple turns
283
+
284
+ ❌ **Not ideal for:**
285
+
286
+ - **Open-Ended Chat** - General conversation without clear goals
287
+ - **Creative Tasks** - Brainstorming, writing, ideation
288
+ - **Simple Q&A** - Stateless question-answering (use stateless routes!)
289
+ - **Rapid Prototyping** - When you need maximum flexibility
290
+
291
+ ## Architecture Patterns
292
+
293
+ ### Stateful Routes (Data Collection)
294
+
295
+ For structured data gathering with state management:
296
+
297
+ ```typescript
298
+ interface BookingData {
299
+ destination: string;
300
+ dates: string;
301
+ passengers: number;
325
302
  }
326
- ```
327
303
 
328
- **Why?** Declarative configuration:
304
+ const bookingRoute = agent.createRoute<BookingData>({
305
+ title: "Flight Booking",
306
+ gatherSchema: {
307
+ /* schema for all fields */
308
+ },
309
+ });
329
310
 
330
- - **Reads like documentation** - Easy to understand intent
331
- - **Less boilerplate** - Framework handles the "how"
332
- - **Easier to maintain** - Change behavior by changing config
333
- - **Better for non-coders** - Business logic in plain terms
311
+ bookingRoute.initialState
312
+ .transitionTo({
313
+ chatState: "Ask destination",
314
+ gather: ["destination"],
315
+ skipIf: (data) => !!data.destination,
316
+ })
317
+ .transitionTo({
318
+ toolState: enrichDestination, // Validates and enriches
319
+ requiredData: ["destination"],
320
+ })
321
+ .transitionTo({
322
+ chatState: "Ask dates",
323
+ gather: ["dates"],
324
+ skipIf: (data) => !!data.dates,
325
+ });
326
+ ```
334
327
 
335
- ## Comparison with Other Approaches
328
+ ### Stateless Routes (Q&A)
336
329
 
337
- ### @falai/agent vs OpenAI Function Calling
330
+ For simple question-answering without state:
338
331
 
339
- | Aspect | @falai/agent | OpenAI Functions |
340
- | ------------------ | ----------------------------- | ---------------------------------- |
341
- | **Tool Execution** | Automatic (state machine) | AI-decided |
342
- | **Flow Control** | Explicit routes & states | AI inference |
343
- | **Determinism** | High - same input = same flow | Low - AI may vary |
344
- | **Debugging** | Clear state progression | Black box AI decisions |
345
- | **Cost** | Lower - tools not in prompts | Higher - full tool specs in prompt |
346
- | **Use Case** | Structured conversations | Flexible, open-ended tasks |
332
+ ```typescript
333
+ const qnaRoute = agent.createRoute({
334
+ title: "Company Q&A",
335
+ conditions: ["User asks about company"],
336
+ // NO gatherSchema - stateless!
337
+ });
347
338
 
348
- ### When to Use @falai/agent
339
+ // Just use initial state
340
+ qnaRoute.initialState.chatState = "Answer from knowledge base";
341
+ ```
349
342
 
350
- **Great for:**
343
+ ### Mixed Architecture
351
344
 
352
- - Customer support workflows
353
- - Onboarding flows
354
- - Multi-step processes
355
- - Compliance-sensitive applications
356
- - Predictable conversation paths
357
- - Tool-heavy applications
345
+ Combine both patterns in one agent:
358
346
 
359
- ❌ **Not ideal for:**
347
+ ```typescript
348
+ // Q&A routes (stateless)
349
+ const companyInfoRoute = agent.createRoute({
350
+ /* no schema */
351
+ });
352
+ const productInfoRoute = agent.createRoute({
353
+ /* no schema */
354
+ });
360
355
 
361
- - Open-ended creative tasks
362
- - Scenarios where AI judgment is critical
363
- - Rapid prototyping without clear flows
364
- - Simple Q&A without state
356
+ // Booking routes (stateful)
357
+ const bookingRoute = agent.createRoute<BookingData>({
358
+ /* with schema */
359
+ });
360
+ const supportRoute = agent.createRoute<SupportData>({
361
+ /* with schema */
362
+ });
363
+ ```
365
364
 
366
365
  ## Design Influences
367
366
 
368
367
  This framework draws inspiration from:
369
368
 
370
- 1. **[Parlant/Emcie](https://github.com/emcie-co/parlant)** - Preparation iteration loop, guideline-driven execution
369
+ 1. **Schema-First APIs** - JSON Schema for data contracts
371
370
  2. **State Machines** - Explicit state modeling for predictability
372
- 3. **Railway-Oriented Programming** - Fluent APIs with chaining
373
- 4. **Domain-Driven Design** - Organizing capabilities by domain
374
- 5. **React Hooks** - Lifecycle hooks pattern for extensibility
371
+ 3. **TypeScript** - Full type safety throughout
372
+ 4. **Functional Programming** - Pure functions for state logic
373
+ 5. **React Hooks** - Lifecycle patterns for extensibility
375
374
 
376
375
  ## Further Reading
377
376
 
378
377
  - [Getting Started Guide](./GETTING_STARTED.md) - Build your first agent
378
+ - [Session State Guide](./CONTEXT_MANAGEMENT.md) - Session management patterns
379
379
  - [API Reference](./API_REFERENCE.md) - Complete API documentation
380
- - [Route DSL Guide](./STRUCTURE.md) - State machine patterns
381
- - [Provider Docs](./PROVIDERS.md) - AI provider configuration
382
- - [Examples](/examples) - Real-world agent implementations
380
+ - [Examples](../examples/) - Real-world implementations
383
381
 
384
382
  ---
385
383