@falai/agent 0.6.3 → 0.6.5

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 (137) hide show
  1. package/README.md +89 -29
  2. package/dist/cjs/constants/index.d.ts +6 -1
  3. package/dist/cjs/constants/index.d.ts.map +1 -1
  4. package/dist/cjs/constants/index.js +8 -3
  5. package/dist/cjs/constants/index.js.map +1 -1
  6. package/dist/cjs/core/Agent.d.ts +22 -0
  7. package/dist/cjs/core/Agent.d.ts.map +1 -1
  8. package/dist/cjs/core/Agent.js +113 -26
  9. package/dist/cjs/core/Agent.js.map +1 -1
  10. package/dist/cjs/core/Events.d.ts +13 -0
  11. package/dist/cjs/core/Events.d.ts.map +1 -1
  12. package/dist/cjs/core/Events.js +28 -14
  13. package/dist/cjs/core/Events.js.map +1 -1
  14. package/dist/cjs/core/ResponseEngine.d.ts +1 -1
  15. package/dist/cjs/core/ResponseEngine.d.ts.map +1 -1
  16. package/dist/cjs/core/ResponseEngine.js +4 -1
  17. package/dist/cjs/core/ResponseEngine.js.map +1 -1
  18. package/dist/cjs/core/Route.d.ts.map +1 -1
  19. package/dist/cjs/core/Route.js +4 -4
  20. package/dist/cjs/core/Route.js.map +1 -1
  21. package/dist/cjs/core/RoutingEngine.d.ts +6 -1
  22. package/dist/cjs/core/RoutingEngine.d.ts.map +1 -1
  23. package/dist/cjs/core/RoutingEngine.js +116 -41
  24. package/dist/cjs/core/RoutingEngine.js.map +1 -1
  25. package/dist/cjs/core/State.d.ts +18 -6
  26. package/dist/cjs/core/State.d.ts.map +1 -1
  27. package/dist/cjs/core/State.js +30 -7
  28. package/dist/cjs/core/State.js.map +1 -1
  29. package/dist/cjs/core/Tool.d.ts +8 -1
  30. package/dist/cjs/core/Tool.d.ts.map +1 -1
  31. package/dist/cjs/core/Tool.js +25 -28
  32. package/dist/cjs/core/Tool.js.map +1 -1
  33. package/dist/cjs/core/Transition.js +1 -1
  34. package/dist/cjs/index.d.ts +1 -1
  35. package/dist/cjs/index.d.ts.map +1 -1
  36. package/dist/cjs/index.js +3 -2
  37. package/dist/cjs/index.js.map +1 -1
  38. package/dist/cjs/types/agent.d.ts +5 -0
  39. package/dist/cjs/types/agent.d.ts.map +1 -1
  40. package/dist/cjs/types/agent.js.map +1 -1
  41. package/dist/cjs/types/route.d.ts +7 -1
  42. package/dist/cjs/types/route.d.ts.map +1 -1
  43. package/dist/cjs/types/session.d.ts +13 -2
  44. package/dist/cjs/types/session.d.ts.map +1 -1
  45. package/dist/cjs/types/session.js +28 -5
  46. package/dist/cjs/types/session.js.map +1 -1
  47. package/dist/cjs/utils/logger.d.ts +10 -0
  48. package/dist/cjs/utils/logger.d.ts.map +1 -0
  49. package/dist/cjs/utils/logger.js +23 -0
  50. package/dist/cjs/utils/logger.js.map +1 -0
  51. package/dist/constants/index.d.ts +6 -1
  52. package/dist/constants/index.d.ts.map +1 -1
  53. package/dist/constants/index.js +6 -1
  54. package/dist/constants/index.js.map +1 -1
  55. package/dist/core/Agent.d.ts +22 -0
  56. package/dist/core/Agent.d.ts.map +1 -1
  57. package/dist/core/Agent.js +113 -26
  58. package/dist/core/Agent.js.map +1 -1
  59. package/dist/core/Events.d.ts +13 -0
  60. package/dist/core/Events.d.ts.map +1 -1
  61. package/dist/core/Events.js +28 -14
  62. package/dist/core/Events.js.map +1 -1
  63. package/dist/core/ResponseEngine.d.ts +1 -1
  64. package/dist/core/ResponseEngine.d.ts.map +1 -1
  65. package/dist/core/ResponseEngine.js +4 -1
  66. package/dist/core/ResponseEngine.js.map +1 -1
  67. package/dist/core/Route.d.ts.map +1 -1
  68. package/dist/core/Route.js +4 -4
  69. package/dist/core/Route.js.map +1 -1
  70. package/dist/core/RoutingEngine.d.ts +6 -1
  71. package/dist/core/RoutingEngine.d.ts.map +1 -1
  72. package/dist/core/RoutingEngine.js +116 -41
  73. package/dist/core/RoutingEngine.js.map +1 -1
  74. package/dist/core/State.d.ts +18 -6
  75. package/dist/core/State.d.ts.map +1 -1
  76. package/dist/core/State.js +31 -8
  77. package/dist/core/State.js.map +1 -1
  78. package/dist/core/Tool.d.ts +8 -1
  79. package/dist/core/Tool.d.ts.map +1 -1
  80. package/dist/core/Tool.js +25 -28
  81. package/dist/core/Tool.js.map +1 -1
  82. package/dist/core/Transition.js +1 -1
  83. package/dist/index.d.ts +1 -1
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +1 -1
  86. package/dist/index.js.map +1 -1
  87. package/dist/types/agent.d.ts +5 -0
  88. package/dist/types/agent.d.ts.map +1 -1
  89. package/dist/types/agent.js.map +1 -1
  90. package/dist/types/route.d.ts +7 -1
  91. package/dist/types/route.d.ts.map +1 -1
  92. package/dist/types/session.d.ts +13 -2
  93. package/dist/types/session.d.ts.map +1 -1
  94. package/dist/types/session.js +28 -5
  95. package/dist/types/session.js.map +1 -1
  96. package/dist/utils/logger.d.ts +10 -0
  97. package/dist/utils/logger.d.ts.map +1 -0
  98. package/dist/utils/logger.js +17 -0
  99. package/dist/utils/logger.js.map +1 -0
  100. package/docs/{CONSTRUCTOR_OPTIONS.md → AGENT.md} +79 -7
  101. package/docs/API_REFERENCE.md +309 -18
  102. package/docs/ARCHITECTURE.md +1 -1
  103. package/docs/DOCS.md +46 -22
  104. package/docs/GETTING_STARTED.md +1 -1
  105. package/docs/README.md +13 -5
  106. package/docs/ROUTES.md +743 -0
  107. package/docs/STATES.md +798 -0
  108. package/examples/business-onboarding.ts +46 -5
  109. package/examples/company-qna-agent.ts +107 -1
  110. package/examples/custom-database-persistence.ts +44 -1
  111. package/examples/declarative-agent.ts +80 -37
  112. package/examples/domain-scoping.ts +91 -21
  113. package/examples/extracted-data-modification.ts +64 -2
  114. package/examples/healthcare-agent.ts +61 -4
  115. package/examples/openai-agent.ts +24 -2
  116. package/examples/opensearch-persistence.ts +26 -1
  117. package/examples/persistent-onboarding.ts +84 -18
  118. package/examples/prisma-persistence.ts +90 -16
  119. package/examples/redis-persistence.ts +89 -17
  120. package/examples/rules-prohibitions.ts +300 -139
  121. package/examples/streaming-agent.ts +60 -0
  122. package/examples/travel-agent.ts +66 -24
  123. package/package.json +3 -2
  124. package/src/constants/index.ts +6 -1
  125. package/src/core/Agent.ts +140 -24
  126. package/src/core/Events.ts +73 -10
  127. package/src/core/ResponseEngine.ts +6 -0
  128. package/src/core/Route.ts +8 -4
  129. package/src/core/RoutingEngine.ts +154 -43
  130. package/src/core/State.ts +45 -12
  131. package/src/core/Tool.ts +67 -10
  132. package/src/core/Transition.ts +1 -1
  133. package/src/index.ts +1 -1
  134. package/src/types/agent.ts +5 -0
  135. package/src/types/route.ts +10 -1
  136. package/src/types/session.ts +47 -7
  137. package/src/utils/logger.ts +19 -0
