@falai/agent 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/README.md +21 -74
  2. package/dist/cjs/core/Agent.d.ts +22 -29
  3. package/dist/cjs/core/Agent.d.ts.map +1 -1
  4. package/dist/cjs/core/Agent.js +464 -291
  5. package/dist/cjs/core/Agent.js.map +1 -1
  6. package/dist/cjs/core/Events.d.ts +10 -1
  7. package/dist/cjs/core/Events.d.ts.map +1 -1
  8. package/dist/cjs/core/Events.js +3 -2
  9. package/dist/cjs/core/Events.js.map +1 -1
  10. package/dist/cjs/core/PersistenceManager.d.ts +19 -0
  11. package/dist/cjs/core/PersistenceManager.d.ts.map +1 -1
  12. package/dist/cjs/core/PersistenceManager.js +57 -0
  13. package/dist/cjs/core/PersistenceManager.js.map +1 -1
  14. package/dist/cjs/core/PromptComposer.d.ts +24 -0
  15. package/dist/cjs/core/PromptComposer.d.ts.map +1 -0
  16. package/dist/cjs/core/PromptComposer.js +127 -0
  17. package/dist/cjs/core/PromptComposer.js.map +1 -0
  18. package/dist/cjs/core/ResponseEngine.d.ts +19 -0
  19. package/dist/cjs/core/ResponseEngine.d.ts.map +1 -0
  20. package/dist/cjs/core/ResponseEngine.js +51 -0
  21. package/dist/cjs/core/ResponseEngine.js.map +1 -0
  22. package/dist/cjs/core/Route.d.ts +18 -12
  23. package/dist/cjs/core/Route.d.ts.map +1 -1
  24. package/dist/cjs/core/Route.js +15 -9
  25. package/dist/cjs/core/Route.js.map +1 -1
  26. package/dist/cjs/core/RoutingEngine.d.ts +38 -0
  27. package/dist/cjs/core/RoutingEngine.d.ts.map +1 -0
  28. package/dist/cjs/core/RoutingEngine.js +110 -0
  29. package/dist/cjs/core/RoutingEngine.js.map +1 -0
  30. package/dist/cjs/core/State.d.ts +15 -4
  31. package/dist/cjs/core/State.d.ts.map +1 -1
  32. package/dist/cjs/core/State.js +21 -2
  33. package/dist/cjs/core/State.js.map +1 -1
  34. package/dist/cjs/core/ToolExecutor.d.ts +29 -0
  35. package/dist/cjs/core/ToolExecutor.d.ts.map +1 -0
  36. package/dist/cjs/core/ToolExecutor.js +73 -0
  37. package/dist/cjs/core/ToolExecutor.js.map +1 -0
  38. package/dist/cjs/core/Transition.d.ts +5 -5
  39. package/dist/cjs/core/Transition.d.ts.map +1 -1
  40. package/dist/cjs/core/Transition.js.map +1 -1
  41. package/dist/cjs/index.d.ts +6 -8
  42. package/dist/cjs/index.d.ts.map +1 -1
  43. package/dist/cjs/index.js +8 -10
  44. package/dist/cjs/index.js.map +1 -1
  45. package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -1
  46. package/dist/cjs/providers/AnthropicProvider.js +10 -13
  47. package/dist/cjs/providers/AnthropicProvider.js.map +1 -1
  48. package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
  49. package/dist/cjs/providers/GeminiProvider.js +12 -8
  50. package/dist/cjs/providers/GeminiProvider.js.map +1 -1
  51. package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
  52. package/dist/cjs/providers/OpenAIProvider.js +10 -53
  53. package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
  54. package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
  55. package/dist/cjs/providers/OpenRouterProvider.js +10 -53
  56. package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
  57. package/dist/cjs/types/agent.d.ts +13 -12
  58. package/dist/cjs/types/agent.d.ts.map +1 -1
  59. package/dist/cjs/types/ai.d.ts +8 -2
  60. package/dist/cjs/types/ai.d.ts.map +1 -1
  61. package/dist/cjs/types/history.d.ts +8 -0
  62. package/dist/cjs/types/history.d.ts.map +1 -1
  63. package/dist/cjs/types/index.d.ts +0 -3
  64. package/dist/cjs/types/index.d.ts.map +1 -1
  65. package/dist/cjs/types/index.js +1 -3
  66. package/dist/cjs/types/index.js.map +1 -1
  67. package/dist/cjs/types/route.d.ts +39 -4
  68. package/dist/cjs/types/route.d.ts.map +1 -1
  69. package/dist/cjs/types/routing.d.ts +16 -0
  70. package/dist/cjs/types/routing.d.ts.map +1 -0
  71. package/dist/cjs/types/routing.js +3 -0
  72. package/dist/cjs/types/routing.js.map +1 -0
  73. package/dist/cjs/types/schema.d.ts +22 -0
  74. package/dist/cjs/types/schema.d.ts.map +1 -0
  75. package/dist/cjs/types/schema.js +3 -0
  76. package/dist/cjs/types/schema.js.map +1 -0
  77. package/dist/cjs/types/session.d.ts +72 -0
  78. package/dist/cjs/types/session.d.ts.map +1 -0
  79. package/dist/cjs/types/session.js +140 -0
  80. package/dist/cjs/types/session.js.map +1 -0
  81. package/dist/cjs/types/tool.d.ts +11 -5
  82. package/dist/cjs/types/tool.d.ts.map +1 -1
  83. package/dist/cjs/utils/id.d.ts +0 -5
  84. package/dist/cjs/utils/id.d.ts.map +1 -1
  85. package/dist/cjs/utils/id.js +0 -10
  86. package/dist/cjs/utils/id.js.map +1 -1
  87. package/dist/cjs/utils/schema.d.ts +17 -0
  88. package/dist/cjs/utils/schema.d.ts.map +1 -0
  89. package/dist/cjs/utils/schema.js +32 -0
  90. package/dist/cjs/utils/schema.js.map +1 -0
  91. package/dist/core/Agent.d.ts +22 -29
  92. package/dist/core/Agent.d.ts.map +1 -1
  93. package/dist/core/Agent.js +464 -291
  94. package/dist/core/Agent.js.map +1 -1
  95. package/dist/core/Events.d.ts +10 -1
  96. package/dist/core/Events.d.ts.map +1 -1
  97. package/dist/core/Events.js +3 -2
  98. package/dist/core/Events.js.map +1 -1
  99. package/dist/core/PersistenceManager.d.ts +19 -0
  100. package/dist/core/PersistenceManager.d.ts.map +1 -1
  101. package/dist/core/PersistenceManager.js +57 -0
  102. package/dist/core/PersistenceManager.js.map +1 -1
  103. package/dist/core/PromptComposer.d.ts +24 -0
  104. package/dist/core/PromptComposer.d.ts.map +1 -0
  105. package/dist/core/PromptComposer.js +123 -0
  106. package/dist/core/PromptComposer.js.map +1 -0
  107. package/dist/core/ResponseEngine.d.ts +19 -0
  108. package/dist/core/ResponseEngine.d.ts.map +1 -0
  109. package/dist/core/ResponseEngine.js +47 -0
  110. package/dist/core/ResponseEngine.js.map +1 -0
  111. package/dist/core/Route.d.ts +18 -12
  112. package/dist/core/Route.d.ts.map +1 -1
  113. package/dist/core/Route.js +15 -9
  114. package/dist/core/Route.js.map +1 -1
  115. package/dist/core/RoutingEngine.d.ts +38 -0
  116. package/dist/core/RoutingEngine.d.ts.map +1 -0
  117. package/dist/core/RoutingEngine.js +106 -0
  118. package/dist/core/RoutingEngine.js.map +1 -0
  119. package/dist/core/State.d.ts +15 -4
  120. package/dist/core/State.d.ts.map +1 -1
  121. package/dist/core/State.js +21 -2
  122. package/dist/core/State.js.map +1 -1
  123. package/dist/core/ToolExecutor.d.ts +29 -0
  124. package/dist/core/ToolExecutor.d.ts.map +1 -0
  125. package/dist/core/ToolExecutor.js +69 -0
  126. package/dist/core/ToolExecutor.js.map +1 -0
  127. package/dist/core/Transition.d.ts +5 -5
  128. package/dist/core/Transition.d.ts.map +1 -1
  129. package/dist/core/Transition.js.map +1 -1
  130. package/dist/index.d.ts +6 -8
  131. package/dist/index.d.ts.map +1 -1
  132. package/dist/index.js +3 -5
  133. package/dist/index.js.map +1 -1
  134. package/dist/providers/AnthropicProvider.d.ts.map +1 -1
  135. package/dist/providers/AnthropicProvider.js +10 -13
  136. package/dist/providers/AnthropicProvider.js.map +1 -1
  137. package/dist/providers/GeminiProvider.d.ts.map +1 -1
  138. package/dist/providers/GeminiProvider.js +12 -8
  139. package/dist/providers/GeminiProvider.js.map +1 -1
  140. package/dist/providers/OpenAIProvider.d.ts.map +1 -1
  141. package/dist/providers/OpenAIProvider.js +10 -53
  142. package/dist/providers/OpenAIProvider.js.map +1 -1
  143. package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
  144. package/dist/providers/OpenRouterProvider.js +10 -53
  145. package/dist/providers/OpenRouterProvider.js.map +1 -1
  146. package/dist/types/agent.d.ts +13 -12
  147. package/dist/types/agent.d.ts.map +1 -1
  148. package/dist/types/ai.d.ts +8 -2
  149. package/dist/types/ai.d.ts.map +1 -1
  150. package/dist/types/history.d.ts +8 -0
  151. package/dist/types/history.d.ts.map +1 -1
  152. package/dist/types/index.d.ts +0 -3
  153. package/dist/types/index.d.ts.map +1 -1
  154. package/dist/types/index.js +0 -1
  155. package/dist/types/index.js.map +1 -1
  156. package/dist/types/route.d.ts +39 -4
  157. package/dist/types/route.d.ts.map +1 -1
  158. package/dist/types/routing.d.ts +16 -0
  159. package/dist/types/routing.d.ts.map +1 -0
  160. package/dist/types/routing.js +2 -0
  161. package/dist/types/routing.js.map +1 -0
  162. package/dist/types/schema.d.ts +22 -0
  163. package/dist/types/schema.d.ts.map +1 -0
  164. package/dist/types/schema.js +2 -0
  165. package/dist/types/schema.js.map +1 -0
  166. package/dist/types/session.d.ts +72 -0
  167. package/dist/types/session.d.ts.map +1 -0
  168. package/dist/types/session.js +132 -0
  169. package/dist/types/session.js.map +1 -0
  170. package/dist/types/tool.d.ts +11 -5
  171. package/dist/types/tool.d.ts.map +1 -1
  172. package/dist/utils/id.d.ts +0 -5
  173. package/dist/utils/id.d.ts.map +1 -1
  174. package/dist/utils/id.js +0 -9
  175. package/dist/utils/id.js.map +1 -1
  176. package/dist/utils/schema.d.ts +17 -0
  177. package/dist/utils/schema.d.ts.map +1 -0
  178. package/dist/utils/schema.js +27 -0
  179. package/dist/utils/schema.js.map +1 -0
  180. package/docs/ADAPTERS.md +83 -3
  181. package/docs/API_REFERENCE.md +95 -104
  182. package/docs/ARCHITECTURE.md +284 -286
  183. package/docs/CONSTRUCTOR_OPTIONS.md +192 -135
  184. package/docs/CONTEXT_MANAGEMENT.md +311 -28
  185. package/docs/CONTRIBUTING.md +1 -1
  186. package/docs/DOMAINS.md +61 -0
  187. package/docs/GETTING_STARTED.md +177 -88
  188. package/docs/PERSISTENCE.md +170 -23
  189. package/docs/README.md +7 -10
  190. package/examples/business-onboarding.ts +21 -9
  191. package/examples/company-qna-agent.ts +508 -0
  192. package/examples/declarative-agent.ts +143 -26
  193. package/examples/domain-scoping.ts +31 -10
  194. package/examples/extracted-data-modification.ts +415 -0
  195. package/examples/healthcare-agent.ts +194 -90
  196. package/examples/openai-agent.ts +67 -25
  197. package/examples/opensearch-persistence.ts +455 -151
  198. package/examples/persistent-onboarding.ts +162 -96
  199. package/examples/prisma-persistence.ts +371 -125
  200. package/examples/redis-persistence.ts +393 -23
  201. package/examples/rules-prohibitions.ts +32 -11
  202. package/examples/streaming-agent.ts +61 -13
  203. package/examples/travel-agent.ts +266 -133
  204. package/package.json +1 -1
  205. package/src/core/Agent.ts +674 -356
  206. package/src/core/Events.ts +12 -2
  207. package/src/core/PersistenceManager.ts +83 -0
  208. package/src/core/PromptComposer.ts +143 -0
  209. package/src/core/ResponseEngine.ts +82 -0
  210. package/src/core/Route.ts +32 -17
  211. package/src/core/RoutingEngine.ts +165 -0
  212. package/src/core/State.ts +55 -15
  213. package/src/core/ToolExecutor.ts +117 -0
  214. package/src/core/Transition.ts +5 -5
  215. package/src/index.ts +12 -21
  216. package/src/providers/AnthropicProvider.ts +10 -13
  217. package/src/providers/GeminiProvider.ts +12 -8
  218. package/src/providers/OpenAIProvider.ts +10 -56
  219. package/src/providers/OpenRouterProvider.ts +10 -56
  220. package/src/types/agent.ts +16 -13
  221. package/src/types/ai.ts +6 -2
  222. package/src/types/history.ts +8 -0
  223. package/src/types/index.ts +0 -11
  224. package/src/types/route.ts +41 -5
  225. package/src/types/routing.ts +18 -0
  226. package/src/types/schema.ts +23 -0
  227. package/src/types/session.ts +207 -0
  228. package/src/types/tool.ts +29 -7
  229. package/src/utils/id.ts +0 -10
  230. package/src/utils/schema.ts +32 -0
  231. package/dist/cjs/core/ConditionEvaluator.d.ts +0 -72
  232. package/dist/cjs/core/ConditionEvaluator.d.ts.map +0 -1
  233. package/dist/cjs/core/ConditionEvaluator.js +0 -272
  234. package/dist/cjs/core/ConditionEvaluator.js.map +0 -1
  235. package/dist/cjs/core/Observation.d.ts +0 -24
  236. package/dist/cjs/core/Observation.d.ts.map +0 -1
  237. package/dist/cjs/core/Observation.js +0 -39
  238. package/dist/cjs/core/Observation.js.map +0 -1
  239. package/dist/cjs/core/PreparationEngine.d.ts +0 -116
  240. package/dist/cjs/core/PreparationEngine.d.ts.map +0 -1
  241. package/dist/cjs/core/PreparationEngine.js +0 -353
  242. package/dist/cjs/core/PreparationEngine.js.map +0 -1
  243. package/dist/cjs/core/PromptBuilder.d.ts +0 -136
  244. package/dist/cjs/core/PromptBuilder.d.ts.map +0 -1
  245. package/dist/cjs/core/PromptBuilder.js +0 -421
  246. package/dist/cjs/core/PromptBuilder.js.map +0 -1
  247. package/dist/cjs/types/observation.d.ts +0 -27
  248. package/dist/cjs/types/observation.d.ts.map +0 -1
  249. package/dist/cjs/types/observation.js +0 -6
  250. package/dist/cjs/types/observation.js.map +0 -1
  251. package/dist/cjs/types/prompt.d.ts +0 -46
  252. package/dist/cjs/types/prompt.d.ts.map +0 -1
  253. package/dist/cjs/types/prompt.js +0 -19
  254. package/dist/cjs/types/prompt.js.map +0 -1
  255. package/dist/core/ConditionEvaluator.d.ts +0 -72
  256. package/dist/core/ConditionEvaluator.d.ts.map +0 -1
  257. package/dist/core/ConditionEvaluator.js +0 -268
  258. package/dist/core/ConditionEvaluator.js.map +0 -1
  259. package/dist/core/Observation.d.ts +0 -24
  260. package/dist/core/Observation.d.ts.map +0 -1
  261. package/dist/core/Observation.js +0 -35
  262. package/dist/core/Observation.js.map +0 -1
  263. package/dist/core/PreparationEngine.d.ts +0 -116
  264. package/dist/core/PreparationEngine.d.ts.map +0 -1
  265. package/dist/core/PreparationEngine.js +0 -349
  266. package/dist/core/PreparationEngine.js.map +0 -1
  267. package/dist/core/PromptBuilder.d.ts +0 -136
  268. package/dist/core/PromptBuilder.d.ts.map +0 -1
  269. package/dist/core/PromptBuilder.js +0 -417
  270. package/dist/core/PromptBuilder.js.map +0 -1
  271. package/dist/types/observation.d.ts +0 -27
  272. package/dist/types/observation.d.ts.map +0 -1
  273. package/dist/types/observation.js +0 -5
  274. package/dist/types/observation.js.map +0 -1
  275. package/dist/types/prompt.d.ts +0 -46
  276. package/dist/types/prompt.d.ts.map +0 -1
  277. package/dist/types/prompt.js +0 -16
  278. package/dist/types/prompt.js.map +0 -1
  279. package/docs/STRUCTURE.md +0 -58
  280. package/src/core/ConditionEvaluator.ts +0 -381
  281. package/src/core/Observation.ts +0 -47
  282. package/src/core/PreparationEngine.ts +0 -561
  283. package/src/core/PromptBuilder.ts +0 -617
  284. package/src/types/observation.ts +0 -29
  285. package/src/types/prompt.ts +0 -49