@@ -9,7 +9,14 @@
9
9
  * 5. Three-phase pipeline: PREPARATION → ROUTING → RESPONSE
10
10
  */
11
11
 
12
- import { Agent, createSession, EventSource, createMessageEvent } from "../src";
12
+ import {
13
+ Agent,
14
+ createSession,
15
+ EventSource,
16
+ createMessageEvent,
17
+ END_STATE,
18
+ OpenAIProvider,
19
+ } from "../src";
13
20
  import type { Event } from "../src/types";
14
21
  import type { ToolRef } from "../src/types/tool";
15
22
 
@@ -179,6 +186,20 @@ const searchFlightsTool: ToolRef<FlightBookingContext, [], void, FlightData> = {
179
186
  },
180
187
  };
181
188
 
189
+ // Tool 4: Book the flight
190
+ const bookFlightTool: ToolRef<FlightBookingContext, [], void, FlightData> = {
191
+ id: "book_flight",
192
+ name: "Book Flight",
193
+ description: "Finalize the flight booking",
194
+ handler: async (context) => {
195
+ const { extracted } = context;
196
+ const flightData = extracted as Partial<FlightData>;
197
+ console.log("[Tool] Booking flight with data:", flightData);
198
+ // Simulate booking API call
199
+ return { data: undefined };
200
+ },
201
+ };
202
+
182
203
  // ==============================================================================
183
204
  // LIFECYCLE HOOKS: Data Validation & Business Logic (RESPONSE Phase)
184
205
  // ==============================================================================
@@ -236,7 +257,10 @@ const agent = new Agent<FlightBookingContext>({
236
257
  name: "Flight Booking Agent",
237
258
  goal: "Help users book flights efficiently",
238
259
  description: "I help you find and book flights",
239
- ai: null as any, // Replace with actual AI provider
260
+ ai: new OpenAIProvider({
261
+ apiKey: process.env.OPENAI_API_KEY || "your-api-key-here",
262
+ model: "gpt-5o-mini",
263
+ }),
240
264
  context: {},
241
265
  hooks: {
242
266
  onExtractedUpdate, // Validation & enrichment hook
@@ -334,6 +358,21 @@ const presentResults = searchFlights.transitionTo({
334
358
  chatState: "Present available flights to the user",
335
359
  });
336
360
 
361
+ // State 8: Confirm booking
362
+ const confirmBooking = presentResults.transitionTo({
363
+ chatState: "Ask user to confirm the booking",
364
+ requiredData: ["destinationCode", "departureDateParsed", "passengers"],
365
+ });
366
+
367
+ // State 9: Finalize booking
368
+ const finalizeBooking = confirmBooking.transitionTo({
369
+ toolState: bookFlightTool,
370
+ condition: "User confirms the booking",
371
+ });
372
+
373
+ // State 10: End of conversation
374
+ finalizeBooking.transitionTo({ state: END_STATE });
375
+
337
376
  // ==============================================================================
338
377
  // USAGE EXAMPLE: Three-Phase Pipeline Demonstration
339
378
  // ==============================================================================
@@ -357,6 +396,11 @@ async function main() {
357
396
  console.log("Extracted:", response.session?.extracted);
358
397
  console.log("Context:", agent["context"]);
359
398
 
399
+ if (response.isRouteComplete) {
400
+ console.log("\n✅ Flight booking complete!");
401
+ await sendBookingConfirmation(response.session?.extracted);
402
+ }
403
+
360
404
  /*
361
405
  * Expected flow:
362
406
  * 1. AI extracts: { destination: "Paris", departureDate: "tomorrow", passengers: 2 }
@@ -374,6 +418,24 @@ async function main() {
374
418
  */
375
419
  }
376
420
 
421
+ /**
422
+ * Mock function to send a booking confirmation.
423
+ * @param data - The flight booking data.
424
+ */
425
+ async function sendBookingConfirmation(data: Partial<FlightData> | undefined) {
426
+ console.log("\n" + "=".repeat(60));
427
+ console.log("🚀 Sending Booking Confirmation...");
428
+ console.log("=".repeat(60));
429
+ console.log("Booking Details:", JSON.stringify(data, null, 2));
430
+ console.log(
431
+ ` - Sending confirmation for flight to ${data?.destinationCode} on ${
432
+ data?.departureDateParsed ?? ""
433
+ }.`
434
+ );
435
+ await new Promise((resolve) => setTimeout(resolve, 1000));
436
+ console.log("✨ Confirmation sent!");
437
+ }
438
+
377
439
  // ==============================================================================
378
440
  // KEY TAKEAWAYS: Architecture Principles Demonstrated
379
441
  // ==============================================================================
@@ -7,7 +7,7 @@ import {
7
7
  Agent,
8
8
  defineTool,
9
9
  AnthropicProvider,
10
- END_ROUTE,
10
+ END_STATE,
11
11
  EventSource,
12
12
  createMessageEvent,
13
13
  createSession,
@@ -213,7 +213,7 @@ async function createHealthcareAgent() {
213
213
  });
214
214
 
215
215
  confirmation.transitionTo({
216
- state: END_ROUTE,
216
+ state: END_STATE,
217
217
  condition: "Appointment booked successfully",
218
218
  });
219
219
 
@@ -232,7 +232,7 @@ async function createHealthcareAgent() {
232
232
  chatState:
233
233
  "Ask the patient to call the office to schedule an appointment",
234
234
  })
235
- .transitionTo({ state: END_ROUTE });
235
+ .transitionTo({ state: END_STATE });
236
236
 
237
237
  schedulingRoute.createGuideline({
238
238
  condition: "The patient says their visit is urgent",
@@ -291,7 +291,7 @@ async function createHealthcareAgent() {
291
291
  chatState: "Present the lab results and explain what they mean",
292
292
  });
293
293
 
294
- presentResults.transitionTo({ state: END_ROUTE });
294
+ presentResults.transitionTo({ state: END_STATE });
295
295
 
296
296
  labResultsRoute.createGuideline({
297
297
  condition:
@@ -383,9 +383,66 @@ async function main() {
383
383
 
384
384
  // Update session again
385
385
  session = response2.session!;
386
+
387
+ // Turn 3: Patient provides final details
388
+ const history3 = [
389
+ ...history2,
390
+ createMessageEvent(EventSource.AI_AGENT, "Agent", response2.message),
391
+ createMessageEvent(
392
+ EventSource.CUSTOMER,
393
+ "Patient",
394
+ "Tuesday at 2 PM works for me."
395
+ ),
396
+ ];
397
+
398
+ const response3 = await agent.respond({ history: history3, session });
399
+ console.log("\nPatient: Tuesday at 2 PM works for me.");
400
+ console.log("Agent:", response3.message);
401
+ console.log("Updated extracted:", response3.session?.extracted);
402
+ console.log("Current state:", response3.session?.currentState?.id);
403
+
404
+ // Check for route completion
405
+ if (response3.isRouteComplete) {
406
+ console.log("\n✅ Appointment scheduling complete!");
407
+ await sendAppointmentReminder(
408
+ agent.getExtractedData(
409
+ response3.session?.id
410
+ ) as unknown as AppointmentData
411
+ );
412
+ }
386
413
  }
387
414
  }
388
415
 
416
+ /**
417
+ * Mock function to send an appointment reminder.
418
+ * @param data - The appointment data.
419
+ */
420
+ async function sendAppointmentReminder(data: AppointmentData) {
421
+ console.log("\n" + "=".repeat(60));
422
+ console.log("🚀 Sending Appointment Reminder...");
423
+ console.log("=".repeat(60));
424
+ console.log("Appointment Details:", JSON.stringify(data, null, 2));
425
+ console.log(
426
+ ` - Sending reminder for ${data.appointmentReason} on ${data.preferredDate} at ${data.preferredTime}.`
427
+ );
428
+ await new Promise((resolve) => setTimeout(resolve, 1000));
429
+ console.log("✨ Reminder sent!");
430
+ }
431
+
432
+ /**
433
+ * Mock function to log a patient inquiry about lab results.
434
+ * @param data - The lab results data.
435
+ */
436
+ async function logPatientInquiry(data: LabResultsData) {
437
+ console.log("\n" + "=".repeat(60));
438
+ console.log("📝 Logging Patient Inquiry...");
439
+ console.log("=".repeat(60));
440
+ console.log("Inquiry Details:", JSON.stringify(data, null, 2));
441
+ console.log(` - Logging inquiry for ${data.testType} results.`);
442
+ await new Promise((resolve) => setTimeout(resolve, 1000));
443
+ console.log("✨ Inquiry logged!");
444
+ }
445
+
389
446
  if (import.meta.url === `file://${process.argv[1]}`) {
390
447
  main().catch(console.error);
391
448
  }
@@ -10,7 +10,7 @@ import {
10
10
  createMessageEvent,
11
11
  EventSource,
12
12
  createSession,
13
- END_ROUTE,
13
+ END_STATE,
14
14
  } from "../src/index";
15
15
 
16
16
  // Custom context type
@@ -151,7 +151,7 @@ async function main() {
151
151
  "Present the weather information in a friendly way with temperature and condition",
152
152
  });
153
153
 
154
- showWeather.transitionTo({ state: END_ROUTE });
154
+ showWeather.transitionTo({ state: END_STATE });
155
155
 
156
156
  // Example conversation with session state management
157
157
  console.log("🤖 Starting OpenAI Agent Example\n");
@@ -191,6 +191,14 @@ async function main() {
191
191
  // Update session with progress
192
192
  session = response.session!;
193
193
 
194
+ // Check for route completion
195
+ if (response.isRouteComplete) {
196
+ console.log("\n✅ Weather route complete!");
197
+ await logWeatherRequest(
198
+ agent.getExtractedData(session.id) as WeatherData
199
+ );
200
+ }
201
+
194
202
  console.log("\n✨ Session state benefits:");
195
203
  console.log(" ✅ Data extraction tracked across turns");
196
204
  console.log(" ✅ State progression managed automatically");
@@ -203,6 +211,20 @@ async function main() {
203
211
  }
204
212
  }
205
213
 
214
+ /**
215
+ * Mock function to log the weather request for analytics.
216
+ * @param data - The weather data from the completed route.
217
+ */
218
+ async function logWeatherRequest(data: WeatherData) {
219
+ console.log("\n" + "=".repeat(60));
220
+ console.log("📊 Logging Weather Request for Analytics...");
221
+ console.log("=".repeat(60));
222
+ console.log("Request Details:", JSON.stringify(data, null, 2));
223
+ console.log(` - Logging request for location: ${data.location}`);
224
+ await new Promise((resolve) => setTimeout(resolve, 500));
225
+ console.log("✨ Request logged!");
226
+ }
227
+
206
228
  // Run if executed directly
207
229
  if (import.meta.url === `file://${process.argv[1]}`) {
208
230
  main().catch(console.error);
@@ -16,6 +16,7 @@ import {
16
16
  EventSource,
17
17
  MessageEventData,
18
18
  Event,
19
+ END_STATE,
19
20
  } from "../src/index";
20
21
  // @ts-ignore
21
22
  import { Client } from "@opensearch-project/opensearch";
@@ -157,7 +158,8 @@ async function example() {
157
158
  .transitionTo({
158
159
  chatState: "Propose solution and close complaint",
159
160
  requiredData: ["category", "description"],
160
- });
161
+ })
162
+ .transitionTo({ state: END_STATE });
161
163
 
162
164
  const persistence = agent.getPersistenceManager();
163
165
  if (!persistence) return;
@@ -239,6 +241,13 @@ async function example() {
239
241
  content: response2.message,
240
242
  });
241
243
 
244
+ if (response2.isRouteComplete) {
245
+ console.log("\n✅ Complaint route complete!");
246
+ await createSupportTicket(
247
+ agent.getExtractedData(session.id) as ComplaintData
248
+ );
249
+ }
250
+
242
251
  // Load session from OpenSearch
243
252
  console.log("\n--- Loading Session from OpenSearch ---");
244
253
  const loadedSession = await persistence.loadSessionState<ComplaintData>(
@@ -471,6 +480,22 @@ async function timeSeriesExample() {
471
480
  }
472
481
  }
473
482
 
483
+ /**
484
+ * Mock function to create a support ticket.
485
+ * @param data - The complaint data from the completed route.
486
+ */
487
+ async function createSupportTicket(data: ComplaintData) {
488
+ console.log("\n" + "=".repeat(60));
489
+ console.log("🎫 Creating Support Ticket...");
490
+ console.log("=".repeat(60));
491
+ console.log("Ticket Details:", JSON.stringify(data, null, 2));
492
+ console.log(
493
+ ` - Creating ticket for category: ${data.category} with severity: ${data.severity}`
494
+ );
495
+ await new Promise((resolve) => setTimeout(resolve, 1000));
496
+ console.log("✨ Ticket created successfully!");
497
+ }
498
+
474
499
  // Run the example
475
500
  if (require.main === module) {
476
501
  example().catch(console.error);
@@ -7,7 +7,8 @@ import {
7
7
  Agent,
8
8
  defineTool,
9
9
  GeminiProvider,
10
- END_ROUTE,
10
+ END_STATE,
11
+ END_STATE_ID,
11
12
  EventSource,
12
13
  createMessageEvent,
13
14
  createSession,
@@ -269,24 +270,20 @@ async function createPersistentOnboardingAgent(sessionId: string) {
269
270
  });
270
271
 
271
272
  // State 1: Gather business name and description
272
- const gatherBusinessInfo = onboardingRoute.initialState.transitionTo(
273
- {
274
- chatState: "Ask for business name and a brief description",
275
- gather: ["businessName", "businessDescription"],
276
- skipIf: (extracted) =>
277
- !!extracted.businessName && !!extracted.businessDescription,
278
- },
279
- "Need to collect basic business information first"
280
- );
273
+ const gatherBusinessInfo = onboardingRoute.initialState.transitionTo({
274
+ chatState: "Ask for business name and a brief description",
275
+ gather: ["businessName", "businessDescription"],
276
+ skipIf: (extracted) =>
277
+ !!extracted.businessName && !!extracted.businessDescription,
278
+ condition: "Need to collect basic business information first",
279
+ });
281
280
 
282
281
  // State 2: Save business info (tool execution)
283
- const saveBusiness = gatherBusinessInfo.transitionTo(
284
- {
285
- toolState: saveBusinessInfo,
286
- requiredData: ["businessName", "businessDescription"],
287
- },
288
- "Business name and description provided, save to database"
289
- );
282
+ const saveBusiness = gatherBusinessInfo.transitionTo({
283
+ toolState: saveBusinessInfo,
284
+ requiredData: ["businessName", "businessDescription"],
285
+ condition: "Business name and description provided, save to database",
286
+ });
290
287
 
291
288
  // State 3: Gather industry
292
289
  const gatherIndustry = saveBusiness.transitionTo({
@@ -319,7 +316,7 @@ async function createPersistentOnboardingAgent(sessionId: string) {
319
316
  chatState: "Summarize all collected information and ask for confirmation",
320
317
  });
321
318
 
322
- confirm.transitionTo({ state: END_ROUTE });
319
+ confirm.transitionTo({ state: END_STATE });
323
320
 
324
321
  // Guidelines
325
322
  onboardingRoute.createGuideline({
@@ -438,6 +435,15 @@ async function main() {
438
435
  console.log("🤖 Bot:", response1.message);
439
436
  console.log("📊 Extracted after turn 1:", response1.session?.extracted);
440
437
  console.log("📊 Route:", response1.session?.currentRoute?.title);
438
+
439
+ // Check route completion after turn 1
440
+ console.log("🔍 Route Completion Check (Turn 1):");
441
+ if (response1.isRouteComplete) {
442
+ console.log(" ✅ Route completed after turn 1!");
443
+ } else {
444
+ console.log(" ⏳ Route still in progress after turn 1");
445
+ }
446
+
441
447
  console.log();
442
448
 
443
449
  // Update session with progress
@@ -461,6 +467,15 @@ async function main() {
461
467
  const response2 = await agent.respond({ history: history2, session });
462
468
  console.log("🤖 Bot:", response2.message);
463
469
  console.log("📊 Extracted after turn 2:", response2.session?.extracted);
470
+
471
+ // Check route completion after turn 2
472
+ console.log("🔍 Route Completion Check (Turn 2):");
473
+ if (response2.isRouteComplete) {
474
+ console.log(" ✅ Route completed after turn 2!");
475
+ } else {
476
+ console.log(" ⏳ Route still in progress after turn 2");
477
+ }
478
+
464
479
  console.log();
465
480
 
466
481
  // Update session again
@@ -480,8 +495,43 @@ async function main() {
480
495
  const response3 = await agent.respond({ history: history3, session });
481
496
  console.log("🤖 Bot:", response3.message);
482
497
  console.log("📊 Extracted after turn 3:", response3.session?.extracted);
498
+
499
+ // Check route completion after turn 3
500
+ console.log("🔍 Route Completion Check (Turn 3):");
501
+ if (response3.isRouteComplete) {
502
+ console.log(" ✅ Route completed after turn 3!");
503
+ } else {
504
+ console.log(" ⏳ Route still in progress after turn 3");
505
+ }
506
+
483
507
  console.log();
484
508
 
509
+ // Update session again
510
+ session = response3.session!;
511
+
512
+ // Turn 4: User provides contact email, completing the flow
513
+ console.log("📱 Turn 4: User provides contact email");
514
+ const history4 = [
515
+ ...history3,
516
+ createMessageEvent(EventSource.AI_AGENT, "Agent", response3.message),
517
+ createMessageEvent(
518
+ EventSource.CUSTOMER,
519
+ "Alice",
520
+ "Our contact email is contact@techflow.ai"
521
+ ),
522
+ ];
523
+ const response4 = await agent.respond({ history: history4, session });
524
+ console.log("🤖 Bot:", response4.message);
525
+ console.log("📊 Extracted after turn 4:", response4.session?.extracted);
526
+
527
+ // Check for route completion
528
+ if (response4.isRouteComplete) {
529
+ console.log("\n✅ Onboarding complete!");
530
+ await finalizeOnboarding(
531
+ agent.getExtractedData(response4.session?.id) as unknown as OnboardingData
532
+ );
533
+ }
534
+
485
535
  // Verify persistence
486
536
  console.log("=== PERSISTENCE VERIFICATION ===");
487
537
  const finalSession = await db.sessions.findById(sessionId);
@@ -529,6 +579,22 @@ async function main() {
529
579
  * - No more LLM interpretation of state logic
530
580
  */
531
581
 
582
+ /**
583
+ * Mock function to finalize the onboarding process.
584
+ * @param data - The complete onboarding data.
585
+ */
586
+ async function finalizeOnboarding(data: OnboardingData) {
587
+ console.log("\n" + "=".repeat(60));
588
+ console.log("🚀 Finalizing Onboarding...");
589
+ console.log("=".repeat(60));
590
+ console.log("Onboarding Details:", JSON.stringify(data, null, 2));
591
+ console.log(` - Sending welcome email to ${data.contactEmail}...`);
592
+ await new Promise((resolve) => setTimeout(resolve, 1000));
593
+ console.log(" - Scheduling follow-up call...");
594
+ await new Promise((resolve) => setTimeout(resolve, 500));
595
+ console.log("✨ Onboarding finalized!");
596
+ }
597
+
532
598
  if (import.meta.url === `file://${process.argv[1]}`) {
533
599
  main().catch(console.error);
534
600
  }
@@ -11,7 +11,7 @@ import {
11
11
  PrismaAdapter,
12
12
  createMessageEvent,
13
13
  EventSource,
14
- createSession,
14
+ END_STATE,
15
15
  } from "../src/index";
16
16
 
17
17
  // @ts-ignore
@@ -57,6 +57,21 @@ interface FlightBookingData {
57
57
  cabinClass: "economy" | "premium" | "business" | "first";
58
58
  }
59
59
 
60
+ // Extracted data type for onboarding
61
+ interface OnboardingData {
62
+ fullName: string;
63
+ email: string;
64
+ phoneNumber: string;
65
+ country: string;
66
+ }
67
+
68
+ // Extracted data type for contact form
69
+ interface ContactFormData {
70
+ name: string;
71
+ email: string;
72
+ message: string;
73
+ }
74
+
60
75
  async function example() {
61
76
  // Initialize Prisma client
62
77
  const prisma = new PrismaClient();
@@ -166,6 +181,8 @@ async function example() {
166
181
  requiredData: ["destination", "departureDate", "passengers", "cabinClass"],
167
182
  });
168
183
 
184
+ confirmBooking.transitionTo({ state: END_STATE });
185
+
169
186
  /**
170
187
  * Get persistence manager from agent
171
188
  */
@@ -305,6 +322,13 @@ async function example() {
305
322
 
306
323
  session = response2.session!;
307
324
 
325
+ if (response2.isRouteComplete) {
326
+ console.log("\n✅ Flight booking complete!");
327
+ await sendFlightConfirmation(
328
+ agent.getExtractedData(session.id) as FlightBookingData
329
+ );
330
+ }
331
+
308
332
  /**
309
333
  * Load session state from database (demonstrates persistence)
310
334
  */
@@ -356,13 +380,6 @@ async function advancedExample() {
356
380
  };
357
381
  }
358
382
 
359
- interface OnboardingData {
360
- fullName: string;
361
- email: string;
362
- phoneNumber: string;
363
- country: string;
364
- }
365
-
366
383
  const agent = new Agent<UserContext>({
367
384
  name: "Onboarding Assistant",
368
385
  description: "Help new users get started",
@@ -447,7 +464,8 @@ async function advancedExample() {
447
464
  })
448
465
  .transitionTo({
449
466
  chatState: "Confirm and complete onboarding",
450
- });
467
+ })
468
+ .transitionTo({ state: END_STATE });
451
469
 
452
470
  const persistence = agent.getPersistenceManager()!;
453
471
 
@@ -481,6 +499,13 @@ async function advancedExample() {
481
499
  content: response.message,
482
500
  });
483
501
 
502
+ if (response.isRouteComplete) {
503
+ console.log("\n✅ Onboarding complete!");
504
+ await sendOnboardingEmail(
505
+ agent.getExtractedData(sessionData.id) as OnboardingData
506
+ );
507
+ }
508
+
484
509
  console.log("✅ Session state automatically saved to database!");
485
510
 
486
511
  await prisma.$disconnect();
@@ -492,12 +517,6 @@ async function advancedExample() {
492
517
  async function quickStart() {
493
518
  const prisma = new PrismaClient();
494
519
 
495
- interface ContactFormData {
496
- name: string;
497
- email: string;
498
- message: string;
499
- }
500
-
501
520
  const agent = new Agent({
502
521
  name: "Support Agent",
503
522
  ai: new GeminiProvider({
@@ -532,7 +551,8 @@ async function quickStart() {
532
551
  })
533
552
  .transitionTo({
534
553
  chatState: "Confirm submission",
535
- });
554
+ })
555
+ .transitionTo({ state: END_STATE });
536
556
 
537
557
  const persistence = agent.getPersistenceManager()!;
538
558
 
@@ -557,11 +577,65 @@ async function quickStart() {
557
577
 
558
578
  console.log("✅ Response:", response.message);
559
579
  console.log("📊 Extracted:", response.session?.extracted);
580
+
581
+ if (response.isRouteComplete) {
582
+ console.log("\n✅ Contact form submitted!");
583
+ await logContactForm(
584
+ agent.getExtractedData(sessionData.id) as ContactFormData
585
+ );
586
+ }
587
+
560
588
  console.log("💾 Session state auto-saved to Prisma!");
561
589
 
562
590
  await prisma.$disconnect();
563
591
  }
564
592
 
593
+ /**
594
+ * Mock function to send a flight confirmation email.
595
+ * @param data - The flight booking data.
596
+ */
597
+ async function sendFlightConfirmation(
598
+ data: Partial<FlightBookingData> | undefined
599
+ ) {
600
+ console.log("\n" + "=".repeat(60));
601
+ console.log("🚀 Sending Flight Confirmation...");
602
+ console.log("=".repeat(60));
603
+ console.log("Booking Details:", JSON.stringify(data, null, 2));
604
+ console.log(
605
+ ` - Sending confirmation for ${data?.passengers} passengers to ${data?.destination}.`
606
+ );
607
+ await new Promise((resolve) => setTimeout(resolve, 1000));
608
+ console.log("✨ Confirmation sent!");
609
+ }
610
+
611
+ /**
612
+ * Mock function to send an onboarding email.
613
+ * @param data - The onboarding data.
614
+ */
615
+ async function sendOnboardingEmail(data: Partial<OnboardingData> | undefined) {
616
+ console.log("\n" + "=".repeat(60));
617
+ console.log("🚀 Sending Onboarding Email...");
618
+ console.log("=".repeat(60));
619
+ console.log("Onboarding Details:", JSON.stringify(data, null, 2));
620
+ console.log(` - Sending welcome email to ${data?.email}.`);
621
+ await new Promise((resolve) => setTimeout(resolve, 1000));
622
+ console.log("✨ Email sent!");
623
+ }
624
+
625
+ /**
626
+ * Mock function to log a contact form submission.
627
+ * @param data - The contact form data.
628
+ */
629
+ async function logContactForm(data: Partial<ContactFormData> | undefined) {
630
+ console.log("\n" + "=".repeat(60));
631
+ console.log("📝 Logging Contact Form Submission...");
632
+ console.log("=".repeat(60));
633
+ console.log("Submission Details:", JSON.stringify(data, null, 2));
634
+ console.log(` - Logging message from ${data?.name}.`);
635
+ await new Promise((resolve) => setTimeout(resolve, 500));
636
+ console.log("✨ Submission logged!");
637
+ }
638
+
565
639
  // Run the example
566
640
  if (require.main === module) {
567
641
  example().catch(console.error);