package/src/core/State.ts CHANGED
@@ -16,18 +16,27 @@ import { generateStateId } from "../utils/id";
16
16
  /**
17
17
  * Represents a state within a route
18
18
  */
19
- export class State<TContext = unknown> {
19
+ export class State<TContext = unknown, TExtracted = unknown> {
20
20
  public readonly id: string;
21
- private transitions: Transition<TContext>[] = [];
21
+ private transitions: Transition<TContext, TExtracted>[] = [];
22
22
  private guidelines: Guideline[] = [];
23
+ public readonly gatherFields?: string[];
24
+ public readonly skipIf?: (extracted: Partial<TExtracted>) => boolean;
25
+ public readonly requiredData?: string[];
23
26
 
24
27
  constructor(
25
28
  public readonly routeId: string,
26
29
  public readonly description?: string,
27
- customId?: string
30
+ customId?: string,
31
+ gatherFields?: string[],
32
+ skipIf?: (extracted: Partial<TExtracted>) => boolean,
33
+ requiredData?: string[]
28
34
  ) {
29
35
  // Use provided ID or generate a deterministic one
30
36
  this.id = customId || generateStateId(routeId, description);
37
+ this.gatherFields = gatherFields;
38
+ this.skipIf = skipIf;
39
+ this.requiredData = requiredData;
31
40
  }
32
41
 
33
42
  /**
@@ -38,16 +47,16 @@ export class State<TContext = unknown> {
38
47
  * @returns TransitionResult that supports chaining
39
48
  */
40
49
  transitionTo(
41
- spec: TransitionSpec<TContext>,
50
+ spec: TransitionSpec<TContext, TExtracted>,
42
51
  condition?: string
43
- ): TransitionResult<TContext> {
52
+ ): TransitionResult<TContext, TExtracted> {
44
53
  // Handle END_ROUTE
45
54
  if (
46
55
  spec.state &&
47
56
  typeof spec.state === "symbol" &&
48
57
  spec.state === END_ROUTE
49
58
  ) {
50
- const endTransition = new Transition<TContext>(
59
+ const endTransition = new Transition<TContext, TExtracted>(
51
60
  this.getRef(),
52
61
  { state: END_ROUTE },
53
62
  condition
@@ -60,7 +69,7 @@ export class State<TContext = unknown> {
60
69
 
61
70
  // Handle direct state reference
62
71
  if (spec.state && typeof spec.state !== "symbol") {
63
- const transition = new Transition<TContext>(
72
+ const transition = new Transition<TContext, TExtracted>(
64
73
  this.getRef(),
65
74
  spec,
66
75
  condition
@@ -71,8 +80,19 @@ export class State<TContext = unknown> {
71
80
  }
72
81
 
73
82
  // Create new target state for chatState or toolState
74
- const targetState = new State<TContext>(this.routeId, spec.chatState);
75
- const transition = new Transition<TContext>(this.getRef(), spec, condition);
83
+ const targetState = new State<TContext, TExtracted>(
84
+ this.routeId,
85
+ spec.chatState,
86
+ undefined,
87
+ spec.gather,
88
+ spec.skipIf,
89
+ spec.requiredData
90
+ );
91
+ const transition = new Transition<TContext, TExtracted>(
92
+ this.getRef(),
93
+ spec,
94
+ condition
95
+ );
76
96
  transition.setTarget(targetState);
77
97
 
78
98
  this.transitions.push(transition);
@@ -97,10 +117,28 @@ export class State<TContext = unknown> {
97
117
  /**
98
118
  * Get all transitions from this state
99
119
  */
100
- getTransitions(): Transition<TContext>[] {
120
+ getTransitions(): Transition<TContext, TExtracted>[] {
101
121
  return [...this.transitions];
102
122
  }
103
123
 
124
+ /**
125
+ * Check if this state should be skipped based on extracted data
126
+ */
127
+ shouldSkip(extracted: Partial<TExtracted>): boolean {
128
+ if (!this.skipIf) return false;
129
+ return this.skipIf(extracted);
130
+ }
131
+
132
+ /**
133
+ * Check if this state has all required data to proceed
134
+ */
135
+ hasRequiredData(extracted: Partial<TExtracted>): boolean {
136
+ if (!this.requiredData || this.requiredData.length === 0) return true;
137
+ return this.requiredData.every(
138
+ (key) => extracted[key as keyof TExtracted] !== undefined
139
+ );
140
+ }
141
+
104
142
  /**
105
143
  * Get state reference
106
144
  */
@@ -116,21 +154,23 @@ export class State<TContext = unknown> {
116
154
  */
117
155
  private createStateRefWithTransition(
118
156
  ref: StateRef,
119
- state?: State<TContext>
120
- ): TransitionResult<TContext> {
157
+ state?: State<TContext, TExtracted>
158
+ ): TransitionResult<TContext, TExtracted> {
121
159
  const stateInstance = state || this;
122
160
 
123
161
  return {
124
162
  ...ref,
125
- transitionTo: (spec: TransitionSpec<TContext>, condition?: string) =>
126
- stateInstance.transitionTo(spec, condition),
163
+ transitionTo: (
164
+ spec: TransitionSpec<TContext, TExtracted>,
165
+ condition?: string
166
+ ) => stateInstance.transitionTo(spec, condition),
127
167
  };
128
168
  }
129
169
 
130
170
  /**
131
171
  * Create a terminal state reference (for END_ROUTE)
132
172
  */
133
- private createTerminalRef(): TransitionResult<TContext> {
173
+ private createTerminalRef(): TransitionResult<TContext, TExtracted> {
134
174
  const terminalRef: StateRef = {
135
175
  id: "END",
136
176
  routeId: this.routeId,
@@ -0,0 +1,117 @@
1
+ /**
2
+ * ToolExecutor - Simple utility for executing tools with context
3
+ *
4
+ * Tools execute BEFORE message generation to enrich context with data.
5
+ * The LLM sees the enriched context when generating responses.
6
+ */
7
+
8
+ import type { Event } from "../types/history";
9
+ import type { ToolRef, ToolContext } from "../types/tool";
10
+
11
+ export interface ToolExecutionResult {
12
+ toolName: string;
13
+ success: boolean;
14
+ data?: unknown;
15
+ contextUpdate?: Record<string, unknown>;
16
+ extractedUpdate?: Record<string, unknown>;
17
+ error?: string;
18
+ }
19
+
20
+ export class ToolExecutor<TContext = unknown, TExtracted = unknown> {
21
+ /**
22
+ * Execute a single tool with context and extracted data
23
+ * @param allowedDomains - Array of domain names allowed for this execution context (undefined = all domains allowed)
24
+ */
25
+ async executeTool(
26
+ tool: ToolRef<TContext, unknown[], unknown>,
27
+ context: TContext,
28
+ updateContext: (updates: Partial<TContext>) => Promise<void>,
29
+ history: Event[],
30
+ extracted?: Partial<TExtracted>,
31
+ allowedDomains?: string[]
32
+ ): Promise<ToolExecutionResult> {
33
+ try {
34
+ // Domain enforcement: Check if tool's domain is allowed
35
+ if (allowedDomains !== undefined && tool.domainName) {
36
+ // allowedDomains is explicitly set (could be empty array)
37
+ if (!allowedDomains.includes(tool.domainName)) {
38
+ throw new Error(
39
+ `Domain security violation: Tool "${
40
+ tool.name
41
+ }" belongs to domain "${
42
+ tool.domainName
43
+ }" which is not allowed in this route. Allowed domains: [${allowedDomains.join(
44
+ ", "
45
+ )}]`
46
+ );
47
+ }
48
+ }
49
+
50
+ // Build tool context with extracted data
51
+ const toolContext: ToolContext<TContext, TExtracted> = {
52
+ context,
53
+ updateContext,
54
+ history,
55
+ extracted,
56
+ };
57
+
58
+ // Execute tool (no arguments - tools read from context/extracted)
59
+ const result = await tool.handler(toolContext);
60
+
61
+ // Return execution result
62
+ return {
63
+ toolName: tool.name,
64
+ success: true,
65
+ data: result.data,
66
+ contextUpdate: result.contextUpdate,
67
+ extractedUpdate: result.extractedUpdate,
68
+ };
69
+ } catch (error) {
70
+ return {
71
+ toolName: tool.name,
72
+ success: false,
73
+ error: error instanceof Error ? error.message : String(error),
74
+ };
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Execute multiple tools in sequence
80
+ * @param allowedDomains - Array of domain names allowed for this execution context (undefined = all domains allowed)
81
+ */
82
+ async executeTools(
83
+ tools: Array<ToolRef<TContext, unknown[], unknown>>,
84
+ context: TContext,
85
+ updateContext: (updates: Partial<TContext>) => Promise<void>,
86
+ history: Event[],
87
+ extracted?: Partial<TExtracted>,
88
+ allowedDomains?: string[]
89
+ ): Promise<ToolExecutionResult[]> {
90
+ const results: ToolExecutionResult[] = [];
91
+
92
+ for (const tool of tools) {
93
+ const result = await this.executeTool(
94
+ tool,
95
+ context,
96
+ updateContext,
97
+ history,
98
+ extracted,
99
+ allowedDomains
100
+ );
101
+ results.push(result);
102
+
103
+ // If tool failed, stop execution chain
104
+ if (!result.success) {
105
+ console.error(`[ToolExecutor] Tool ${tool.name} failed:`, result.error);
106
+ break;
107
+ }
108
+
109
+ // Apply context updates from tool result
110
+ if (result.contextUpdate) {
111
+ await updateContext(result.contextUpdate as Partial<TContext>);
112
+ }
113
+ }
114
+
115
+ return results;
116
+ }
117
+ }
@@ -8,26 +8,26 @@ import type { State } from "./State";
8
8
  /**
9
9
  * Represents a transition from one state to another
10
10
  */
11
- export class Transition<TContext = unknown> {
12
- private target?: State<TContext>;
11
+ export class Transition<TContext = unknown, TExtracted = unknown> {
12
+ private target?: State<TContext, TExtracted>;
13
13
 
14
14
  constructor(
15
15
  public readonly source: StateRef,
16
- public readonly spec: TransitionSpec<TContext>,
16
+ public readonly spec: TransitionSpec<TContext, TExtracted>,
17
17
  public readonly condition?: string
18
18
  ) {}
19
19
 
20
20
  /**
21
21
  * Set the target state for this transition
22
22
  */
23
- setTarget(state: State<TContext>): void {
23
+ setTarget(state: State<TContext, TExtracted>): void {
24
24
  this.target = state;
25
25
  }
26
26
 
27
27
  /**
28
28
  * Get the target state
29
29
  */
30
- getTarget(): State<TContext> | undefined {
30
+ getTarget(): State<TContext, TExtracted> | undefined {
31
31
  return this.target;
32
32
  }
33
33
 
package/src/index.ts CHANGED
@@ -9,14 +9,12 @@ export { Agent } from "./core/Agent";
9
9
  export { Route } from "./core/Route";
10
10
  export { State } from "./core/State";
11
11
  export { Transition } from "./core/Transition";
12
- export { Observation } from "./core/Observation";
13
12
  export { defineTool } from "./core/Tool";
14
13
  export { DomainRegistry } from "./core/DomainRegistry";
15
14
  export { adaptEvent, createMessageEvent, createToolEvent } from "./core/Events";
16
- export { PromptBuilder } from "./core/PromptBuilder";
17
- export type { Customer, AgentInfo } from "./core/PromptBuilder";
18
- export { BuiltInSection } from "./core/PromptBuilder";
19
15
  export { PersistenceManager } from "./core/PersistenceManager";
16
+ export { ToolExecutor } from "./core/ToolExecutor";
17
+ export type { ToolExecutionResult } from "./core/ToolExecutor";
20
18
 
21
19
  // Providers
22
20
  export { GeminiProvider } from "./providers/GeminiProvider";
@@ -67,12 +65,7 @@ export type {
67
65
  export { END_ROUTE } from "./constants";
68
66
 
69
67
  // Utils
70
- export {
71
- generateRouteId,
72
- generateStateId,
73
- generateObservationId,
74
- generateToolId,
75
- } from "./utils/id";
68
+ export { generateRouteId, generateStateId, generateToolId } from "./utils/id";
76
69
 
77
70
  // Types
78
71
  export type {
@@ -104,6 +97,14 @@ export type {
104
97
  TransitionResult,
105
98
  } from "./types/route";
106
99
 
100
+ export type { SessionState } from "./types/session";
101
+ export {
102
+ createSession,
103
+ enterRoute,
104
+ enterState,
105
+ mergeExtracted,
106
+ } from "./types/session";
107
+
107
108
  export type {
108
109
  ToolContext,
109
110
  ToolResult,
@@ -119,17 +120,7 @@ export type {
119
120
  ReasoningConfig,
120
121
  } from "./types/ai";
121
122
 
122
- export type {
123
- PromptSection,
124
- ContextVariable,
125
- ContextVariableValue,
126
- } from "./types/prompt";
127
- export { SectionStatus } from "./types/prompt";
128
-
129
- export type {
130
- Observation as IObservation,
131
- ObservationOptions,
132
- } from "./types/observation";
123
+ export type { StructuredSchema } from "./types/schema";
133
124
 
134
125
  export type {
135
126
  SessionData,
@@ -258,13 +258,10 @@ export class AnthropicProvider implements AiProvider {
258
258
  ...this.config,
259
259
  };
260
260
 
261
- // Handle JSON mode if requested
262
- // Note: Anthropic doesn't have a native JSON mode like OpenAI,
263
- // but we can add it to the system prompt
264
- if (input.parameters?.jsonMode) {
265
- // Add system message requesting JSON output
261
+ // Handle schema: Anthropic doesn't have a native schema mode, so embed constraints
262
+ if (input.parameters?.jsonSchema) {
266
263
  const systemPrompt =
267
- "You must respond with valid JSON only. Do not include any text outside the JSON structure.";
264
+ "You must respond with valid JSON only and it MUST match the provided schema.";
268
265
 
269
266
  // Merge with existing system if present
270
267
  if (typeof this.config?.system === "string") {
@@ -294,9 +291,9 @@ export class AnthropicProvider implements AiProvider {
294
291
  throw new Error("No response from Anthropic");
295
292
  }
296
293
 
297
- // Parse JSON response if JSON mode was enabled
294
+ // Parse JSON response if schema was provided
298
295
  let structured: AgentStructuredResponse | undefined;
299
- if (input.parameters?.jsonMode) {
296
+ if (input.parameters?.jsonSchema) {
300
297
  try {
301
298
  structured = JSON.parse(message) as AgentStructuredResponse;
302
299
  } catch (error) {
@@ -418,10 +415,10 @@ export class AnthropicProvider implements AiProvider {
418
415
  ...this.config,
419
416
  };
420
417
 
421
- // Handle JSON mode if requested
422
- if (input.parameters?.jsonMode) {
418
+ // Handle schema in streaming: embed constraint
419
+ if (input.parameters?.jsonSchema) {
423
420
  const systemPrompt =
424
- "You must respond with valid JSON only. Do not include any text outside the JSON structure.";
421
+ "You must respond with valid JSON only and it MUST match the provided schema.";
425
422
 
426
423
  if (typeof this.config?.system === "string") {
427
424
  params.system = `${this.config.system}\n\n${systemPrompt}`;
@@ -466,9 +463,9 @@ export class AnthropicProvider implements AiProvider {
466
463
  }
467
464
  }
468
465
 
469
- // Parse JSON response if JSON mode was enabled
466
+ // Parse JSON response if schema was provided
470
467
  let structured: AgentStructuredResponse | undefined;
471
- if (input.parameters?.jsonMode && accumulated) {
468
+ if (input.parameters?.jsonSchema && accumulated) {
472
469
  try {
473
470
  structured = JSON.parse(accumulated) as AgentStructuredResponse;
474
471
  } catch (error) {
@@ -235,10 +235,13 @@ export class GeminiProvider implements AiProvider {
235
235
  input: GenerateMessageInput<TContext>
236
236
  ): Promise<GenerateMessageOutput<TStructured>> {
237
237
  const operation = async (): Promise<GenerateMessageOutput> => {
238
- // Enable JSON mode if requested
238
+ // Schema-required: configure response schema
239
239
  const configOverride: Partial<GenerateContentConfig> = { ...this.config };
240
- if (input.parameters?.jsonMode) {
240
+ if (input.parameters?.jsonSchema) {
241
241
  configOverride.responseMimeType = "application/json";
242
+ // Gemini expects responseSchema. We pass through our schema as-is.
243
+ // A deeper mapping can be added via utils if needed.
244
+ configOverride.responseSchema = input.parameters.jsonSchema;
242
245
  }
243
246
 
244
247
  const response: GenerateContentResponse =
@@ -253,9 +256,9 @@ export class GeminiProvider implements AiProvider {
253
256
  throw new Error("No response from Gemini");
254
257
  }
255
258
 
256
- // Parse JSON response if JSON mode was enabled
259
+ // Parse JSON response if schema was provided
257
260
  let structured: AgentStructuredResponse | undefined;
258
- if (input.parameters?.jsonMode) {
261
+ if (input.parameters?.jsonSchema) {
259
262
  try {
260
263
  structured = JSON.parse(message) as AgentStructuredResponse;
261
264
  } catch (error) {
@@ -353,10 +356,11 @@ export class GeminiProvider implements AiProvider {
353
356
  model: string,
354
357
  input: GenerateMessageInput<TContext>
355
358
  ): AsyncGenerator<GenerateMessageStreamChunk<TStructured>> {
356
- // Enable JSON mode if requested
359
+ // Streaming: request JSON if schema provided
357
360
  const configOverride: Partial<GenerateContentConfig> = { ...this.config };
358
- if (input.parameters?.jsonMode) {
361
+ if (input.parameters?.jsonSchema) {
359
362
  configOverride.responseMimeType = "application/json";
363
+ configOverride.responseSchema = input.parameters.jsonSchema;
360
364
  }
361
365
 
362
366
  const stream = await this.genAI.models.generateContentStream({
@@ -390,9 +394,9 @@ export class GeminiProvider implements AiProvider {
390
394
  }
391
395
  }
392
396
 
393
- // Parse JSON response if JSON mode was enabled
397
+ // Parse JSON response if schema was provided
394
398
  let structured: AgentStructuredResponse | undefined;
395
- if (input.parameters?.jsonMode && accumulated) {
399
+ if (input.parameters?.jsonSchema && accumulated) {
396
400
  try {
397
401
  structured = JSON.parse(accumulated) as AgentStructuredResponse;
398
402
  } catch (error) {
@@ -266,53 +266,8 @@ export class OpenAIProvider implements AiProvider {
266
266
  params.max_tokens = input.parameters.maxOutputTokens;
267
267
  }
268
268
 
269
- // Use structured output API if JSON mode is enabled
270
- if (input.parameters?.jsonMode) {
271
- // Define the JSON schema for agent response
272
- const agentResponseSchema = {
273
- type: "object",
274
- properties: {
275
- message: {
276
- type: "string",
277
- description: "The actual message to send to the user",
278
- },
279
- route: {
280
- type: ["string", "null"],
281
- description:
282
- "The title of the route chosen (or null if no specific route)",
283
- },
284
- state: {
285
- type: ["string", "null"],
286
- description:
287
- "The current state within the route (or null if not in a route)",
288
- },
289
- toolCalls: {
290
- type: "array",
291
- items: {
292
- type: "object",
293
- properties: {
294
- toolName: {
295
- type: "string",
296
- description: "Name of the tool to call",
297
- },
298
- arguments: {
299
- type: "object",
300
- description: "Arguments to pass to the tool",
301
- },
302
- },
303
- required: ["toolName", "arguments"],
304
- },
305
- description: "Tool calls the agent wants to execute",
306
- },
307
- reasoning: {
308
- type: "string",
309
- description: "Optional: Internal reasoning for this response",
310
- },
311
- },
312
- required: ["message"],
313
- additionalProperties: false,
314
- };
315
-
269
+ // Use structured output API if JSON schema is provided
270
+ if (input.parameters?.jsonSchema) {
316
271
  const response = await this.client.responses.parse({
317
272
  model,
318
273
  instructions: input.prompt,
@@ -323,8 +278,8 @@ export class OpenAIProvider implements AiProvider {
323
278
  text: {
324
279
  format: {
325
280
  type: "json_schema",
326
- name: "agentResponseSchema",
327
- schema: agentResponseSchema,
281
+ name: input.parameters?.schemaName || "structured_output",
282
+ schema: input.parameters.jsonSchema,
328
283
  },
329
284
  },
330
285
  });
@@ -348,7 +303,7 @@ export class OpenAIProvider implements AiProvider {
348
303
  };
349
304
  }
350
305
 
351
- // Fall back to regular chat completions API if JSON mode not enabled
306
+ // Fall back to regular chat completions API if no schema provided
352
307
  const response = await this.client.chat.completions.create(params);
353
308
 
354
309
  const message = response.choices[0]?.message?.content;
@@ -468,10 +423,9 @@ export class OpenAIProvider implements AiProvider {
468
423
  params.max_tokens = input.parameters.maxOutputTokens;
469
424
  }
470
425
 
471
- // Use JSON mode if requested
472
- // Note: OpenAI streaming doesn't support the responses.parse API,
473
- // so we use response_format with JSON mode instead
474
- if (input.parameters?.jsonMode) {
426
+ // Streaming path does not support responses.parse; if schema present,
427
+ // request JSON object and parse at the end.
428
+ if (input.parameters?.jsonSchema) {
475
429
  params.response_format = { type: "json_object" };
476
430
  }
477
431
 
@@ -509,9 +463,9 @@ export class OpenAIProvider implements AiProvider {
509
463
  }
510
464
  }
511
465
 
512
- // Parse JSON response if JSON mode was enabled
466
+ // Parse JSON response if schema was provided
513
467
  let structured: TStructured | undefined;
514
- if (input.parameters?.jsonMode && accumulated) {
468
+ if (input.parameters?.jsonSchema && accumulated) {
515
469
  try {
516
470
  structured = JSON.parse(accumulated) as TStructured;
517
471
  } catch (error) {
@@ -273,53 +273,8 @@ export class OpenRouterProvider implements AiProvider {
273
273
  params.max_tokens = input.parameters.maxOutputTokens;
274
274
  }
275
275
 
276
- // Use structured output API if JSON mode is enabled
277
- if (input.parameters?.jsonMode) {
278
- // Define the JSON schema for agent response
279
- const agentResponseSchema = {
280
- type: "object",
281
- properties: {
282
- message: {
283
- type: "string",
284
- description: "The actual message to send to the user",
285
- },
286
- route: {
287
- type: ["string", "null"],
288
- description:
289
- "The title of the route chosen (or null if no specific route)",
290
- },
291
- state: {
292
- type: ["string", "null"],
293
- description:
294
- "The current state within the route (or null if not in a route)",
295
- },
296
- toolCalls: {
297
- type: "array",
298
- items: {
299
- type: "object",
300
- properties: {
301
- toolName: {
302
- type: "string",
303
- description: "Name of the tool to call",
304
- },
305
- arguments: {
306
- type: "object",
307
- description: "Arguments to pass to the tool",
308
- },
309
- },
310
- required: ["toolName", "arguments"],
311
- },
312
- description: "Tool calls the agent wants to execute",
313
- },
314
- reasoning: {
315
- type: "string",
316
- description: "Optional: Internal reasoning for this response",
317
- },
318
- },
319
- required: ["message"],
320
- additionalProperties: false,
321
- };
322
-
276
+ // Use structured output API if JSON schema is provided
277
+ if (input.parameters?.jsonSchema) {
323
278
  const response = await this.client.responses.parse({
324
279
  model,
325
280
  instructions: input.prompt,
@@ -330,8 +285,8 @@ export class OpenRouterProvider implements AiProvider {
330
285
  text: {
331
286
  format: {
332
287
  type: "json_schema",
333
- name: "agentResponseSchema",
334
- schema: agentResponseSchema,
288
+ name: input.parameters?.schemaName || "structured_output",
289
+ schema: input.parameters.jsonSchema,
335
290
  },
336
291
  },
337
292
  });
@@ -355,7 +310,7 @@ export class OpenRouterProvider implements AiProvider {
355
310
  };
356
311
  }
357
312
 
358
- // Fall back to regular chat completions API if JSON mode not enabled
313
+ // Fall back to regular chat completions API if no schema provided
359
314
  const response = await this.client.chat.completions.create(params);
360
315
 
361
316
  const message = response.choices[0]?.message?.content;
@@ -469,10 +424,9 @@ export class OpenRouterProvider implements AiProvider {
469
424
  params.max_tokens = input.parameters.maxOutputTokens;
470
425
  }
471
426
 
472
- // Use JSON mode if requested
473
- // Note: OpenRouter streaming doesn't support the responses.parse API,
474
- // so we use response_format with JSON mode instead
475
- if (input.parameters?.jsonMode) {
427
+ // Streaming path does not support responses.parse; if schema present,
428
+ // request JSON object and parse at the end.
429
+ if (input.parameters?.jsonSchema) {
476
430
  params.response_format = { type: "json_object" };
477
431
  }
478
432
 
@@ -510,9 +464,9 @@ export class OpenRouterProvider implements AiProvider {
510
464
  }
511
465
  }
512
466
 
513
- // Parse JSON response if JSON mode was enabled
467
+ // Parse JSON response if schema was provided
514
468
  let structured: TStructured | undefined;
515
- if (input.parameters?.jsonMode && accumulated) {
469
+ if (input.parameters?.jsonSchema && accumulated) {
516
470
  try {
517
471
  structured = JSON.parse(accumulated) as TStructured;
518
472
  } catch (error) {